summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ios_builds.yml2
-rw-r--r--.github/workflows/macos_builds.yml2
-rw-r--r--CONTRIBUTING.md8
-rw-r--r--SConstruct29
-rw-r--r--core/SCsub1
-rw-r--r--core/config/engine.cpp5
-rw-r--r--core/core_constants.cpp3
-rw-r--r--core/debugger/remote_debugger_peer.cpp2
-rw-r--r--core/error/error_macros.cpp4
-rw-r--r--core/error/error_macros.h3
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/io/file_access.cpp2
-rw-r--r--core/io/file_access_network.cpp1
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/http_client_tcp.cpp2
-rw-r--r--core/io/image.cpp132
-rw-r--r--core/io/image.h2
-rw-r--r--core/io/ip_address.cpp4
-rw-r--r--core/io/json.cpp14
-rw-r--r--core/io/pck_packer.cpp4
-rw-r--r--core/io/resource.h2
-rw-r--r--core/io/resource_format_binary.cpp4
-rw-r--r--core/io/resource_importer.cpp9
-rw-r--r--core/io/resource_importer.h5
-rw-r--r--core/io/resource_uid.cpp4
-rw-r--r--core/io/translation_loader_po.cpp2
-rw-r--r--core/math/a_star.cpp1
-rw-r--r--core/math/aabb.h8
-rw-r--r--core/math/audio_frame.h12
-rw-r--r--core/math/basis.h11
-rw-r--r--core/math/camera_matrix.cpp4
-rw-r--r--core/math/camera_matrix.h10
-rw-r--r--core/math/delaunay_2d.h1
-rw-r--r--core/math/dynamic_bvh.h6
-rw-r--r--core/math/expression.cpp68
-rw-r--r--core/math/face3.h6
-rw-r--r--core/math/geometry_2d.h4
-rw-r--r--core/math/plane.h5
-rw-r--r--core/math/quaternion.h3
-rw-r--r--core/math/rect2.cpp10
-rw-r--r--core/math/rect2.h218
-rw-r--r--core/math/rect2i.cpp42
-rw-r--r--core/math/rect2i.h245
-rw-r--r--core/math/transform_2d.cpp2
-rw-r--r--core/math/transform_2d.h7
-rw-r--r--core/math/transform_3d.h9
-rw-r--r--core/math/triangulate.h1
-rw-r--r--core/math/vector2.cpp92
-rw-r--r--core/math/vector2.h133
-rw-r--r--core/math/vector2i.cpp125
-rw-r--r--core/math/vector2i.h143
-rw-r--r--core/math/vector3.h6
-rw-r--r--core/math/vector3i.h10
-rw-r--r--core/multiplayer/multiplayer_api.cpp364
-rw-r--r--core/multiplayer/multiplayer_api.h116
-rw-r--r--core/multiplayer/multiplayer_replicator.cpp791
-rw-r--r--core/multiplayer/multiplayer_replicator.h138
-rw-r--r--core/object/callable_method_pointer.h8
-rw-r--r--core/object/class_db.cpp5
-rw-r--r--core/object/make_virtuals.py4
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/string/char_utils.h92
-rw-r--r--core/string/string_name.cpp15
-rw-r--r--core/string/string_name.h12
-rw-r--r--core/string/translation.cpp23
-rw-r--r--core/string/ustring.cpp46
-rw-r--r--core/string/ustring.h4
-rw-r--r--core/templates/rid_owner.h56
-rw-r--r--core/variant/callable.cpp9
-rw-r--r--core/variant/callable.h1
-rw-r--r--core/variant/callable_bind.cpp14
-rw-r--r--core/variant/callable_bind.h2
-rw-r--r--core/variant/method_ptrcall.h1
-rw-r--r--core/variant/native_ptr.h46
-rw-r--r--core/variant/variant.h4
-rw-r--r--core/variant/variant_call.cpp4
-rw-r--r--core/variant/variant_op.cpp137
-rw-r--r--core/variant/variant_parser.cpp18
-rw-r--r--core/version.h3
-rw-r--r--doc/classes/@GlobalScope.xml6
-rw-r--r--doc/classes/ArrayOccluder3D.xml24
-rw-r--r--doc/classes/AudioStreamRandomPitch.xml19
-rw-r--r--doc/classes/AudioStreamRandomizer.xml90
-rw-r--r--doc/classes/BoxOccluder3D.xml13
-rw-r--r--doc/classes/Button.xml2
-rw-r--r--doc/classes/CanvasItem.xml4
-rw-r--r--doc/classes/CanvasLayer.xml11
-rw-r--r--doc/classes/Color.xml33
-rw-r--r--doc/classes/DisplayServer.xml13
-rw-r--r--doc/classes/EditorPlugin.xml6
-rw-r--r--doc/classes/Image.xml9
-rw-r--r--doc/classes/InputEventMouse.xml6
-rw-r--r--doc/classes/JavaScript.xml22
-rw-r--r--doc/classes/Mesh.xml2
-rw-r--r--doc/classes/MultiplayerAPI.xml6
-rw-r--r--doc/classes/MultiplayerReplicator.xml191
-rw-r--r--doc/classes/MultiplayerSpawner.xml47
-rw-r--r--doc/classes/MultiplayerSynchronizer.xml17
-rw-r--r--doc/classes/Node.xml30
-rw-r--r--doc/classes/Occluder3D.xml18
-rw-r--r--doc/classes/OccluderInstance3D.xml2
-rw-r--r--doc/classes/Panel.xml10
-rw-r--r--doc/classes/PolygonOccluder3D.xml13
-rw-r--r--doc/classes/PopupMenu.xml7
-rw-r--r--doc/classes/ProjectSettings.xml2
-rw-r--r--doc/classes/QuadOccluder3D.xml13
-rw-r--r--doc/classes/RID.xml6
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--doc/classes/SceneReplicationConfig.xml61
-rw-r--r--doc/classes/SphereOccluder3D.xml13
-rw-r--r--doc/classes/String.xml23
-rw-r--r--doc/classes/StringName.xml24
-rw-r--r--doc/classes/TextServerExtension.xml17
-rw-r--r--doc/classes/TextureButton.xml10
-rw-r--r--doc/classes/Theme.xml200
-rw-r--r--doc/classes/TranslationServer.xml7
-rw-r--r--doc/classes/Tree.xml1
-rw-r--r--doc/classes/Vector2i.xml10
-rw-r--r--doc/classes/Vector3i.xml11
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--doc/classes/VisualShaderNode.xml2
-rw-r--r--doc/classes/VisualShaderNodeParticleRandomness.xml10
-rw-r--r--doc/classes/Window.xml7
-rw-r--r--doc/classes/bool.xml2
-rw-r--r--doc/classes/float.xml12
-rw-r--r--doc/classes/int.xml7
-rw-r--r--drivers/vulkan/vulkan_context.cpp6
-rw-r--r--editor/animation_track_editor.cpp14
-rw-r--r--editor/array_property_edit.cpp4
-rw-r--r--editor/code_editor.cpp4
-rw-r--r--editor/connections_dialog.cpp8
-rw-r--r--editor/create_dialog.cpp18
-rw-r--r--editor/create_dialog.h2
-rw-r--r--editor/debugger/editor_debugger_inspector.cpp2
-rw-r--r--editor/debugger/editor_debugger_node.cpp4
-rw-r--r--editor/debugger/script_editor_debugger.cpp24
-rw-r--r--editor/dependency_editor.cpp2
-rw-r--r--editor/editor_about.cpp3
-rw-r--r--editor/editor_atlas_packer.cpp3
-rw-r--r--editor/editor_atlas_packer.h5
-rw-r--r--editor/editor_autoload_settings.cpp16
-rw-r--r--editor/editor_data.cpp30
-rw-r--r--editor/editor_data.h2
-rw-r--r--editor/editor_export.cpp12
-rw-r--r--editor/editor_feature_profile.cpp12
-rw-r--r--editor/editor_help.cpp46
-rw-r--r--editor/editor_help_search.cpp9
-rw-r--r--editor/editor_inspector.cpp52
-rw-r--r--editor/editor_locale_dialog.cpp14
-rw-r--r--editor/editor_node.cpp135
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_plugin.cpp19
-rw-r--r--editor/editor_plugin.h6
-rw-r--r--editor/editor_properties.cpp6
-rw-r--r--editor/editor_properties_array_dict.cpp49
-rw-r--r--editor/editor_properties_array_dict.h1
-rw-r--r--editor/editor_resource_picker.cpp18
-rw-r--r--editor/editor_sectioned_inspector.cpp4
-rw-r--r--editor/editor_settings_dialog.cpp49
-rw-r--r--editor/editor_settings_dialog.h1
-rw-r--r--editor/editor_themes.cpp252
-rw-r--r--editor/editor_toaster.cpp30
-rw-r--r--editor/filesystem_dock.cpp95
-rw-r--r--editor/find_in_files.cpp9
-rw-r--r--editor/import/collada.cpp8
-rw-r--r--editor/import/dynamicfont_import_settings.cpp68
-rw-r--r--editor/import/resource_importer_layered_texture.cpp176
-rw-r--r--editor/import/resource_importer_layered_texture.h23
-rw-r--r--editor/import/resource_importer_scene.cpp166
-rw-r--r--editor/import/resource_importer_scene.h22
-rw-r--r--editor/import/resource_importer_texture.cpp34
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp2
-rw-r--r--editor/plugins/audio_stream_randomizer_editor_plugin.cpp119
-rw-r--r--editor/plugins/audio_stream_randomizer_editor_plugin.h57
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp67
-rw-r--r--editor/plugins/editor_preview_plugins.cpp20
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp215
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h5
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp990
-rw-r--r--editor/plugins/node_3d_editor_plugin.h14
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.cpp8
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp9
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.h4
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp (renamed from editor/plugins/collision_polygon_3d_editor_plugin.cpp)114
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.h (renamed from editor/plugins/collision_polygon_3d_editor_plugin.h)29
-rw-r--r--editor/plugins/replication_editor_plugin.cpp390
-rw-r--r--editor/plugins/replication_editor_plugin.h108
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp10
-rw-r--r--editor/plugins/script_text_editor.cpp25
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp2
-rw-r--r--editor/plugins/text_control_editor_plugin.cpp449
-rw-r--r--editor/plugins/text_control_editor_plugin.h5
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp4
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp326
-rw-r--r--editor/project_export.cpp2
-rw-r--r--editor/project_export.h2
-rw-r--r--editor/project_manager.cpp5
-rw-r--r--editor/project_settings_editor.cpp24
-rw-r--r--editor/property_editor.cpp8
-rw-r--r--editor/property_selector.cpp13
-rw-r--r--editor/scene_tree_dock.cpp14
-rw-r--r--editor/scene_tree_editor.cpp16
-rw-r--r--editor/script_create_dialog.cpp14
-rw-r--r--editor/shader_create_dialog.cpp2
-rw-r--r--main/main.cpp32
-rw-r--r--methods.py8
-rw-r--r--misc/dist/html/editor.html36
-rw-r--r--misc/dist/html/service-worker.js112
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj4
-rw-r--r--modules/SCsub45
-rw-r--r--modules/bullet/shape_bullet.h10
-rw-r--r--modules/camera/camera_osx.h1
-rw-r--r--modules/camera/camera_osx.mm27
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCombiner3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGCylinder3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGPrimitive3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGSphere3D.xml2
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml2
-rw-r--r--modules/cvtt/SCsub11
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp34
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp2
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp2
-rw-r--r--modules/gdnative/gdnative/rect2.cpp2
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp1
-rw-r--r--modules/gdnative/gdnative/vector2.cpp2
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp1
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp8
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp22
-rw-r--r--modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd4
-rw-r--r--modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd4
-rw-r--r--modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd3
-rw-r--r--modules/gdscript/gdscript.cpp18
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp147
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp16
-rw-r--r--modules/gdscript/gdscript_editor.cpp30
-rw-r--r--modules/gdscript/gdscript_parser.cpp10
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp86
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h61
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp50
-rw-r--r--modules/gdscript/gdscript_warning.cpp4
-rw-r--r--modules/gdscript/gdscript_warning.h1
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp4
-rw-r--r--modules/gdscript/language_server/lsp.hpp2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp60
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd21
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out7
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd15
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out21
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd1
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out2
-rw-r--r--modules/gltf/gltf_document.cpp42
-rw-r--r--modules/gridmap/grid_map.cpp1
-rw-r--r--modules/meshoptimizer/register_types.cpp6
-rw-r--r--modules/modules_builders.py7
-rw-r--r--modules/mono/csharp_script.cpp4
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs1
-rw-r--r--modules/mono/editor/bindings_generator.cpp4
-rw-r--r--modules/mono/editor_templates/CharacterBody2D/basic_movement.cs4
-rw-r--r--modules/mono/editor_templates/CharacterBody3D/basic_movement.cs4
-rw-r--r--modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs47
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs25
-rw-r--r--modules/ogg/ogg_packet_sequence.h2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp5
-rw-r--r--modules/text_server_adv/text_server_adv.cpp16
-rw-r--r--modules/text_server_fb/text_server_fb.cpp24
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp3
-rw-r--r--modules/visual_script/editor/visual_script_editor.h18
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp6
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h8
-rw-r--r--modules/visual_script/visual_script.cpp3
-rw-r--r--modules/visual_script/visual_script_expression.cpp16
-rw-r--r--modules/visual_script/visual_script_nodes.cpp10
-rw-r--r--modules/websocket/wsl_client.cpp72
-rw-r--r--modules/websocket/wsl_client.h5
-rw-r--r--platform/android/display_server_android.cpp10
-rw-r--r--platform/android/display_server_android.h1
-rw-r--r--platform/android/export/export_plugin.cpp10
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java8
-rw-r--r--platform/android/java_godot_io_wrapper.cpp14
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/iphone/detect.py1
-rw-r--r--platform/iphone/display_server_iphone.h1
-rw-r--r--platform/iphone/display_server_iphone.mm4
-rw-r--r--platform/iphone/export/export_plugin.cpp1
-rw-r--r--platform/iphone/export/export_plugin.h2
-rw-r--r--platform/javascript/api/api.cpp9
-rw-r--r--platform/javascript/api/javascript_singleton.h2
-rw-r--r--platform/javascript/display_server_javascript.cpp7
-rw-r--r--platform/javascript/display_server_javascript.h1
-rw-r--r--platform/javascript/export/export_plugin.cpp54
-rw-r--r--platform/javascript/export/export_plugin.h2
-rw-r--r--platform/javascript/godot_js.h2
-rw-r--r--platform/javascript/javascript_singleton.cpp8
-rw-r--r--platform/javascript/js/engine/config.js51
-rw-r--r--platform/javascript/js/engine/engine.js3
-rw-r--r--platform/javascript/js/libs/library_godot_os.js55
-rw-r--r--platform/javascript/os_javascript.cpp16
-rw-r--r--platform/javascript/os_javascript.h5
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp7
-rw-r--r--platform/linuxbsd/display_server_x11.cpp68
-rw-r--r--platform/linuxbsd/display_server_x11.h1
-rw-r--r--platform/linuxbsd/joypad_linux.cpp5
-rw-r--r--platform/osx/SCsub11
-rw-r--r--platform/osx/crash_handler_osx.mm7
-rw-r--r--platform/osx/detect.py10
-rw-r--r--platform/osx/display_server_osx.h144
-rw-r--r--platform/osx/display_server_osx.mm2792
-rw-r--r--platform/osx/export/export_plugin.h2
-rw-r--r--platform/osx/gl_manager_osx_legacy.h (renamed from platform/osx/gl_manager_osx.h)39
-rw-r--r--platform/osx/gl_manager_osx_legacy.mm (renamed from platform/osx/gl_manager_osx.mm)121
-rw-r--r--platform/osx/godot_application.h42
-rw-r--r--platform/osx/godot_application.mm53
-rw-r--r--platform/osx/godot_application_delegate.h45
-rw-r--r--platform/osx/godot_application_delegate.mm132
-rw-r--r--platform/osx/godot_content_view.h65
-rw-r--r--platform/osx/godot_content_view.mm760
-rw-r--r--platform/osx/godot_main_osx.mm23
-rw-r--r--platform/osx/godot_menu_item.h52
-rw-r--r--platform/osx/godot_window.h47
-rw-r--r--platform/osx/godot_window.mm69
-rw-r--r--platform/osx/godot_window_delegate.h47
-rw-r--r--platform/osx/godot_window_delegate.mm254
-rw-r--r--platform/osx/joypad_osx.cpp12
-rw-r--r--platform/osx/joypad_osx.h2
-rw-r--r--platform/osx/key_mapping_osx.h52
-rw-r--r--platform/osx/key_mapping_osx.mm477
-rw-r--r--platform/osx/os_osx.h27
-rw-r--r--platform/osx/os_osx.mm438
-rw-r--r--platform/osx/osx_terminal_logger.h44
-rw-r--r--platform/osx/osx_terminal_logger.mm81
-rw-r--r--platform/osx/vulkan_context_osx.mm2
-rw-r--r--platform/windows/crash_handler_windows.cpp7
-rw-r--r--platform/windows/display_server_windows.cpp215
-rw-r--r--platform/windows/display_server_windows.h5
-rw-r--r--scene/2d/navigation_region_2d.cpp8
-rw-r--r--scene/2d/navigation_region_2d.h4
-rw-r--r--scene/2d/ray_cast_2d.cpp7
-rw-r--r--scene/3d/occluder_instance_3d.cpp548
-rw-r--r--scene/3d/occluder_instance_3d.h128
-rw-r--r--scene/3d/ray_cast_3d.cpp7
-rw-r--r--scene/3d/skeleton_3d.cpp2
-rw-r--r--scene/3d/xr_nodes.cpp8
-rw-r--r--scene/SCsub1
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/animation_node_state_machine.cpp6
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/animation/animation_tree.cpp4
-rw-r--r--scene/gui/code_edit.cpp28
-rw-r--r--scene/gui/menu_button.cpp3
-rw-r--r--scene/gui/option_button.cpp49
-rw-r--r--scene/gui/panel.cpp23
-rw-r--r--scene/gui/panel.h15
-rw-r--r--scene/gui/popup_menu.cpp130
-rw-r--r--scene/gui/popup_menu.h3
-rw-r--r--scene/gui/tab_bar.cpp3
-rw-r--r--scene/gui/tab_container.cpp2
-rw-r--r--scene/gui/text_edit.cpp30
-rw-r--r--scene/gui/texture_button.cpp100
-rw-r--r--scene/gui/texture_button.h8
-rw-r--r--scene/gui/tree.cpp36
-rw-r--r--scene/gui/tree.h2
-rw-r--r--scene/main/canvas_item.cpp74
-rw-r--r--scene/main/canvas_item.h5
-rw-r--r--scene/main/canvas_layer.cpp33
-rw-r--r--scene/main/canvas_layer.h4
-rw-r--r--scene/main/node.cpp26
-rw-r--r--scene/main/node.h3
-rw-r--r--scene/main/scene_tree.cpp3
-rw-r--r--scene/main/scene_tree.h2
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/main/window.cpp1
-rw-r--r--scene/main/window.h1
-rw-r--r--scene/multiplayer/SCsub5
-rw-r--r--scene/multiplayer/multiplayer_spawner.cpp227
-rw-r--r--scene/multiplayer/multiplayer_spawner.h101
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.cpp158
-rw-r--r--scene/multiplayer/multiplayer_synchronizer.h72
-rw-r--r--scene/multiplayer/scene_cache_interface.cpp249
-rw-r--r--scene/multiplayer/scene_cache_interface.h82
-rw-r--r--scene/multiplayer/scene_replication_interface.cpp411
-rw-r--r--scene/multiplayer/scene_replication_interface.h84
-rw-r--r--scene/multiplayer/scene_replication_state.cpp258
-rw-r--r--scene/multiplayer/scene_replication_state.h121
-rw-r--r--scene/multiplayer/scene_rpc_interface.cpp (renamed from core/multiplayer/rpc_manager.cpp)123
-rw-r--r--scene/multiplayer/scene_rpc_interface.h (renamed from core/multiplayer/rpc_manager.h)26
-rw-r--r--scene/property_utils.cpp2
-rw-r--r--scene/register_scene_types.cpp19
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/resource_format_text.cpp14
-rw-r--r--scene/resources/scene_replication_config.cpp187
-rw-r--r--scene/resources/scene_replication_config.h90
-rw-r--r--scene/resources/surface_tool.cpp79
-rw-r--r--scene/resources/surface_tool.h12
-rw-r--r--scene/resources/syntax_highlighter.cpp14
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--scene/resources/theme.cpp4
-rw-r--r--scene/resources/tile_set.cpp4
-rw-r--r--scene/resources/visual_shader.cpp294
-rw-r--r--scene/resources/visual_shader.h6
-rw-r--r--scene/resources/visual_shader_nodes.cpp118
-rw-r--r--scene/resources/visual_shader_nodes.h2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp205
-rw-r--r--scene/resources/visual_shader_particle_nodes.h14
-rw-r--r--scene/resources/visual_shader_sdf_nodes.cpp32
-rw-r--r--servers/audio/audio_stream.cpp370
-rw-r--r--servers/audio/audio_stream.h77
-rw-r--r--servers/display_server.cpp2
-rw-r--r--servers/display_server.h6
-rw-r--r--servers/display_server_headless.h1
-rw-r--r--servers/register_server_types.cpp2
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp21
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp43
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.h7
-rw-r--r--servers/rendering/renderer_rd/shaders/copy.glsl91
-rw-r--r--servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl3
-rw-r--r--servers/rendering/renderer_scene_cull.cpp8
-rw-r--r--servers/rendering/rendering_device_binds.cpp4
-rw-r--r--servers/rendering/shader_compiler.cpp2
-rw-r--r--servers/rendering/shader_language.cpp85
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/rendering_server.h2
-rw-r--r--servers/text/text_server_extension.cpp26
-rw-r--r--servers/text/text_server_extension.h14
-rw-r--r--servers/text_server.h1
-rw-r--r--servers/xr/xr_positional_tracker.cpp10
-rw-r--r--tests/core/math/test_expression.h2
-rw-r--r--tests/core/math/test_math.cpp6
-rw-r--r--tests/core/math/test_rect2.h278
-rw-r--r--tests/core/math/test_rect2i.h311
-rw-r--r--tests/core/math/test_vector2.h1
-rw-r--r--tests/core/math/test_vector2i.h1
-rw-r--r--tests/core/string/test_string.h13
-rw-r--r--tests/scene/test_animation.h314
-rw-r--r--tests/test_main.cpp2
-rw-r--r--thirdparty/README.md6
-rw-r--r--thirdparty/cvtt/ConvectionKernels.cpp7586
-rw-r--r--thirdparty/cvtt/ConvectionKernels.h169
-rw-r--r--thirdparty/cvtt/ConvectionKernels_API.cpp346
-rw-r--r--thirdparty/cvtt/ConvectionKernels_AggregatedError.h55
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC67.cpp3485
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC67.h99
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC6H_IO.cpp881
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC6H_IO.h16
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC7_Prio.h17
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC7_PrioData.cpp1301
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BC7_SingleColor.h2
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BCCommon.cpp46
-rw-r--r--thirdparty/cvtt/ConvectionKernels_BCCommon.h104
-rw-r--r--thirdparty/cvtt/ConvectionKernels_Config.h12
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ETC.cpp3147
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ETC.h126
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ETC1.h29
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ETC2.h35
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ETC2_Rounding.h27
-rw-r--r--thirdparty/cvtt/ConvectionKernels_EndpointRefiner.h181
-rw-r--r--thirdparty/cvtt/ConvectionKernels_EndpointSelector.h153
-rw-r--r--thirdparty/cvtt/ConvectionKernels_FakeBT709_Rounding.h282
-rw-r--r--thirdparty/cvtt/ConvectionKernels_IndexSelector.cpp66
-rw-r--r--thirdparty/cvtt/ConvectionKernels_IndexSelector.h147
-rw-r--r--thirdparty/cvtt/ConvectionKernels_IndexSelectorHDR.h155
-rw-r--r--thirdparty/cvtt/ConvectionKernels_PackedCovarianceMatrix.h68
-rw-r--r--thirdparty/cvtt/ConvectionKernels_ParallelMath.h1816
-rw-r--r--thirdparty/cvtt/ConvectionKernels_S3TC.cpp1054
-rw-r--r--thirdparty/cvtt/ConvectionKernels_S3TC.h51
-rw-r--r--thirdparty/cvtt/ConvectionKernels_S3TC_SingleColor.h304
-rw-r--r--thirdparty/cvtt/ConvectionKernels_SingleFile.cpp48
-rw-r--r--thirdparty/cvtt/ConvectionKernels_UnfinishedEndpoints.h121
-rw-r--r--thirdparty/cvtt/ConvectionKernels_Util.cpp88
-rw-r--r--thirdparty/cvtt/ConvectionKernels_Util.h21
-rw-r--r--thirdparty/cvtt/etc_notes.txt27
-rw-r--r--thirdparty/misc/patches/polypartition-godot-types.patch85
-rw-r--r--thirdparty/misc/polypartition.cpp2
511 files changed, 29134 insertions, 15918 deletions
diff --git a/.github/workflows/ios_builds.yml b/.github/workflows/ios_builds.yml
index 721d574dbe..cd9c7ec117 100644
--- a/.github/workflows/ios_builds.yml
+++ b/.github/workflows/ios_builds.yml
@@ -25,7 +25,7 @@ jobs:
- name: Setup python and scons
uses: ./.github/actions/godot-deps
- - name: Compilation (armv7)
+ - name: Compilation (arm64v8)
uses: ./.github/actions/godot-build
with:
sconsflags: ${{ env.SCONSFLAGS }}
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
index aede3f8d49..68623f2770 100644
--- a/.github/workflows/macos_builds.yml
+++ b/.github/workflows/macos_builds.yml
@@ -4,7 +4,7 @@ on: [push, pull_request]
# Global Settings
env:
# Only used for the cache key. Increment version to force clean build.
- GODOT_BASE_BRANCH: master-v2
+ GODOT_BASE_BRANCH: master-v3
SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes
concurrency:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d7a4f976bf..49355d2782 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -66,8 +66,10 @@ understand that:
To speed up our work, **please upload a minimal project** that isolates
and reproduces the issue. This is always the **best way for us to fix it**.
-You can attach a ZIP file with the minimal project directly to the bug report,
-by drag and dropping the file in the GitHub edition field.
+We recommend attaching a ZIP file with the minimal project directly to the bug report,
+by drag and dropping the file in the GitHub edition field. This ensures the file
+can remain available for a long period of time. Only use third-party file hosts
+if your ZIP file isn't accepted by GitHub because it's too large.
We recommend always attaching a minimal reproduction project, even if the issue
may seem simple to reproduce manually.
@@ -84,7 +86,7 @@ it'll be considered too difficult to diagnose.
Now that you've read the guidelines, click the link below to create a
bug report:
-- **[Report a bug](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.md&title=)**
+- **[Report a bug](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml)**
## Proposing features or improvements
diff --git a/SConstruct b/SConstruct
index b8063589c6..a27e5490a5 100644
--- a/SConstruct
+++ b/SConstruct
@@ -179,9 +179,11 @@ opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loade
# Advanced options
opts.Add(BoolVariable("dev", "If yes, alias for verbose=yes warnings=extra werror=yes", False))
-opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
opts.Add(BoolVariable("tests", "Build the unit tests", False))
+opts.Add(BoolVariable("fast_unsafe", "Enable unsafe options for faster rebuilds", False))
+opts.Add(BoolVariable("compiledb", "Generate compilation DB (`compile_commands.json`) for external tools", False))
opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
+opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no")))
opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", False))
opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all generated binary files", "")
@@ -360,6 +362,17 @@ if env_base["target"] == "debug":
# working on the engine itself.
env_base.Append(CPPDEFINES=["DEV_ENABLED"])
+# SCons speed optimization controlled by the `fast_unsafe` option, which provide
+# more than 10 s speed up for incremental rebuilds.
+# Unsafe as they reduce the certainty of rebuilding all changed files, so it's
+# enabled by default for `debug` builds, and can be overridden from command line.
+# Ref: https://github.com/SCons/scons/wiki/GoFastButton
+if methods.get_cmdline_bool("fast_unsafe", env_base["target"] == "debug"):
+ # Renamed to `content-timestamp` in SCons >= 4.2, keeping MD5 for compat.
+ env_base.Decider("MD5-timestamp")
+ env_base.SetOption("implicit_cache", 1)
+ env_base.SetOption("max_drift", 60)
+
if env_base["use_precise_math_checks"]:
env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
@@ -385,14 +398,15 @@ if selected_platform in platform_list:
else:
env = env_base.Clone()
- # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
- from SCons import __version__ as scons_raw_version
+ if env["compiledb"]:
+ # Generating the compilation DB (`compile_commands.json`) requires SCons 4.0.0 or later.
+ from SCons import __version__ as scons_raw_version
- scons_ver = env._get_major_minor_revision(scons_raw_version)
+ scons_ver = env._get_major_minor_revision(scons_raw_version)
- if scons_ver >= (4, 0, 0):
- env.Tool("compilation_db")
- env.Alias("compiledb", env.CompilationDatabase())
+ if scons_ver >= (4, 0, 0):
+ env.Tool("compilation_db")
+ env.Alias("compiledb", env.CompilationDatabase())
# 'dev' and 'production' are aliases to set default options if they haven't been set
# manually by the user.
@@ -817,6 +831,7 @@ elif selected_platform != "":
# The following only makes sense when the 'env' is defined, and assumes it is.
if "env" in locals():
+ # FIXME: This method mixes both cosmetic progress stuff and cache handling...
methods.show_progress(env)
# TODO: replace this with `env.Dump(format="json")`
# once we start requiring SCons 4.0 as min version.
diff --git a/core/SCsub b/core/SCsub
index c12dd4e60e..1379e9df9b 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -147,6 +147,7 @@ env.core_sources += thirdparty_obj
env.add_source_files(env.core_sources, "*.cpp")
env.add_source_files(env.core_sources, "script_encryption_key.gen.cpp")
+env.add_source_files(env.core_sources, "version_hash.gen.cpp")
# Certificates
env.Depends(
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index d9abf5e5e9..ff8a8d283f 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -35,7 +35,6 @@
#include "core/donors.gen.h"
#include "core/license.gen.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
void Engine::set_physics_ticks_per_second(int p_ips) {
ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0.");
@@ -95,8 +94,8 @@ Dictionary Engine::get_version_info() const {
dict["build"] = VERSION_BUILD;
dict["year"] = VERSION_YEAR;
- String hash = VERSION_HASH;
- dict["hash"] = hash.length() == 0 ? String("unknown") : hash;
+ String hash = String(VERSION_HASH);
+ dict["hash"] = hash.is_empty() ? String("unknown") : hash;
String stringver = String(dict["major"]) + "." + String(dict["minor"]);
if ((int)dict["patch"] != 0) {
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 2f5fd05e6a..63e7323f7a 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -165,6 +165,9 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER);
BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_IMAGE_MASK);
+ BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TEXT_MASK);
+
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPECIAL);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ESCAPE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TAB);
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp
index 8c6e95e276..7c7d38ab0a 100644
--- a/core/debugger/remote_debugger_peer.cpp
+++ b/core/debugger/remote_debugger_peer.cpp
@@ -226,7 +226,7 @@ RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) {
String debug_host = p_uri.replace("tcp://", "");
uint16_t debug_port = 6007;
- if (debug_host.find(":") != -1) {
+ if (debug_host.contains(":")) {
int sep_pos = debug_host.rfind(":");
debug_port = debug_host.substr(sep_pos + 1).to_int();
debug_host = debug_host.substr(0, sep_pos);
diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp
index 928ddd3397..ceccd43259 100644
--- a/core/error/error_macros.cpp
+++ b/core/error/error_macros.cpp
@@ -118,3 +118,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify, bool p_fatal) {
_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), p_fatal);
}
+
+void _err_flush_stdout() {
+ fflush(stdout);
+}
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index 802d7f9ef4..7b032fb4cd 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -69,6 +69,7 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co
void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR);
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false);
void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false);
+void _err_flush_stdout();
#ifdef __GNUC__
//#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
@@ -789,6 +790,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
#define CRASH_NOW() \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -801,6 +803,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li
#define CRASH_NOW_MSG(m_msg) \
if (true) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index db0758d7fc..86d8dea3d9 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -159,7 +159,7 @@ Error DirAccess::make_dir_recursive(String p_dir) {
base = full_dir.substr(0, pos + 1);
} else if (full_dir.begins_with("/")) {
base = "/";
- } else if (full_dir.find(":/") != -1) {
+ } else if (full_dir.contains(":/")) {
base = full_dir.substr(0, full_dir.find(":/") + 2);
} else {
ERR_FAIL_V(ERR_INVALID_PARAMETER);
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index bb92648484..86836454c5 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -538,7 +538,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
for (int i = 0; i < size; ++i) {
String value = p_values[i];
- if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) {
+ if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) {
value = "\"" + value.replace("\"", "\"\"") + "\"";
}
if (i < size - 1) {
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 307004b1c2..cb38ac0928 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -487,7 +487,6 @@ FileAccessNetwork::~FileAccessNetwork() {
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
- id = nc->last_id++;
nc->accesses.erase(id);
nc->unlock_mutex();
}
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 3882627103..7dbea96c3d 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -70,7 +70,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
String p = p_path.replace_first("res://", "");
PackedDir *cd = root;
- if (p.find("/") != -1) { //in a subdir
+ if (p.contains("/")) { //in a subdir
Vector<String> ds = p.get_base_dir().split("/");
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index e61833ce7c..f920799677 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -614,7 +614,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
for (int i = 0; i < chunk.size() - 2; i++) {
char c = chunk[i];
int v = 0;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a' + 10;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 9df2b6835c..577fc59807 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -30,14 +30,17 @@
#include "image.h"
+#include "core/error/error_list.h"
#include "core/error/error_macros.h"
#include "core/io/image_loader.h"
#include "core/io/resource_loader.h"
#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/templates/hash_map.h"
+#include "core/variant/dictionary.h"
#include <stdio.h>
+#include <cmath>
const char *Image::format_names[Image::FORMAT_MAX] = {
"Lum8", //luminance
@@ -2056,7 +2059,7 @@ void Image::create(const char **p_xpm) {
for (int i = 0; i < 6; i++) {
char v = line_ptr[i];
- if (v >= '0' && v <= '9') {
+ if (is_digit(v)) {
v -= '0';
} else if (v >= 'A' && v <= 'F') {
v = (v - 'A') + 10;
@@ -3135,6 +3138,8 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb);
ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0));
+ ClassDB::bind_method(D_METHOD("compute_image_metrics", "compared_image", "use_luma"), &Image::compute_image_metrics);
+
ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect);
ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask);
ClassDB::bind_method(D_METHOD("blend_rect", "src", "src_rect", "dst"), &Image::blend_rect);
@@ -3620,3 +3625,128 @@ Ref<Resource> Image::duplicate(bool p_subresources) const {
void Image::set_as_black() {
memset(data.ptrw(), 0, data.size());
}
+
+Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric) {
+ // https://github.com/richgel999/bc7enc_rdo/blob/master/LICENSE
+ //
+ // This is free and unencumbered software released into the public domain.
+ // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+ // software, either in source code form or as a compiled binary, for any purpose,
+ // commercial or non - commercial, and by any means.
+ // In jurisdictions that recognize copyright laws, the author or authors of this
+ // software dedicate any and all copyright interest in the software to the public
+ // domain. We make this dedication for the benefit of the public at large and to
+ // the detriment of our heirs and successors. We intend this dedication to be an
+ // overt act of relinquishment in perpetuity of all present and future rights to
+ // this software under copyright law.
+ // 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 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.
+
+ Dictionary result;
+ result["max"] = INFINITY;
+ result["mean"] = INFINITY;
+ result["mean_squared"] = INFINITY;
+ result["root_mean_squared"] = INFINITY;
+ result["peak_snr"] = 0.0f;
+
+ ERR_FAIL_NULL_V(p_compared_image, result);
+ Error err = OK;
+ Ref<Image> compared_image = duplicate(true);
+ if (compared_image->is_compressed()) {
+ err = compared_image->decompress();
+ }
+ ERR_FAIL_COND_V(err != OK, result);
+ Ref<Image> source_image = p_compared_image->duplicate(true);
+ if (source_image->is_compressed()) {
+ err = source_image->decompress();
+ }
+ ERR_FAIL_COND_V(err != OK, result);
+
+ ERR_FAIL_COND_V(err != OK, result);
+
+ ERR_FAIL_COND_V_MSG((compared_image->get_format() >= Image::FORMAT_RH) && (compared_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported.");
+ ERR_FAIL_COND_V_MSG((source_image->get_format() >= Image::FORMAT_RH) && (source_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported.");
+
+ double image_metric_max, image_metric_mean, image_metric_mean_squared, image_metric_root_mean_squared, image_metric_peak_snr = 0.0;
+ const bool average_component_error = true;
+
+ const uint32_t width = MIN(compared_image->get_width(), source_image->get_width());
+ const uint32_t height = MIN(compared_image->get_height(), source_image->get_height());
+
+ // Histogram approach originally due to Charles Bloom.
+ double hist[256];
+ memset(hist, 0, sizeof(hist));
+
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ const Color color_a = compared_image->get_pixel(x, y);
+
+ const Color color_b = source_image->get_pixel(x, y);
+
+ if (!p_luma_metric) {
+ ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ hist[Math::abs(color_a.get_r8() - color_b.get_r8())]++;
+ ERR_FAIL_COND_V_MSG(color_a.g > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ ERR_FAIL_COND_V_MSG(color_b.g > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ hist[Math::abs(color_a.get_g8() - color_b.get_g8())]++;
+ ERR_FAIL_COND_V_MSG(color_a.b > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ ERR_FAIL_COND_V_MSG(color_b.b > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ hist[Math::abs(color_a.get_b8() - color_b.get_b8())]++;
+ ERR_FAIL_COND_V_MSG(color_a.a > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ ERR_FAIL_COND_V_MSG(color_b.a > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ hist[Math::abs(color_a.get_a8() - color_b.get_a8())]++;
+ } else {
+ ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors.");
+ // REC709 weightings
+ int luma_a = (13938U * color_a.get_r8() + 46869U * color_a.get_g8() + 4729U * color_a.get_b8() + 32768U) >> 16U;
+ int luma_b = (13938U * color_b.get_r8() + 46869U * color_b.get_g8() + 4729U * color_b.get_b8() + 32768U) >> 16U;
+ hist[Math::abs(luma_a - luma_b)]++;
+ }
+ }
+ }
+
+ image_metric_max = 0;
+ double sum = 0.0f, sum2 = 0.0f;
+ for (uint32_t i = 0; i < 256; i++) {
+ if (!hist[i]) {
+ continue;
+ }
+
+ image_metric_max = MAX(image_metric_max, i);
+
+ double x = i * hist[i];
+
+ sum += x;
+ sum2 += i * x;
+ }
+
+ // See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html
+ double total_values = width * height;
+
+ if (average_component_error) {
+ total_values *= 4;
+ }
+
+ image_metric_mean = CLAMP(sum / total_values, 0.0f, 255.0f);
+ image_metric_mean_squared = CLAMP(sum2 / total_values, 0.0f, 255.0f * 255.0f);
+
+ image_metric_root_mean_squared = sqrt(image_metric_mean_squared);
+
+ if (!image_metric_root_mean_squared) {
+ image_metric_peak_snr = 1e+10f;
+ } else {
+ image_metric_peak_snr = CLAMP(log10(255.0f / image_metric_root_mean_squared) * 20.0f, 0.0f, 500.0f);
+ }
+ result["max"] = image_metric_max;
+ result["mean"] = image_metric_mean;
+ result["mean_squared"] = image_metric_mean_squared;
+ result["root_mean_squared"] = image_metric_root_mean_squared;
+ result["peak_snr"] = image_metric_peak_snr;
+ return result;
+}
diff --git a/core/io/image.h b/core/io/image.h
index ddfb2bb01d..53bfa0881f 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -399,6 +399,8 @@ public:
mipmaps = p_image->mipmaps;
data = p_image->data;
}
+
+ Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true);
};
VARIANT_ENUM_CAST(Image::Format)
diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp
index 38f99a08a4..d183c60798 100644
--- a/core/io/ip_address.cpp
+++ b/core/io/ip_address.cpp
@@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) {
int n = 0;
char32_t c = p_string[i];
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
n = c - '0';
} else if (c >= 'a' && c <= 'f') {
n = 10 + (c - 'a');
@@ -113,7 +113,7 @@ void IPAddress::_parse_ipv6(const String &p_string) {
} else if (c == '.') {
part_ipv4 = true;
- } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ } else if (is_hex_digit(c)) {
if (!part_found) {
parts[parts_idx++] = i;
part_found = true;
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 7b642f6a59..4b745dff44 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -229,12 +229,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_err_str = "Unterminated String";
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -265,12 +265,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_err_str = "Unterminated String";
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -326,7 +326,7 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
break;
}
- if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) {
+ if (p_str[index] == '-' || is_digit(p_str[index])) {
//a number
const char32_t *rptr;
double number = String::to_float(&p_str[index], &rptr);
@@ -335,10 +335,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
r_token.value = number;
return OK;
- } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) {
+ } else if (is_ascii_char(p_str[index])) {
String id;
- while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) {
+ while (is_ascii_char(p_str[index])) {
id += p_str[index];
index++;
}
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 221a680130..272ace3438 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -62,7 +62,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
int v = 0;
if (i * 2 < _key.length()) {
char32_t ct = _key[i * 2];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
@@ -72,7 +72,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
if (i * 2 + 1 < _key.length()) {
char32_t ct = _key[i * 2 + 1];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
diff --git a/core/io/resource.h b/core/io/resource.h
index dea2160616..b1e1c15541 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -105,7 +105,7 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
- _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.find("::") != -1 || path_cache.begins_with("local://"); }
+ _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); }
static String generate_scene_unique_id();
void set_scene_unique_id(const String &p_id);
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 200d5fafde..8588bab0be 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -335,7 +335,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
String exttype = get_unicode_string();
String path = get_unicode_string();
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));
}
@@ -626,7 +626,7 @@ Error ResourceLoaderBinary::load() {
path = remaps[path];
}
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path));
}
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index 470fb2d42d..9b6440e2a2 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -463,3 +463,12 @@ void ResourceImporter::_bind_methods() {
BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT);
BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE);
}
+
+void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) {
+ ERR_FAIL_COND(p_importer.is_null());
+ if (p_first_priority) {
+ importers.insert(0, p_importer);
+ } else {
+ importers.push_back(p_importer);
+ }
+}
diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h
index 261afbab69..2fffc16ad8 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -80,9 +80,8 @@ public:
String get_internal_resource_path(const String &p_path) const;
void get_internal_resource_path_list(const String &p_path, List<String> *r_paths);
- void add_importer(const Ref<ResourceImporter> &p_importer) {
- importers.push_back(p_importer);
- }
+ void add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority = false);
+
void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); }
Ref<ResourceImporter> get_importer_by_name(const String &p_name) const;
Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const;
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 1a16d5b47a..776756e64e 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -71,9 +71,9 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
for (uint32_t i = 6; i < l; i++) {
uid *= base;
uint32_t c = p_text[i];
- if (c >= 'a' && c <= 'z') {
+ if (is_ascii_lower_case(c)) {
uid += c - 'a';
- } else if (c >= '0' && c <= '9') {
+ } else if (is_digit(c)) {
uid += c - '0' + char_count;
} else {
return INVALID_ID;
diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp
index 2f6742bd7a..8d3e58cad1 100644
--- a/core/io/translation_loader_po.cpp
+++ b/core/io/translation_loader_po.cpp
@@ -179,7 +179,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
}
if (l.is_empty() || l.begins_with("#")) {
- if (l.find("fuzzy") != -1) {
+ if (l.contains("fuzzy")) {
skip_next = true;
}
line++;
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index ce2435216b..14057b96be 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -32,7 +32,6 @@
#include "core/math/geometry_3d.h"
#include "core/object/script_language.h"
-#include "scene/scene_string_names.h"
int AStar::get_available_point_id() const {
if (points.has(last_free_id)) {
diff --git a/core/math/aabb.h b/core/math/aabb.h
index 3d19410ddf..cb6f05e9ea 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -36,13 +36,13 @@
#include "core/math/vector3.h"
/**
- * AABB / AABB (Axis Aligned Bounding Box)
- * This is implemented by a point (position) and the box size
+ * AABB (Axis Aligned Bounding Box)
+ * This is implemented by a point (position) and the box size.
*/
+
class Variant;
-class _NO_DISCARD_ AABB {
-public:
+struct _NO_DISCARD_ AABB {
Vector3 position;
Vector3 size;
diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h
index 94fc3de2b1..8b244e9fe4 100644
--- a/core/math/audio_frame.h
+++ b/core/math/audio_frame.h
@@ -140,4 +140,16 @@ struct AudioFrame {
_ALWAYS_INLINE_ AudioFrame() {}
};
+_ALWAYS_INLINE_ AudioFrame operator*(float p_scalar, const AudioFrame &p_frame) {
+ return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+}
+
+_ALWAYS_INLINE_ AudioFrame operator*(int32_t p_scalar, const AudioFrame &p_frame) {
+ return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+}
+
+_ALWAYS_INLINE_ AudioFrame operator*(int64_t p_scalar, const AudioFrame &p_frame) {
+ return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+}
+
#endif // AUDIO_FRAME_H
diff --git a/core/math/basis.h b/core/math/basis.h
index 802da82089..683f05150c 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -34,11 +34,7 @@
#include "core/math/quaternion.h"
#include "core/math/vector3.h"
-class _NO_DISCARD_ Basis {
-private:
- void _set_diagonal(const Vector3 &p_diag);
-
-public:
+struct _NO_DISCARD_ Basis {
Vector3 elements[3] = {
Vector3(1, 0, 0),
Vector3(0, 1, 0),
@@ -263,6 +259,10 @@ public:
}
_FORCE_INLINE_ Basis() {}
+
+private:
+ // Helper method.
+ void _set_diagonal(const Vector3 &p_diag);
};
_FORCE_INLINE_ void Basis::operator*=(const Basis &p_matrix) {
@@ -334,4 +334,5 @@ real_t Basis::determinant() const {
elements[1][0] * (elements[0][1] * elements[2][2] - elements[2][1] * elements[0][2]) +
elements[2][0] * (elements[0][1] * elements[1][2] - elements[1][1] * elements[0][2]);
}
+
#endif // BASIS_H
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 2902ca59b9..f5d746ef0f 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -30,7 +30,11 @@
#include "camera_matrix.h"
+#include "core/math/aabb.h"
#include "core/math/math_funcs.h"
+#include "core/math/plane.h"
+#include "core/math/rect2.h"
+#include "core/math/transform_3d.h"
#include "core/string/print_string.h"
float CameraMatrix::determinant() const {
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index da1aba7562..285d2ae384 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -31,8 +31,14 @@
#ifndef CAMERA_MATRIX_H
#define CAMERA_MATRIX_H
-#include "core/math/rect2.h"
-#include "core/math/transform_3d.h"
+#include "core/math/math_defs.h"
+#include "core/math/vector3.h"
+
+struct AABB;
+struct Plane;
+struct Rect2;
+struct Transform3D;
+struct Vector2;
struct CameraMatrix {
enum Planes {
diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h
index 08f5df8472..c39997d6a9 100644
--- a/core/math/delaunay_2d.h
+++ b/core/math/delaunay_2d.h
@@ -32,6 +32,7 @@
#define DELAUNAY_2D_H
#include "core/math/rect2.h"
+#include "core/templates/vector.h"
class Delaunay2D {
public:
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h
index 3041cdf268..50ec2c2b30 100644
--- a/core/math/dynamic_bvh.h
+++ b/core/math/dynamic_bvh.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef DYNAMICBVH_H
-#define DYNAMICBVH_H
+#ifndef DYNAMIC_BVH_H
+#define DYNAMIC_BVH_H
#include "core/math/aabb.h"
#include "core/templates/list.h"
@@ -474,4 +474,4 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
} while (depth > 0);
}
-#endif // DYNAMICBVH_H
+#endif // DYNAMIC_BVH_H
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 203566579d..0ddac9744e 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -37,10 +37,6 @@
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
-static bool _is_number(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
Error Expression::_get_token(Token &r_token) {
while (true) {
#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++])
@@ -88,7 +84,7 @@ Error Expression::_get_token(Token &r_token) {
r_token.type = TK_INPUT;
int index = 0;
do {
- if (!_is_number(expression[str_ofs])) {
+ if (!is_digit(expression[str_ofs])) {
_set_error("Expected number after '$'");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
@@ -97,7 +93,7 @@ Error Expression::_get_token(Token &r_token) {
index += expression[str_ofs] - '0';
str_ofs++;
- } while (_is_number(expression[str_ofs]));
+ } while (is_digit(expression[str_ofs]));
r_token.value = index;
return OK;
@@ -247,13 +243,13 @@ Error Expression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!(_is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
_set_error("Malformed hex constant in string");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (_is_number(c)) {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -328,39 +324,67 @@ Error Expression::_get_token(Token &r_token) {
}
char32_t next_char = (str_ofs >= expression.length()) ? 0 : expression[str_ofs];
- if (_is_number(cchar) || (cchar == '.' && _is_number(next_char))) {
+ if (is_digit(cchar) || (cchar == '.' && is_digit(next_char))) {
//a number
String num;
#define READING_SIGN 0
#define READING_INT 1
-#define READING_DEC 2
-#define READING_EXP 3
-#define READING_DONE 4
+#define READING_HEX 2
+#define READING_BIN 3
+#define READING_DEC 4
+#define READING_EXP 5
+#define READING_DONE 6
int reading = READING_INT;
char32_t c = cchar;
bool exp_sign = false;
bool exp_beg = false;
+ bool bin_beg = false;
+ bool hex_beg = false;
bool is_float = false;
+ bool is_first_char = true;
while (true) {
switch (reading) {
case READING_INT: {
- if (_is_number(c)) {
- //pass
+ if (is_digit(c)) {
+ if (is_first_char && c == '0') {
+ if (next_char == 'b') {
+ reading = READING_BIN;
+ } else if (next_char == 'x') {
+ reading = READING_HEX;
+ }
+ }
} else if (c == '.') {
reading = READING_DEC;
is_float = true;
} else if (c == 'e') {
reading = READING_EXP;
+ is_float = true;
} else {
reading = READING_DONE;
}
} break;
+ case READING_BIN: {
+ if (bin_beg && !is_binary_digit(c)) {
+ reading = READING_DONE;
+ } else if (c == 'b') {
+ bin_beg = true;
+ }
+
+ } break;
+ case READING_HEX: {
+ if (hex_beg && !is_hex_digit(c)) {
+ reading = READING_DONE;
+ } else if (c == 'x') {
+ hex_beg = true;
+ }
+
+ } break;
case READING_DEC: {
- if (_is_number(c)) {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
@@ -370,13 +394,10 @@ Error Expression::_get_token(Token &r_token) {
} break;
case READING_EXP: {
- if (_is_number(c)) {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
- if (c == '-') {
- is_float = true;
- }
exp_sign = true;
} else {
@@ -390,6 +411,7 @@ Error Expression::_get_token(Token &r_token) {
}
num += String::chr(c);
c = GET_CHAR();
+ is_first_char = false;
}
str_ofs--;
@@ -398,16 +420,20 @@ Error Expression::_get_token(Token &r_token) {
if (is_float) {
r_token.value = num.to_float();
+ } else if (bin_beg) {
+ r_token.value = num.bin_to_int();
+ } else if (hex_beg) {
+ r_token.value = num.hex_to_int();
} else {
r_token.value = num.to_int();
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || is_underscore(cchar)) {
String id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && _is_number(cchar))) {
+ while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) {
id += String::chr(cchar);
cchar = GET_CHAR();
first = false;
diff --git a/core/math/face3.h b/core/math/face3.h
index 3dbbca09e0..8b123f078c 100644
--- a/core/math/face3.h
+++ b/core/math/face3.h
@@ -36,8 +36,7 @@
#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
-class _NO_DISCARD_ Face3 {
-public:
+struct _NO_DISCARD_ Face3 {
enum Side {
SIDE_OVER,
SIDE_UNDER,
@@ -48,14 +47,11 @@ public:
Vector3 vertex[3];
/**
- *
* @param p_plane plane used to split the face
* @param p_res array of at least 3 faces, amount used in function return
* @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return
- * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen)
* @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3
*/
-
int split_by_plane(const Plane &p_plane, Face3 *p_res, bool *p_is_point_over) const;
Plane get_plane(ClockDirection p_dir = CLOCKWISE) const;
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 7385dba438..a2881d5f60 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -32,7 +32,11 @@
#define GEOMETRY_2D_H
#include "core/math/delaunay_2d.h"
+#include "core/math/math_funcs.h"
#include "core/math/triangulate.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/math/vector3.h"
#include "core/math/vector3i.h"
#include "core/templates/vector.h"
diff --git a/core/math/plane.h b/core/math/plane.h
index 8cb6f62b3b..66c1741662 100644
--- a/core/math/plane.h
+++ b/core/math/plane.h
@@ -35,13 +35,12 @@
class Variant;
-class _NO_DISCARD_ Plane {
-public:
+struct _NO_DISCARD_ Plane {
Vector3 normal;
real_t d = 0;
void set_normal(const Vector3 &p_normal);
- _FORCE_INLINE_ Vector3 get_normal() const { return normal; }; ///Point is coplanar, CMP_EPSILON for precision
+ _FORCE_INLINE_ Vector3 get_normal() const { return normal; };
void normalize();
Plane normalized() const;
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index 2575d7d229..7874e4f428 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -36,8 +36,7 @@
#include "core/math/vector3.h"
#include "core/string/ustring.h"
-class _NO_DISCARD_ Quaternion {
-public:
+struct _NO_DISCARD_ Quaternion {
union {
struct {
real_t x;
diff --git a/core/math/rect2.cpp b/core/math/rect2.cpp
index 9047c19434..d6e20bdc3c 100644
--- a/core/math/rect2.cpp
+++ b/core/math/rect2.cpp
@@ -28,7 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/math/transform_2d.h" // Includes rect2.h but Rect2 needs Transform2D
+#include "rect2.h"
+
+#include "core/math/rect2i.h"
+#include "core/math/transform_2d.h"
+#include "core/string/ustring.h"
bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
@@ -278,6 +282,6 @@ Rect2::operator String() const {
return "[P: " + position.operator String() + ", S: " + size + "]";
}
-Rect2i::operator String() const {
- return "[P: " + position.operator String() + ", S: " + size + "]";
+Rect2::operator Rect2i() const {
+ return Rect2i(position, size);
}
diff --git a/core/math/rect2.h b/core/math/rect2.h
index b14c69302c..6ecc02336c 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -31,8 +31,11 @@
#ifndef RECT2_H
#define RECT2_H
-#include "core/math/vector2.h" // also includes math_funcs and ustring
+#include "core/error/error_macros.h"
+#include "core/math/vector2.h"
+class String;
+struct Rect2i;
struct Transform2D;
struct _NO_DISCARD_ Rect2 {
@@ -179,6 +182,7 @@ struct _NO_DISCARD_ Rect2 {
return new_rect;
}
+
inline bool has_point(const Point2 &p_point) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0)) {
@@ -201,6 +205,7 @@ struct _NO_DISCARD_ Rect2 {
return true;
}
+
bool is_equal_approx(const Rect2 &p_rect) const;
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
@@ -351,6 +356,7 @@ struct _NO_DISCARD_ Rect2 {
}
operator String() const;
+ operator Rect2i() const;
Rect2() {}
Rect2(real_t p_x, real_t p_y, real_t p_width, real_t p_height) :
@@ -363,214 +369,4 @@ struct _NO_DISCARD_ Rect2 {
}
};
-struct _NO_DISCARD_ Rect2i {
- Point2i position;
- Size2i size;
-
- const Point2i &get_position() const { return position; }
- void set_position(const Point2i &p_position) { position = p_position; }
- const Size2i &get_size() const { return size; }
- void set_size(const Size2i &p_size) { size = p_size; }
-
- int get_area() const { return size.width * size.height; }
-
- _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); }
-
- inline bool intersects(const Rect2i &p_rect) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- if (position.x >= (p_rect.position.x + p_rect.size.width)) {
- return false;
- }
- if ((position.x + size.width) <= p_rect.position.x) {
- return false;
- }
- if (position.y >= (p_rect.position.y + p_rect.size.height)) {
- return false;
- }
- if ((position.y + size.height) <= p_rect.position.y) {
- return false;
- }
-
- return true;
- }
-
- inline bool encloses(const Rect2i &p_rect) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
- ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
- ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
- }
-
- _FORCE_INLINE_ bool has_no_area() const {
- return (size.x <= 0 || size.y <= 0);
- }
-
- // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection
- inline Rect2i intersection(const Rect2i &p_rect) const {
- Rect2i new_rect = p_rect;
-
- if (!intersects(new_rect)) {
- return Rect2i();
- }
-
- new_rect.position.x = MAX(p_rect.position.x, position.x);
- new_rect.position.y = MAX(p_rect.position.y, position.y);
-
- Point2i p_rect_end = p_rect.position + p_rect.size;
- Point2i end = position + size;
-
- new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x;
- new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;
-
- return new_rect;
- }
-
- inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- Rect2i new_rect;
-
- new_rect.position.x = MIN(p_rect.position.x, position.x);
- new_rect.position.y = MIN(p_rect.position.y, position.y);
-
- new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x);
- new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y);
-
- new_rect.size = new_rect.size - new_rect.position; //make relative again
-
- return new_rect;
- }
- bool has_point(const Point2i &p_point) const {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- if (p_point.x < position.x) {
- return false;
- }
- if (p_point.y < position.y) {
- return false;
- }
-
- if (p_point.x >= (position.x + size.x)) {
- return false;
- }
- if (p_point.y >= (position.y + size.y)) {
- return false;
- }
-
- return true;
- }
-
- bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; }
- bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }
-
- Rect2i grow(int p_amount) const {
- Rect2i g = *this;
- g.position.x -= p_amount;
- g.position.y -= p_amount;
- g.size.width += p_amount * 2;
- g.size.height += p_amount * 2;
- return g;
- }
-
- inline Rect2i grow_side(Side p_side, int p_amount) const {
- Rect2i g = *this;
- g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0,
- (SIDE_TOP == p_side) ? p_amount : 0,
- (SIDE_RIGHT == p_side) ? p_amount : 0,
- (SIDE_BOTTOM == p_side) ? p_amount : 0);
- return g;
- }
-
- inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const {
- return grow_side(Side(p_side), p_amount);
- }
-
- inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const {
- Rect2i g = *this;
- g.position.x -= p_left;
- g.position.y -= p_top;
- g.size.width += p_left + p_right;
- g.size.height += p_top + p_bottom;
-
- return g;
- }
-
- _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const {
- Rect2i r = *this;
- r.expand_to(p_vector);
- return r;
- }
-
- inline void expand_to(const Point2i &p_vector) {
-#ifdef MATH_CHECKS
- if (unlikely(size.x < 0 || size.y < 0)) {
- ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
- }
-#endif
- Point2i begin = position;
- Point2i end = position + size;
-
- if (p_vector.x < begin.x) {
- begin.x = p_vector.x;
- }
- if (p_vector.y < begin.y) {
- begin.y = p_vector.y;
- }
-
- if (p_vector.x > end.x) {
- end.x = p_vector.x;
- }
- if (p_vector.y > end.y) {
- end.y = p_vector.y;
- }
-
- position = begin;
- size = end - begin;
- }
-
- _FORCE_INLINE_ Rect2i abs() const {
- return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
- }
-
- _FORCE_INLINE_ void set_end(const Vector2i &p_end) {
- size = p_end - position;
- }
-
- _FORCE_INLINE_ Vector2i get_end() const {
- return position + size;
- }
-
- operator String() const;
-
- operator Rect2() const { return Rect2(position, size); }
-
- Rect2i() {}
- Rect2i(const Rect2 &p_r2) :
- position(p_r2.position),
- size(p_r2.size) {
- }
- Rect2i(int p_x, int p_y, int p_width, int p_height) :
- position(Point2i(p_x, p_y)),
- size(Size2i(p_width, p_height)) {
- }
- Rect2i(const Point2i &p_pos, const Size2i &p_size) :
- position(p_pos),
- size(p_size) {
- }
-};
-
#endif // RECT2_H
diff --git a/core/math/rect2i.cpp b/core/math/rect2i.cpp
new file mode 100644
index 0000000000..0782c450d0
--- /dev/null
+++ b/core/math/rect2i.cpp
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* rect2i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "rect2i.h"
+
+#include "core/math/rect2.h"
+#include "core/string/ustring.h"
+
+Rect2i::operator String() const {
+ return "[P: " + position.operator String() + ", S: " + size + "]";
+}
+
+Rect2i::operator Rect2() const {
+ return Rect2(position, size);
+}
diff --git a/core/math/rect2i.h b/core/math/rect2i.h
new file mode 100644
index 0000000000..db1459a3e6
--- /dev/null
+++ b/core/math/rect2i.h
@@ -0,0 +1,245 @@
+/*************************************************************************/
+/* rect2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RECT2I_H
+#define RECT2I_H
+
+#include "core/error/error_macros.h"
+#include "core/math/vector2i.h"
+
+class String;
+struct Rect2;
+
+struct _NO_DISCARD_ Rect2i {
+ Point2i position;
+ Size2i size;
+
+ const Point2i &get_position() const { return position; }
+ void set_position(const Point2i &p_position) { position = p_position; }
+ const Size2i &get_size() const { return size; }
+ void set_size(const Size2i &p_size) { size = p_size; }
+
+ int get_area() const { return size.width * size.height; }
+
+ _FORCE_INLINE_ Vector2i get_center() const { return position + (size / 2); }
+
+ inline bool intersects(const Rect2i &p_rect) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ if (position.x >= (p_rect.position.x + p_rect.size.width)) {
+ return false;
+ }
+ if ((position.x + size.width) <= p_rect.position.x) {
+ return false;
+ }
+ if (position.y >= (p_rect.position.y + p_rect.size.height)) {
+ return false;
+ }
+ if ((position.y + size.height) <= p_rect.position.y) {
+ return false;
+ }
+
+ return true;
+ }
+
+ inline bool encloses(const Rect2i &p_rect) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ return (p_rect.position.x >= position.x) && (p_rect.position.y >= position.y) &&
+ ((p_rect.position.x + p_rect.size.x) <= (position.x + size.x)) &&
+ ((p_rect.position.y + p_rect.size.y) <= (position.y + size.y));
+ }
+
+ _FORCE_INLINE_ bool has_no_area() const {
+ return (size.x <= 0 || size.y <= 0);
+ }
+
+ // Returns the instersection between two Rect2is or an empty Rect2i if there is no intersection
+ inline Rect2i intersection(const Rect2i &p_rect) const {
+ Rect2i new_rect = p_rect;
+
+ if (!intersects(new_rect)) {
+ return Rect2i();
+ }
+
+ new_rect.position.x = MAX(p_rect.position.x, position.x);
+ new_rect.position.y = MAX(p_rect.position.y, position.y);
+
+ Point2i p_rect_end = p_rect.position + p_rect.size;
+ Point2i end = position + size;
+
+ new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x;
+ new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y;
+
+ return new_rect;
+ }
+
+ inline Rect2i merge(const Rect2i &p_rect) const { ///< return a merged rect
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ Rect2i new_rect;
+
+ new_rect.position.x = MIN(p_rect.position.x, position.x);
+ new_rect.position.y = MIN(p_rect.position.y, position.y);
+
+ new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x);
+ new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y);
+
+ new_rect.size = new_rect.size - new_rect.position; //make relative again
+
+ return new_rect;
+ }
+ bool has_point(const Point2i &p_point) const {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ if (p_point.x < position.x) {
+ return false;
+ }
+ if (p_point.y < position.y) {
+ return false;
+ }
+
+ if (p_point.x >= (position.x + size.x)) {
+ return false;
+ }
+ if (p_point.y >= (position.y + size.y)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool operator==(const Rect2i &p_rect) const { return position == p_rect.position && size == p_rect.size; }
+ bool operator!=(const Rect2i &p_rect) const { return position != p_rect.position || size != p_rect.size; }
+
+ Rect2i grow(int p_amount) const {
+ Rect2i g = *this;
+ g.position.x -= p_amount;
+ g.position.y -= p_amount;
+ g.size.width += p_amount * 2;
+ g.size.height += p_amount * 2;
+ return g;
+ }
+
+ inline Rect2i grow_side(Side p_side, int p_amount) const {
+ Rect2i g = *this;
+ g = g.grow_individual((SIDE_LEFT == p_side) ? p_amount : 0,
+ (SIDE_TOP == p_side) ? p_amount : 0,
+ (SIDE_RIGHT == p_side) ? p_amount : 0,
+ (SIDE_BOTTOM == p_side) ? p_amount : 0);
+ return g;
+ }
+
+ inline Rect2i grow_side_bind(uint32_t p_side, int p_amount) const {
+ return grow_side(Side(p_side), p_amount);
+ }
+
+ inline Rect2i grow_individual(int p_left, int p_top, int p_right, int p_bottom) const {
+ Rect2i g = *this;
+ g.position.x -= p_left;
+ g.position.y -= p_top;
+ g.size.width += p_left + p_right;
+ g.size.height += p_top + p_bottom;
+
+ return g;
+ }
+
+ _FORCE_INLINE_ Rect2i expand(const Vector2i &p_vector) const {
+ Rect2i r = *this;
+ r.expand_to(p_vector);
+ return r;
+ }
+
+ inline void expand_to(const Point2i &p_vector) {
+#ifdef MATH_CHECKS
+ if (unlikely(size.x < 0 || size.y < 0)) {
+ ERR_PRINT("Rect2i size is negative, this is not supported. Use Rect2i.abs() to get a Rect2i with a positive size.");
+ }
+#endif
+ Point2i begin = position;
+ Point2i end = position + size;
+
+ if (p_vector.x < begin.x) {
+ begin.x = p_vector.x;
+ }
+ if (p_vector.y < begin.y) {
+ begin.y = p_vector.y;
+ }
+
+ if (p_vector.x > end.x) {
+ end.x = p_vector.x;
+ }
+ if (p_vector.y > end.y) {
+ end.y = p_vector.y;
+ }
+
+ position = begin;
+ size = end - begin;
+ }
+
+ _FORCE_INLINE_ Rect2i abs() const {
+ return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
+ }
+
+ _FORCE_INLINE_ void set_end(const Vector2i &p_end) {
+ size = p_end - position;
+ }
+
+ _FORCE_INLINE_ Vector2i get_end() const {
+ return position + size;
+ }
+
+ operator String() const;
+ operator Rect2() const;
+
+ Rect2i() {}
+ Rect2i(int p_x, int p_y, int p_width, int p_height) :
+ position(Point2i(p_x, p_y)),
+ size(Size2i(p_width, p_height)) {
+ }
+ Rect2i(const Point2i &p_pos, const Size2i &p_size) :
+ position(p_pos),
+ size(p_size) {
+ }
+};
+
+#endif // RECT2I_H
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index 0201cf575c..e6e24e9b32 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -30,6 +30,8 @@
#include "transform_2d.h"
+#include "core/string/ustring.h"
+
void Transform2D::invert() {
// FIXME: this function assumes the basis is a rotation matrix, with no scaling.
// Transform2D::affine_inverse can handle matrices with scaling, so GDScript should eventually use that.
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index 6c2d51bd9b..f4546c13c8 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -31,7 +31,12 @@
#ifndef TRANSFORM_2D_H
#define TRANSFORM_2D_H
-#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
+#include "core/math/math_funcs.h"
+#include "core/math/rect2.h"
+#include "core/math/vector2.h"
+#include "core/templates/vector.h"
+
+class String;
struct _NO_DISCARD_ Transform2D {
// Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper":
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index c16c278e74..3b4762e221 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -28,15 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TRANSFORM_H
-#define TRANSFORM_H
+#ifndef TRANSFORM_3D_H
+#define TRANSFORM_3D_H
#include "core/math/aabb.h"
#include "core/math/basis.h"
#include "core/math/plane.h"
-class _NO_DISCARD_ Transform3D {
-public:
+struct _NO_DISCARD_ Transform3D {
Basis basis;
Vector3 origin;
@@ -265,4 +264,4 @@ _FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Tra
return Plane(normal, d);
}
-#endif // TRANSFORM_H
+#endif // TRANSFORM_3D_H
diff --git a/core/math/triangulate.h b/core/math/triangulate.h
index d96bdb8cab..0bfcfcb978 100644
--- a/core/math/triangulate.h
+++ b/core/math/triangulate.h
@@ -32,6 +32,7 @@
#define TRIANGULATE_H
#include "core/math/vector2.h"
+#include "core/templates/vector.h"
/*
https://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index 676a0004ea..40149e8cc1 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -30,6 +30,9 @@
#include "vector2.h"
+#include "core/math/vector2i.h"
+#include "core/string/ustring.h"
+
real_t Vector2::angle() const {
return Math::atan2(y, x);
}
@@ -202,91 +205,6 @@ Vector2::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
}
-/* Vector2i */
-
-Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const {
- return Vector2i(
- CLAMP(x, p_min.x, p_max.x),
- CLAMP(y, p_min.y, p_max.y));
-}
-
-int64_t Vector2i::length_squared() const {
- return x * (int64_t)x + y * (int64_t)y;
-}
-
-double Vector2i::length() const {
- return Math::sqrt((double)length_squared());
-}
-
-Vector2i Vector2i::operator+(const Vector2i &p_v) const {
- return Vector2i(x + p_v.x, y + p_v.y);
-}
-
-void Vector2i::operator+=(const Vector2i &p_v) {
- x += p_v.x;
- y += p_v.y;
-}
-
-Vector2i Vector2i::operator-(const Vector2i &p_v) const {
- return Vector2i(x - p_v.x, y - p_v.y);
-}
-
-void Vector2i::operator-=(const Vector2i &p_v) {
- x -= p_v.x;
- y -= p_v.y;
-}
-
-Vector2i Vector2i::operator*(const Vector2i &p_v1) const {
- return Vector2i(x * p_v1.x, y * p_v1.y);
-}
-
-Vector2i Vector2i::operator*(const int32_t &rvalue) const {
- return Vector2i(x * rvalue, y * rvalue);
-}
-
-void Vector2i::operator*=(const int32_t &rvalue) {
- x *= rvalue;
- y *= rvalue;
-}
-
-Vector2i Vector2i::operator/(const Vector2i &p_v1) const {
- return Vector2i(x / p_v1.x, y / p_v1.y);
-}
-
-Vector2i Vector2i::operator/(const int32_t &rvalue) const {
- return Vector2i(x / rvalue, y / rvalue);
-}
-
-void Vector2i::operator/=(const int32_t &rvalue) {
- x /= rvalue;
- y /= rvalue;
-}
-
-Vector2i Vector2i::operator%(const Vector2i &p_v1) const {
- return Vector2i(x % p_v1.x, y % p_v1.y);
-}
-
-Vector2i Vector2i::operator%(const int32_t &rvalue) const {
- return Vector2i(x % rvalue, y % rvalue);
-}
-
-void Vector2i::operator%=(const int32_t &rvalue) {
- x %= rvalue;
- y %= rvalue;
-}
-
-Vector2i Vector2i::operator-() const {
- return Vector2i(-x, -y);
-}
-
-bool Vector2i::operator==(const Vector2i &p_vec2) const {
- return x == p_vec2.x && y == p_vec2.y;
-}
-
-bool Vector2i::operator!=(const Vector2i &p_vec2) const {
- return x != p_vec2.x || y != p_vec2.y;
-}
-
-Vector2i::operator String() const {
- return "(" + itos(x) + ", " + itos(y) + ")";
+Vector2::operator Vector2i() const {
+ return Vector2i(x, y);
}
diff --git a/core/math/vector2.h b/core/math/vector2.h
index af40b9e68d..123e3dc7b6 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -32,8 +32,8 @@
#define VECTOR2_H
#include "core/math/math_funcs.h"
-#include "core/string/ustring.h"
+class String;
struct Vector2i;
struct _NO_DISCARD_ Vector2 {
@@ -167,6 +167,7 @@ struct _NO_DISCARD_ Vector2 {
real_t aspect() const { return width / height; }
operator String() const;
+ operator Vector2i() const;
_FORCE_INLINE_ Vector2() {}
_FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) {
@@ -179,22 +180,6 @@ _FORCE_INLINE_ Vector2 Vector2::plane_project(const real_t p_d, const Vector2 &p
return p_vec - *this * (dot(p_vec) - p_d);
}
-_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) {
- return p_vec * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) {
- return p_vec * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) {
- return p_vec * p_scalar;
-}
-
-_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) {
- return p_vec * p_scalar;
-}
-
_FORCE_INLINE_ Vector2 Vector2::operator+(const Vector2 &p_v) const {
return Vector2(x + p_v.x, y + p_v.y);
}
@@ -279,116 +264,26 @@ Vector2 Vector2::direction_to(const Vector2 &p_to) const {
return ret;
}
-typedef Vector2 Size2;
-typedef Vector2 Point2;
-
-/* INTEGER STUFF */
-
-struct _NO_DISCARD_ Vector2i {
- enum Axis {
- AXIS_X,
- AXIS_Y,
- };
-
- union {
- int32_t x = 0;
- int32_t width;
- };
- union {
- int32_t y = 0;
- int32_t height;
- };
-
- _FORCE_INLINE_ int32_t &operator[](int p_idx) {
- return p_idx ? y : x;
- }
- _FORCE_INLINE_ const int32_t &operator[](int p_idx) const {
- return p_idx ? y : x;
- }
-
- _FORCE_INLINE_ Vector2i::Axis min_axis_index() const {
- return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y;
- }
-
- _FORCE_INLINE_ Vector2i::Axis max_axis_index() const {
- return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X;
- }
-
- Vector2i min(const Vector2i &p_vector2i) const {
- return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
- }
-
- Vector2i max(const Vector2i &p_vector2i) const {
- return Vector2(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
- }
-
- Vector2i operator+(const Vector2i &p_v) const;
- void operator+=(const Vector2i &p_v);
- Vector2i operator-(const Vector2i &p_v) const;
- void operator-=(const Vector2i &p_v);
- Vector2i operator*(const Vector2i &p_v1) const;
-
- Vector2i operator*(const int32_t &rvalue) const;
- void operator*=(const int32_t &rvalue);
-
- Vector2i operator/(const Vector2i &p_v1) const;
- Vector2i operator/(const int32_t &rvalue) const;
- void operator/=(const int32_t &rvalue);
-
- Vector2i operator%(const Vector2i &p_v1) const;
- Vector2i operator%(const int32_t &rvalue) const;
- void operator%=(const int32_t &rvalue);
-
- Vector2i operator-() const;
- bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
- bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); }
-
- bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); }
- bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }
-
- bool operator==(const Vector2i &p_vec2) const;
- bool operator!=(const Vector2i &p_vec2) const;
-
- int64_t length_squared() const;
- double length() const;
-
- real_t aspect() const { return width / (real_t)height; }
- Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); }
- Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }
- Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const;
-
- operator String() const;
+// Multiplication operators required to workaround issues with LLVM using implicit conversion
+// to Vector2i instead for integers where it should not.
- operator Vector2() const { return Vector2(x, y); }
-
- inline Vector2i() {}
- inline Vector2i(const Vector2 &p_vec2) {
- x = (int32_t)p_vec2.x;
- y = (int32_t)p_vec2.y;
- }
- inline Vector2i(const int32_t p_x, const int32_t p_y) {
- x = p_x;
- y = p_y;
- }
-};
-
-_FORCE_INLINE_ Vector2i operator*(const int32_t &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
+_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) {
+ return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2i operator*(const int64_t &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
+_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) {
+ return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2i operator*(const float &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
+_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) {
+ return p_vec * p_scalar;
}
-_FORCE_INLINE_ Vector2i operator*(const double &p_scalar, const Vector2i &p_vector) {
- return p_vector * p_scalar;
+_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) {
+ return p_vec * p_scalar;
}
-typedef Vector2i Size2i;
-typedef Vector2i Point2i;
+typedef Vector2 Size2;
+typedef Vector2 Point2;
#endif // VECTOR2_H
diff --git a/core/math/vector2i.cpp b/core/math/vector2i.cpp
new file mode 100644
index 0000000000..dfed42e4d6
--- /dev/null
+++ b/core/math/vector2i.cpp
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* vector2i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "vector2i.h"
+
+#include "core/math/vector2.h"
+#include "core/string/ustring.h"
+
+Vector2i Vector2i::clamp(const Vector2i &p_min, const Vector2i &p_max) const {
+ return Vector2i(
+ CLAMP(x, p_min.x, p_max.x),
+ CLAMP(y, p_min.y, p_max.y));
+}
+
+int64_t Vector2i::length_squared() const {
+ return x * (int64_t)x + y * (int64_t)y;
+}
+
+double Vector2i::length() const {
+ return Math::sqrt((double)length_squared());
+}
+
+Vector2i Vector2i::operator+(const Vector2i &p_v) const {
+ return Vector2i(x + p_v.x, y + p_v.y);
+}
+
+void Vector2i::operator+=(const Vector2i &p_v) {
+ x += p_v.x;
+ y += p_v.y;
+}
+
+Vector2i Vector2i::operator-(const Vector2i &p_v) const {
+ return Vector2i(x - p_v.x, y - p_v.y);
+}
+
+void Vector2i::operator-=(const Vector2i &p_v) {
+ x -= p_v.x;
+ y -= p_v.y;
+}
+
+Vector2i Vector2i::operator*(const Vector2i &p_v1) const {
+ return Vector2i(x * p_v1.x, y * p_v1.y);
+}
+
+Vector2i Vector2i::operator*(const int32_t &rvalue) const {
+ return Vector2i(x * rvalue, y * rvalue);
+}
+
+void Vector2i::operator*=(const int32_t &rvalue) {
+ x *= rvalue;
+ y *= rvalue;
+}
+
+Vector2i Vector2i::operator/(const Vector2i &p_v1) const {
+ return Vector2i(x / p_v1.x, y / p_v1.y);
+}
+
+Vector2i Vector2i::operator/(const int32_t &rvalue) const {
+ return Vector2i(x / rvalue, y / rvalue);
+}
+
+void Vector2i::operator/=(const int32_t &rvalue) {
+ x /= rvalue;
+ y /= rvalue;
+}
+
+Vector2i Vector2i::operator%(const Vector2i &p_v1) const {
+ return Vector2i(x % p_v1.x, y % p_v1.y);
+}
+
+Vector2i Vector2i::operator%(const int32_t &rvalue) const {
+ return Vector2i(x % rvalue, y % rvalue);
+}
+
+void Vector2i::operator%=(const int32_t &rvalue) {
+ x %= rvalue;
+ y %= rvalue;
+}
+
+Vector2i Vector2i::operator-() const {
+ return Vector2i(-x, -y);
+}
+
+bool Vector2i::operator==(const Vector2i &p_vec2) const {
+ return x == p_vec2.x && y == p_vec2.y;
+}
+
+bool Vector2i::operator!=(const Vector2i &p_vec2) const {
+ return x != p_vec2.x || y != p_vec2.y;
+}
+
+Vector2i::operator String() const {
+ return "(" + itos(x) + ", " + itos(y) + ")";
+}
+
+Vector2i::operator Vector2() const {
+ return Vector2((int32_t)x, (int32_t)y);
+}
diff --git a/core/math/vector2i.h b/core/math/vector2i.h
new file mode 100644
index 0000000000..707c8c9490
--- /dev/null
+++ b/core/math/vector2i.h
@@ -0,0 +1,143 @@
+/*************************************************************************/
+/* vector2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VECTOR2I_H
+#define VECTOR2I_H
+
+#include "core/math/math_funcs.h"
+
+class String;
+struct Vector2;
+
+struct _NO_DISCARD_ Vector2i {
+ enum Axis {
+ AXIS_X,
+ AXIS_Y,
+ };
+
+ union {
+ int32_t x = 0;
+ int32_t width;
+ };
+ union {
+ int32_t y = 0;
+ int32_t height;
+ };
+
+ _FORCE_INLINE_ int32_t &operator[](int p_idx) {
+ return p_idx ? y : x;
+ }
+ _FORCE_INLINE_ const int32_t &operator[](int p_idx) const {
+ return p_idx ? y : x;
+ }
+
+ _FORCE_INLINE_ Vector2i::Axis min_axis_index() const {
+ return x < y ? Vector2i::AXIS_X : Vector2i::AXIS_Y;
+ }
+
+ _FORCE_INLINE_ Vector2i::Axis max_axis_index() const {
+ return x < y ? Vector2i::AXIS_Y : Vector2i::AXIS_X;
+ }
+
+ Vector2i min(const Vector2i &p_vector2i) const {
+ return Vector2i(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y));
+ }
+
+ Vector2i max(const Vector2i &p_vector2i) const {
+ return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
+ }
+
+ Vector2i operator+(const Vector2i &p_v) const;
+ void operator+=(const Vector2i &p_v);
+ Vector2i operator-(const Vector2i &p_v) const;
+ void operator-=(const Vector2i &p_v);
+ Vector2i operator*(const Vector2i &p_v1) const;
+
+ Vector2i operator*(const int32_t &rvalue) const;
+ void operator*=(const int32_t &rvalue);
+
+ Vector2i operator/(const Vector2i &p_v1) const;
+ Vector2i operator/(const int32_t &rvalue) const;
+ void operator/=(const int32_t &rvalue);
+
+ Vector2i operator%(const Vector2i &p_v1) const;
+ Vector2i operator%(const int32_t &rvalue) const;
+ void operator%=(const int32_t &rvalue);
+
+ Vector2i operator-() const;
+ bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
+ bool operator>(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); }
+
+ bool operator<=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y <= p_vec2.y) : (x < p_vec2.x); }
+ bool operator>=(const Vector2i &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); }
+
+ bool operator==(const Vector2i &p_vec2) const;
+ bool operator!=(const Vector2i &p_vec2) const;
+
+ int64_t length_squared() const;
+ double length() const;
+
+ real_t aspect() const { return width / (real_t)height; }
+ Vector2i sign() const { return Vector2i(SIGN(x), SIGN(y)); }
+ Vector2i abs() const { return Vector2i(ABS(x), ABS(y)); }
+ Vector2i clamp(const Vector2i &p_min, const Vector2i &p_max) const;
+
+ operator String() const;
+ operator Vector2() const;
+
+ inline Vector2i() {}
+ inline Vector2i(const int32_t p_x, const int32_t p_y) {
+ x = p_x;
+ y = p_y;
+ }
+};
+
+// Multiplication operators required to workaround issues with LLVM using implicit conversion.
+
+_FORCE_INLINE_ Vector2i operator*(const int32_t p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const int64_t p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const float p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+_FORCE_INLINE_ Vector2i operator*(const double p_scalar, const Vector2i &p_vector) {
+ return p_vector * p_scalar;
+}
+
+typedef Vector2i Size2i;
+typedef Vector2i Point2i;
+
+#endif // VECTOR2I_H
diff --git a/core/math/vector3.h b/core/math/vector3.h
index b62edef40f..345329f7f3 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -35,7 +35,8 @@
#include "core/math/vector2.h"
#include "core/math/vector3i.h"
#include "core/string/ustring.h"
-class Basis;
+
+struct Basis;
struct _NO_DISCARD_ Vector3 {
static const int AXIS_COUNT = 3;
@@ -342,6 +343,9 @@ Vector3 &Vector3::operator*=(const real_t p_scalar) {
return *this;
}
+// Multiplication operators required to workaround issues with LLVM using implicit conversion
+// to Vector2i instead for integers where it should not.
+
_FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) {
return p_vec * p_scalar;
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 1564ee9173..d166de80aa 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -194,6 +194,12 @@ Vector3i &Vector3i::operator*=(const int32_t p_scalar) {
return *this;
}
+Vector3i Vector3i::operator*(const int32_t p_scalar) const {
+ return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
+}
+
+// Multiplication operators required to workaround issues with LLVM using implicit conversion.
+
_FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) {
return p_vector * p_scalar;
}
@@ -210,10 +216,6 @@ _FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vecto
return p_vector * p_scalar;
}
-Vector3i Vector3i::operator*(const int32_t p_scalar) const {
- return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
-}
-
Vector3i &Vector3i::operator/=(const int32_t p_scalar) {
x /= p_scalar;
y /= p_scalar;
diff --git a/core/multiplayer/multiplayer_api.cpp b/core/multiplayer/multiplayer_api.cpp
index 627825246a..c8cb333e2c 100644
--- a/core/multiplayer/multiplayer_api.cpp
+++ b/core/multiplayer/multiplayer_api.cpp
@@ -32,9 +32,6 @@
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
-#include "core/multiplayer/multiplayer_replicator.h"
-#include "core/multiplayer/rpc_manager.h"
-#include "scene/main/node.h"
#include <stdint.h>
@@ -42,6 +39,10 @@
#include "core/os/os.h"
#endif
+MultiplayerReplicationInterface *(*MultiplayerAPI::create_default_replication_interface)(MultiplayerAPI *p_multiplayer) = nullptr;
+MultiplayerRPCInterface *(*MultiplayerAPI::create_default_rpc_interface)(MultiplayerAPI *p_multiplayer) = nullptr;
+MultiplayerCacheInterface *(*MultiplayerAPI::create_default_cache_interface)(MultiplayerAPI *p_multiplayer) = nullptr;
+
#ifdef DEBUG_ENABLED
void MultiplayerAPI::profile_bandwidth(const String &p_inout, int p_size) {
if (EngineDebugger::is_profiling("multiplayer")) {
@@ -74,7 +75,7 @@ void MultiplayerAPI::poll() {
Error err = multiplayer_peer->get_packet(&packet, len);
if (err != OK) {
ERR_PRINT("Error getting packet!");
- break; // Something is wrong!
+ return; // Something is wrong!
}
remote_sender_id = sender;
@@ -82,29 +83,25 @@ void MultiplayerAPI::poll() {
remote_sender_id = 0;
if (!multiplayer_peer.is_valid()) {
- break; // It's also possible that a packet or RPC caused a disconnection, so also check here.
+ return; // It's also possible that a packet or RPC caused a disconnection, so also check here.
}
}
- if (multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTED) {
- replicator->poll();
- }
+ replicator->on_network_process();
}
void MultiplayerAPI::clear() {
- replicator->clear();
connected_peers.clear();
- path_get_cache.clear();
- path_send_cache.clear();
packet_cache.clear();
- last_send_cache_id = 1;
+ cache->clear();
}
-void MultiplayerAPI::set_root_node(Node *p_node) {
- root_node = p_node;
+void MultiplayerAPI::set_root_path(const NodePath &p_path) {
+ ERR_FAIL_COND_MSG(!p_path.is_absolute() && !p_path.is_empty(), "MultiplayerAPI root path must be absolute.");
+ root_path = p_path;
}
-Node *MultiplayerAPI::get_root_node() {
- return root_node;
+NodePath MultiplayerAPI::get_root_path() const {
+ return root_path;
}
void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
@@ -133,6 +130,7 @@ void MultiplayerAPI::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
multiplayer_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed));
multiplayer_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected));
}
+ replicator->on_reset();
}
Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const {
@@ -140,7 +138,7 @@ Ref<MultiplayerPeer> MultiplayerAPI::get_multiplayer_peer() const {
}
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
+ ERR_FAIL_COND_MSG(root_path.is_empty(), "Multiplayer root was not initialized. If you are using custom multiplayer, remember to set the root path via MultiplayerAPI.set_root_path before using it.");
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
#ifdef DEBUG_ENABLED
@@ -152,166 +150,32 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
switch (packet_type) {
case NETWORK_COMMAND_SIMPLIFY_PATH: {
- _process_simplify_path(p_from, p_packet, p_packet_len);
+ cache->process_simplify_path(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_CONFIRM_PATH: {
- _process_confirm_path(p_from, p_packet, p_packet_len);
+ cache->process_confirm_path(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_REMOTE_CALL: {
- rpc_manager->process_rpc(p_from, p_packet, p_packet_len);
+ rpc->process_rpc(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_RAW: {
_process_raw(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_SPAWN: {
- replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, true);
+ replicator->on_spawn_receive(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_DESPAWN: {
- replicator->process_spawn_despawn(p_from, p_packet, p_packet_len, false);
+ replicator->on_despawn_receive(p_from, p_packet, p_packet_len);
} break;
case NETWORK_COMMAND_SYNC: {
- replicator->process_sync(p_from, p_packet, p_packet_len);
+ replicator->on_sync_receive(p_from, p_packet, p_packet_len);
} break;
}
}
-void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
- int ofs = 1;
-
- String methods_md5;
- methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
- ofs += 33;
-
- int id = decode_uint32(&p_packet[ofs]);
- ofs += 4;
-
- String paths;
- paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- Node *node = root_node->get_node(path);
- ERR_FAIL_COND(node == nullptr);
- const bool valid_rpc_checksum = rpc_manager->get_rpc_md5(node) == methods_md5;
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- // Encode path to send ack.
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), nullptr);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 1 + len);
- packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH;
- packet.write[1] = valid_rpc_checksum;
- encode_cstring(pname.get_data(), &packet.write[2]);
-
- multiplayer_peer->set_transfer_channel(0);
- multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- multiplayer_peer->set_target_peer(p_from);
- multiplayer_peer->put_packet(packet.ptr(), packet.size());
-}
-
-void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
-
- const bool valid_rpc_checksum = p_packet[1];
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
-
- NodePath path = paths;
-
- if (valid_rpc_checksum == false) {
- ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
- }
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
-
- Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
- E->get() = true;
-}
-
-bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
- bool has_all_peers = true;
- List<int> peers_to_add; // If one is missing, take note to add it.
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- if (p_target < 0 && E->get() == -p_target) {
- continue; // Continue, excluded.
- }
-
- if (p_target > 0 && E->get() != p_target) {
- continue; // Continue, not for this peer.
- }
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
-
- if (!F || !F->get()) {
- // Path was not cached, or was cached but is unconfirmed.
- if (!F) {
- // Not cached at all, take note.
- peers_to_add.push_back(E->get());
- }
-
- has_all_peers = false;
- }
- }
-
- if (peers_to_add.size() > 0) {
- // Those that need to be added, send a message for this.
-
- // Encode function name.
- const CharString path = String(p_path).utf8();
- const int path_len = encode_cstring(path.get_data(), nullptr);
-
- // Extract MD5 from rpc methods list.
- const String methods_md5 = rpc_manager->get_rpc_md5(p_node);
- const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
-
- Vector<uint8_t> packet;
- packet.resize(1 + 4 + path_len + methods_md5_len);
- int ofs = 0;
-
- packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH;
- ofs += 1;
-
- ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
-
- ofs += encode_uint32(psc->id, &packet.write[ofs]);
-
- ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
-
- for (int &E : peers_to_add) {
- multiplayer_peer->set_target_peer(E); // To all of you.
- multiplayer_peer->set_transfer_channel(0);
- multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- multiplayer_peer->put_packet(packet.ptr(), packet.size());
-
- psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed.
- }
- }
-
- return has_all_peers;
-}
-
// The variant is compressed and encoded; The first byte contains all the meta
// information and the format is:
// - The first LSB 5 bits are used for the variant type.
@@ -324,7 +188,7 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC
#define ENCODE_16 1 << 5
#define ENCODE_32 2 << 5
#define ENCODE_64 3 << 5
-Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) {
+Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
// Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
@@ -385,7 +249,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint
} break;
default:
// Any other case is not yet compressed.
- Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding);
+ Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
@@ -399,7 +263,7 @@ Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint
return OK;
}
-Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) {
+Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
const uint8_t *buf = p_buffer;
int len = p_len;
@@ -458,7 +322,7 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui
}
} break;
default:
- Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding);
+ Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
if (err != OK) {
return err;
}
@@ -467,27 +331,86 @@ Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const ui
return OK;
}
+Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int size = 0;
+
+ if (p_count == 0) {
+ if (r_raw) {
+ *r_raw = true;
+ }
+ return OK;
+ }
+
+ // Try raw encoding optimization.
+ if (r_raw && p_count == 1) {
+ *r_raw = false;
+ const Variant &v = *(p_variants[0]);
+ if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
+ *r_raw = true;
+ const PackedByteArray pba = v;
+ if (p_buffer) {
+ memcpy(p_buffer, pba.ptr(), pba.size());
+ }
+ r_len += pba.size();
+ } else {
+ encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+ }
+
+ // Regular encoding.
+ for (int i = 0; i < p_count; i++) {
+ const Variant &v = *(p_variants[i]);
+ encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
+ r_len += size;
+ }
+ return OK;
+}
+
+Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
+ r_len = 0;
+ int argc = r_variants.size();
+ if (argc == 0 && p_raw) {
+ return OK;
+ }
+ ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
+ if (p_raw) {
+ r_len = p_len;
+ PackedByteArray pba;
+ pba.resize(p_len);
+ memcpy(pba.ptrw(), p_buffer, p_len);
+ r_variants.write[0] = pba;
+ return OK;
+ }
+
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
+
+ int vlen;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
+ r_len += vlen;
+ }
+ return OK;
+}
+
void MultiplayerAPI::_add_peer(int p_id) {
connected_peers.insert(p_id);
- path_get_cache.insert(p_id, PathGetCache());
- if (is_server()) {
- replicator->spawn_all(p_id);
- }
+ cache->on_peer_change(p_id, true);
+ replicator->on_peer_change(p_id, true);
emit_signal(SNAME("peer_connected"), p_id);
}
void MultiplayerAPI::_del_peer(int p_id) {
+ replicator->on_peer_change(p_id, false);
+ cache->on_peer_change(p_id, false);
connected_peers.erase(p_id);
- // Cleanup get cache.
- path_get_cache.erase(p_id);
- // Cleanup sent cache.
- // Some refactoring is needed to make this faster and do paths GC.
- List<NodePath> keys;
- path_send_cache.get_key_list(&keys);
- for (const NodePath &E : keys) {
- PathSentCache *psc = path_send_cache.getptr(E);
- psc->confirmed_peers.erase(p_id);
- }
emit_signal(SNAME("peer_disconnected"), p_id);
}
@@ -500,6 +423,7 @@ void MultiplayerAPI::_connection_failed() {
}
void MultiplayerAPI::_server_disconnected() {
+ replicator->on_reset();
emit_signal(SNAME("server_disconnected"));
}
@@ -537,41 +461,15 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac
}
bool MultiplayerAPI::is_cache_confirmed(NodePath p_path, int p_peer) {
- const PathSentCache *psc = path_send_cache.getptr(p_path);
- ERR_FAIL_COND_V(!psc, false);
- const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer);
- ERR_FAIL_COND_V(!F, false); // Should never happen.
- return F->get();
-}
-
-bool MultiplayerAPI::send_confirm_path(Node *p_node, NodePath p_path, int p_peer_id, int &r_id) {
- // See if the path is cached.
- PathSentCache *psc = path_send_cache.getptr(p_path);
- if (!psc) {
- // Path is not cached, create.
- path_send_cache[p_path] = PathSentCache();
- psc = path_send_cache.getptr(p_path);
- psc->id = last_send_cache_id++;
- }
- r_id = psc->id;
-
- // See if all peers have cached path (if so, call can be fast).
- return _send_confirm_path(p_node, p_path, psc, p_peer_id);
+ return cache->is_cache_confirmed(p_path, p_peer);
}
-Node *MultiplayerAPI::get_cached_node(int p_from, uint32_t p_node_id) {
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
- ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
-
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_node_id);
- ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_node_id, p_from));
+bool MultiplayerAPI::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
+ return cache->send_object_cache(p_obj, p_path, p_peer_id, r_id);
+}
- PathGetCache::NodeInfo *ni = &F->get();
- Node *node = root_node->get_node(ni->path);
- if (!node) {
- ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
- }
- return node;
+Object *MultiplayerAPI::get_cached_object(int p_from, uint32_t p_cache_id) {
+ return cache->get_cached_object(p_from, p_cache_id);
}
int MultiplayerAPI::get_unique_id() const {
@@ -612,17 +510,33 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
return allow_object_decoding;
}
-void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
- replicator->scene_enter_exit_notify(p_scene, p_node, p_enter);
+String MultiplayerAPI::get_rpc_md5(const Object *p_obj) const {
+ return rpc->get_rpc_md5(p_obj);
}
-void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
- rpc_manager->rpcp(p_node, p_peer_id, p_method, p_arg, p_argcount);
+void MultiplayerAPI::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+ rpc->rpcp(p_obj, p_peer_id, p_method, p_arg, p_argcount);
+}
+
+Error MultiplayerAPI::spawn(Object *p_object, Variant p_config) {
+ return replicator->on_spawn(p_object, p_config);
+}
+
+Error MultiplayerAPI::despawn(Object *p_object, Variant p_config) {
+ return replicator->on_despawn(p_object, p_config);
+}
+
+Error MultiplayerAPI::replication_start(Object *p_object, Variant p_config) {
+ return replicator->on_replication_start(p_object, p_config);
+}
+
+Error MultiplayerAPI::replication_stop(Object *p_object, Variant p_config) {
+ return replicator->on_replication_stop(p_object, p_config);
}
void MultiplayerAPI::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
- ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node);
+ ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerAPI::set_root_path);
+ ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerAPI::get_root_path);
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
@@ -638,14 +552,12 @@ void MultiplayerAPI::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerAPI::is_refusing_new_connections);
ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding);
ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed);
- ClassDB::bind_method(D_METHOD("get_replicator"), &MultiplayerAPI::get_replicator);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
ADD_PROPERTY_DEFAULT("refuse_new_connections", false);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "replicator", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerReplicator", PROPERTY_USAGE_NONE), "", "get_replicator");
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
@@ -656,13 +568,23 @@ void MultiplayerAPI::_bind_methods() {
}
MultiplayerAPI::MultiplayerAPI() {
- replicator = memnew(MultiplayerReplicator(this));
- rpc_manager = memnew(RPCManager(this));
- clear();
+ if (create_default_replication_interface) {
+ replicator = Ref<MultiplayerReplicationInterface>(create_default_replication_interface(this));
+ } else {
+ replicator.instantiate();
+ }
+ if (create_default_rpc_interface) {
+ rpc = Ref<MultiplayerRPCInterface>(create_default_rpc_interface(this));
+ } else {
+ rpc.instantiate();
+ }
+ if (create_default_cache_interface) {
+ cache = Ref<MultiplayerCacheInterface>(create_default_cache_interface(this));
+ } else {
+ cache.instantiate();
+ }
}
MultiplayerAPI::~MultiplayerAPI() {
clear();
- memdelete(replicator);
- memdelete(rpc_manager);
}
diff --git a/core/multiplayer/multiplayer_api.h b/core/multiplayer/multiplayer_api.h
index 713035428d..9fe67615e3 100644
--- a/core/multiplayer/multiplayer_api.h
+++ b/core/multiplayer/multiplayer_api.h
@@ -35,8 +35,54 @@
#include "core/multiplayer/multiplayer_peer.h"
#include "core/object/ref_counted.h"
-class MultiplayerReplicator;
-class RPCManager;
+class MultiplayerAPI;
+
+class MultiplayerReplicationInterface : public RefCounted {
+ GDCLASS(MultiplayerReplicationInterface, RefCounted);
+
+public:
+ virtual void on_peer_change(int p_id, bool p_connected) {}
+ virtual void on_reset() {}
+ virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) { return ERR_UNAVAILABLE; }
+ virtual Error on_spawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_despawn(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_replication_start(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual Error on_replication_stop(Object *p_obj, Variant p_config) { return ERR_UNAVAILABLE; }
+ virtual void on_network_process() {}
+
+ MultiplayerReplicationInterface() {}
+};
+
+class MultiplayerRPCInterface : public RefCounted {
+ GDCLASS(MultiplayerRPCInterface, RefCounted);
+
+public:
+ // Called by Node.rpc
+ virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {}
+ virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {}
+ virtual String get_rpc_md5(const Object *p_obj) const { return String(); }
+
+ MultiplayerRPCInterface() {}
+};
+
+class MultiplayerCacheInterface : public RefCounted {
+ GDCLASS(MultiplayerCacheInterface, RefCounted);
+
+public:
+ virtual void clear() {}
+ virtual void on_peer_change(int p_id, bool p_connected) {}
+ virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {}
+ virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {}
+
+ // Returns true if all peers have cached path.
+ virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) { return false; }
+ virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) { return nullptr; }
+ virtual bool is_cache_confirmed(NodePath p_path, int p_peer) { return false; }
+
+ MultiplayerCacheInterface() {}
+};
class MultiplayerAPI : public RefCounted {
GDCLASS(MultiplayerAPI, RefCounted);
@@ -66,67 +112,56 @@ public:
};
private:
- //path sent caches
- struct PathSentCache {
- Map<int, bool> confirmed_peers;
- int id;
- };
-
- //path get caches
- struct PathGetCache {
- struct NodeInfo {
- NodePath path;
- ObjectID instance;
- };
-
- Map<int, NodeInfo> nodes;
- };
-
Ref<MultiplayerPeer> multiplayer_peer;
Set<int> connected_peers;
int remote_sender_id = 0;
int remote_sender_override = 0;
- HashMap<NodePath, PathSentCache> path_send_cache;
- Map<int, PathGetCache> path_get_cache;
- int last_send_cache_id;
Vector<uint8_t> packet_cache;
- Node *root_node = nullptr;
+ NodePath root_path;
bool allow_object_decoding = false;
- MultiplayerReplicator *replicator = nullptr;
- RPCManager *rpc_manager = nullptr;
+ Ref<MultiplayerCacheInterface> cache;
+ Ref<MultiplayerReplicationInterface> replicator;
+ Ref<MultiplayerRPCInterface> rpc;
protected:
static void _bind_methods();
- bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len);
public:
+ static MultiplayerReplicationInterface *(*create_default_replication_interface)(MultiplayerAPI *p_multiplayer);
+ static MultiplayerRPCInterface *(*create_default_rpc_interface)(MultiplayerAPI *p_multiplayer);
+ static MultiplayerCacheInterface *(*create_default_cache_interface)(MultiplayerAPI *p_multiplayer);
+
+ static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
+ static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
+ static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
+ static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
+
void poll();
void clear();
- void set_root_node(Node *p_node);
- Node *get_root_node();
+ void set_root_path(const NodePath &p_path);
+ NodePath get_root_path() const;
void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer);
Ref<MultiplayerPeer> get_multiplayer_peer() const;
Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, Multiplayer::TransferMode p_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0);
- Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len);
- Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len);
-
- // Called by Node.rpc
- void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
- // Called by Node._notification
- void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
- // Called by replicator
- bool send_confirm_path(Node *p_node, NodePath p_path, int p_target, int &p_id);
- Node *get_cached_node(int p_from, uint32_t p_node_id);
+ // RPC API
+ void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ String get_rpc_md5(const Object *p_obj) const;
+ // Replication API
+ Error spawn(Object *p_object, Variant p_config);
+ Error despawn(Object *p_object, Variant p_config);
+ Error replication_start(Object *p_object, Variant p_config);
+ Error replication_stop(Object *p_object, Variant p_config);
+ // Cache API
+ bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id);
+ Object *get_cached_object(int p_from, uint32_t p_cache_id);
bool is_cache_confirmed(NodePath p_path, int p_peer);
void _add_peer(int p_id);
@@ -148,9 +183,6 @@ public:
void set_allow_object_decoding(bool p_enable);
bool is_object_decoding_allowed() const;
- MultiplayerReplicator *get_replicator() const { return replicator; }
- RPCManager *get_rpc_manager() const { return rpc_manager; }
-
#ifdef DEBUG_ENABLED
void profile_bandwidth(const String &p_inout, int p_size);
#endif
diff --git a/core/multiplayer/multiplayer_replicator.cpp b/core/multiplayer/multiplayer_replicator.cpp
deleted file mode 100644
index e7de8219c7..0000000000
--- a/core/multiplayer/multiplayer_replicator.cpp
+++ /dev/null
@@ -1,791 +0,0 @@
-/*************************************************************************/
-/* multiplayer_replicator.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "core/multiplayer/multiplayer_replicator.h"
-
-#include "core/io/marshalls.h"
-#include "scene/main/node.h"
-#include "scene/resources/packed_scene.h"
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) \
- packet_cache.resize(m_amount);
-
-Error MultiplayerReplicator::_sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer) {
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- SceneConfig &cfg = replications[p_scene_id];
- int full_size = 0;
- bool same_size = true;
- int last_size = 0;
- bool all_raw = true;
- struct EncodeInfo {
- int size = 0;
- bool raw = false;
- List<Variant> state;
- };
- Map<ObjectID, struct EncodeInfo> state;
- if (tracked_objects.has(p_scene_id)) {
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- Object *obj = ObjectDB::get_instance(obj_id);
- if (obj) {
- struct EncodeInfo info;
- Error err = _get_state(cfg.sync_properties, obj, info.state);
- ERR_CONTINUE(err);
- err = _encode_state(info.state, nullptr, info.size, &info.raw);
- ERR_CONTINUE(err);
- state[obj_id] = info;
- full_size += info.size;
- if (last_size && info.size != last_size) {
- same_size = false;
- }
- all_raw = all_raw && info.raw;
- last_size = info.size;
- }
- }
- }
- // Default implementation do not send empty updates.
- if (!full_size) {
- return OK;
- }
-#ifdef DEBUG_ENABLED
- if (full_size > 4096 && cfg.sync_interval) {
- WARN_PRINT_ONCE(vformat("The timed state update for scene %d is big (%d bytes) consider optimizing it", p_scene_id));
- }
-#endif
- if (same_size) {
- // This is fast and small. Should we allow more than 256 objects per type?
- // This costs us 1 byte.
- MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + 2 + full_size);
- } else {
- MAKE_ROOM(SYNC_CMD_OFFSET + 1 + 2 + state.size() * 2 + full_size);
- }
- int ofs = 0;
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC | (same_size ? BYTE_OR_ZERO_FLAG : 0);
- ofs = 1;
- ofs += encode_uint64(p_scene_id, &ptr[ofs]);
- ptr[ofs] = cfg.sync_recv++;
- ofs += 1;
- ofs += encode_uint16(state.size(), &ptr[ofs]);
- if (same_size) {
- ofs += encode_uint16(last_size + (all_raw ? 1 << 15 : 0), &ptr[ofs]);
- }
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- if (!state.has(obj_id)) {
- continue;
- }
- struct EncodeInfo &info = state[obj_id];
- Object *obj = ObjectDB::get_instance(obj_id);
- ERR_CONTINUE(!obj);
- int size = 0;
- if (!same_size) {
- // We need to encode the size of every object.
- ofs += encode_uint16(info.size + (info.raw ? 1 << 15 : 0), &ptr[ofs]);
- }
- Error err = _encode_state(info.state, &ptr[ofs], size, &info.raw);
- ERR_CONTINUE(err);
- ofs += size;
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_UNRELIABLE);
- return peer->put_packet(ptr, ofs);
-}
-
-void MultiplayerReplicator::_process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < SYNC_CMD_OFFSET + 5, "Invalid spawn packet received");
- ERR_FAIL_COND_MSG(!replications.has(p_id), "Invalid spawn ID received " + itos(p_id));
- SceneConfig &cfg = replications[p_id];
- ERR_FAIL_COND_MSG(cfg.mode != REPLICATION_MODE_SERVER || multiplayer->is_server(), "The default implementation only allows sync packets from the server");
- const bool same_size = p_packet[0] & BYTE_OR_ZERO_FLAG;
- int ofs = SYNC_CMD_OFFSET;
- int time = p_packet[ofs];
- // Skip old update.
- if (time < cfg.sync_recv && cfg.sync_recv - time < 127) {
- return;
- }
- cfg.sync_recv = time;
- ofs += 1;
- int count = decode_uint16(&p_packet[ofs]);
- ofs += 2;
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND(!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count);
-#else
- if (!tracked_objects.has(p_id) || tracked_objects[p_id].size() != count) {
- return;
- }
-#endif
- int data_size = 0;
- bool raw = false;
- if (same_size) {
- // This is fast and optimized.
- data_size = decode_uint16(&p_packet[ofs]);
- raw = (data_size & (1 << 15)) != 0;
- data_size = data_size & ~(1 << 15);
- ofs += 2;
- ERR_FAIL_COND(p_packet_len - ofs < data_size * count);
- }
- for (const ObjectID &obj_id : tracked_objects[p_id]) {
- Object *obj = ObjectDB::get_instance(obj_id);
- ERR_CONTINUE(!obj);
- if (!same_size) {
- // This is slow and wasteful.
- data_size = decode_uint16(&p_packet[ofs]);
- raw = (data_size & (1 << 15)) != 0;
- data_size = data_size & ~(1 << 15);
- ofs += 2;
- ERR_FAIL_COND(p_packet_len - ofs < data_size);
- }
- int size = 0;
- Error err = _decode_state(cfg.sync_properties, obj, &p_packet[ofs], data_size, size, raw);
- ofs += data_size;
- ERR_CONTINUE(err);
- ERR_CONTINUE(size != data_size);
- }
-}
-
-Error MultiplayerReplicator::_send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn) {
- ERR_FAIL_COND_V(p_spawn && !p_obj, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- Error err;
- // Prepare state
- List<Variant> state_variants;
- int state_len = 0;
- const SceneConfig &cfg = replications[p_scene_id];
- if (p_spawn) {
- if ((err = _get_state(cfg.properties, p_obj, state_variants)) != OK) {
- return err;
- }
- }
-
- bool is_raw = false;
- if (state_variants.size() == 1 && state_variants[0].get_type() == Variant::PACKED_BYTE_ARRAY) {
- is_raw = true;
- const PackedByteArray pba = state_variants[0];
- state_len = pba.size();
- } else if (state_variants.size()) {
- err = _encode_state(state_variants, nullptr, state_len);
- ERR_FAIL_COND_V(err, err);
- } else {
- is_raw = true;
- }
-
- int ofs = 0;
-
- // Prepare simplified path
- const Node *root_node = multiplayer->get_root_node();
- ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED);
- NodePath rel_path = (root_node->get_path()).rel_path_to(p_path);
- const Vector<StringName> names = rel_path.get_names();
- ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER);
-
- NodePath parent = NodePath(names.slice(0, names.size() - 1), false);
- ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent);
-
- int path_id = 0;
- multiplayer->send_confirm_path(root_node->get_node(parent), parent, p_peer_id, path_id);
-
- // Encode name and parent ID.
- CharString cname = String(names[names.size() - 1]).utf8();
- int nlen = encode_cstring(cname.get_data(), nullptr);
- MAKE_ROOM(SPAWN_CMD_OFFSET + 4 + 4 + nlen + state_len);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) | (is_raw ? BYTE_OR_ZERO_FLAG : 0);
- ofs = 1;
- ofs += encode_uint64(p_scene_id, &ptr[ofs]);
- ofs += encode_uint32(path_id, &ptr[ofs]);
- ofs += encode_uint32(nlen, &ptr[ofs]);
- ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
-
- // Encode state.
- if (!is_raw) {
- _encode_state(state_variants, &ptr[ofs], state_len);
- } else if (state_len) {
- PackedByteArray pba = state_variants[0];
- memcpy(&ptr[ofs], pba.ptr(), state_len);
- }
-
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- return peer->put_packet(ptr, ofs + state_len);
-}
-
-void MultiplayerReplicator::_process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET + 9, "Invalid spawn packet received");
- int ofs = SPAWN_CMD_OFFSET;
- uint32_t node_target = decode_uint32(&p_packet[ofs]);
- Node *parent = multiplayer->get_cached_node(p_from, node_target);
- ofs += 4;
- ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found.");
-
- uint32_t name_len = decode_uint32(&p_packet[ofs]);
- ofs += 4;
- ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len));
- ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size.");
-
- const String name = String::utf8((const char *)&p_packet[ofs], name_len);
- // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names.
- ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name));
- ofs += name_len;
-
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.mode == REPLICATION_MODE_SERVER && p_from == 1) {
- String scene_path = ResourceUID::get_singleton()->get_id_path(p_scene_id);
- if (p_spawn) {
- const bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
-
- ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name));
- RES res = ResourceLoader::load(scene_path);
- ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path);
- PackedScene *scene = Object::cast_to<PackedScene>(res.ptr());
- ERR_FAIL_COND(!scene);
- Node *node = scene->instantiate();
- ERR_FAIL_COND(!node);
- replicated_nodes[node->get_instance_id()] = p_scene_id;
- _track(p_scene_id, node);
- int size;
- _decode_state(cfg.properties, node, &p_packet[ofs], p_packet_len - ofs, size, is_raw);
- parent->_add_child_nocheck(node, name);
- emit_signal(SNAME("spawned"), p_scene_id, node);
- } else {
- ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name));
- Node *node = parent->get_node(name);
- ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name));
- emit_signal(SNAME("despawned"), p_scene_id, node);
- _untrack(p_scene_id, node);
- replicated_nodes.erase(node->get_instance_id());
- node->queue_delete();
- }
- } else {
- PackedByteArray data;
- if (p_packet_len > ofs) {
- data.resize(p_packet_len - ofs);
- memcpy(data.ptrw(), &p_packet[ofs], data.size());
- }
- if (p_spawn) {
- emit_signal(SNAME("spawn_requested"), p_from, p_scene_id, parent, name, data);
- } else {
- emit_signal(SNAME("despawn_requested"), p_from, p_scene_id, parent, name, data);
- }
- }
-}
-
-void MultiplayerReplicator::process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
- ResourceUID::ID id = decode_uint64(&p_packet[1]);
- ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
-
- const SceneConfig &cfg = replications[id];
- if (cfg.on_spawn_despawn_receive.is_valid()) {
- int ofs = SPAWN_CMD_OFFSET;
- bool is_raw = ((p_packet[0] & BYTE_OR_ZERO_FLAG) >> BYTE_OR_ZERO_SHIFT) == 1;
- Variant data;
- int left = p_packet_len - ofs;
- if (is_raw && left) {
- PackedByteArray pba;
- pba.resize(left);
- memcpy(pba.ptrw(), &p_packet[ofs], pba.size());
- data = pba;
- } else if (left) {
- ERR_FAIL_COND(decode_variant(data, &p_packet[ofs], left) != OK);
- }
-
- Variant args[4];
- args[0] = p_from;
- args[1] = id;
- args[2] = data;
- args[3] = p_spawn;
- const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_spawn_despawn_receive.call(argp, 4, ret, ce);
- ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom receive function failed");
- } else {
- _process_default_spawn_despawn(p_from, id, p_packet, p_packet_len, p_spawn);
- }
-}
-
-void MultiplayerReplicator::process_sync(int p_from, const uint8_t *p_packet, int p_packet_len) {
- ERR_FAIL_COND_MSG(p_packet_len < SPAWN_CMD_OFFSET, "Invalid spawn packet received");
- ResourceUID::ID id = decode_uint64(&p_packet[1]);
- ERR_FAIL_COND_MSG(!replications.has(id), "Invalid spawn ID received " + itos(id));
- const SceneConfig &cfg = replications[id];
- if (cfg.on_sync_receive.is_valid()) {
- Array objs;
- if (tracked_objects.has(id)) {
- objs.resize(tracked_objects[id].size());
- int idx = 0;
- for (const ObjectID &obj_id : tracked_objects[id]) {
- objs[idx++] = ObjectDB::get_instance(obj_id);
- }
- }
- PackedByteArray pba;
- pba.resize(p_packet_len - SYNC_CMD_OFFSET);
- if (pba.size()) {
- memcpy(pba.ptrw(), p_packet + SYNC_CMD_OFFSET, p_packet_len - SYNC_CMD_OFFSET);
- }
- Variant args[4] = { p_from, id, objs, pba };
- Variant *argp[4] = { args, &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_sync_receive.call((const Variant **)argp, 4, ret, ce);
- ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, "Custom sync function failed");
- } else {
- ERR_FAIL_COND_MSG(p_from != 1, "Default sync implementation only allow syncing from server to client");
- _process_default_sync(id, p_packet, p_packet_len);
- }
-}
-
-Error MultiplayerReplicator::_get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant) {
- ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Cannot encode null object");
- for (const StringName &prop : p_properties) {
- bool valid = false;
- const Variant v = p_obj->get(prop, &valid);
- ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
- r_variant.push_back(v);
- }
- return OK;
-}
-
-Error MultiplayerReplicator::_encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw) {
- r_len = 0;
- int size = 0;
-
- // Try raw encoding optimization.
- if (r_raw && p_variants.size() == 1) {
- *r_raw = false;
- const Variant v = p_variants[0];
- if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
- *r_raw = true;
- const PackedByteArray pba = v;
- if (p_buffer) {
- memcpy(p_buffer, pba.ptr(), pba.size());
- }
- r_len += pba.size();
- } else {
- multiplayer->encode_and_compress_variant(v, p_buffer, size);
- r_len += size;
- }
- return OK;
- }
-
- // Regular encoding.
- for (const Variant &v : p_variants) {
- multiplayer->encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size);
- r_len += size;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::_decode_state(const List<StringName> &p_properties, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw) {
- r_len = 0;
- int argc = p_properties.size();
- if (argc == 0 && p_raw) {
- ERR_FAIL_COND_V_MSG(p_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
- return OK;
- }
- ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
- if (p_raw) {
- r_len = p_len;
- PackedByteArray pba;
- pba.resize(p_len);
- memcpy(pba.ptrw(), p_buffer, p_len);
- p_obj->set(p_properties[0], pba);
- return OK;
- }
-
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
-
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_buffer[r_len], p_len - r_len, &vlen);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
- r_len += vlen;
- }
- ERR_FAIL_COND_V_MSG(p_len - r_len != 0, ERR_INVALID_DATA, "Buffer has trailing bytes.");
-
- int i = 0;
- for (const StringName &prop : p_properties) {
- p_obj->set(prop, args[i]);
- i += 1;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
- ERR_FAIL_COND_V(p_mode < REPLICATION_MODE_NONE || p_mode > REPLICATION_MODE_CUSTOM, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
-#ifdef TOOLS_ENABLED
- if (!p_on_send.is_valid()) {
- // We allow non scene spawning with custom callables.
- String path = ResourceUID::get_singleton()->get_id_path(p_id);
- RES res = ResourceLoader::load(path);
- ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER);
- }
-#endif
- if (p_mode == REPLICATION_MODE_NONE) {
- if (replications.has(p_id)) {
- replications.erase(p_id);
- }
- } else {
- SceneConfig cfg;
- cfg.mode = p_mode;
- for (int i = 0; i < p_props.size(); i++) {
- cfg.properties.push_back(p_props[i]);
- }
- cfg.on_spawn_despawn_send = p_on_send;
- cfg.on_spawn_despawn_receive = p_on_recv;
- replications[p_id] = cfg;
- }
- return OK;
-}
-
-Error MultiplayerReplicator::sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props, const Callable &p_on_send, const Callable &p_on_recv) {
- ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V_MSG(p_on_send.is_valid() != p_on_recv.is_valid(), ERR_INVALID_PARAMETER, "Send and receive custom callables must be both valid or both empty");
- ERR_FAIL_COND_V(!replications.has(p_id), ERR_UNCONFIGURED);
- SceneConfig &cfg = replications[p_id];
- ERR_FAIL_COND_V_MSG(p_interval && cfg.mode != REPLICATION_MODE_SERVER && !p_on_send.is_valid(), ERR_INVALID_PARAMETER, "Timed updates in custom mode are only allowed if custom callbacks are also specified");
- for (int i = 0; i < p_props.size(); i++) {
- cfg.sync_properties.push_back(p_props[i]);
- }
- cfg.on_sync_send = p_on_send;
- cfg.on_sync_receive = p_on_recv;
- cfg.sync_interval = p_interval * 1000;
- return OK;
-}
-
-Error MultiplayerReplicator::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn) {
- int data_size = 0;
- int is_raw = false;
- if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
- const PackedByteArray pba = p_data;
- is_raw = true;
- data_size = p_data.operator PackedByteArray().size();
- } else if (p_data.get_type() == Variant::NIL) {
- is_raw = true;
- } else {
- Error err = encode_variant(p_data, nullptr, data_size);
- ERR_FAIL_COND_V(err, err);
- }
- MAKE_ROOM(SPAWN_CMD_OFFSET + data_size);
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = (p_spawn ? MultiplayerAPI::NETWORK_COMMAND_SPAWN : MultiplayerAPI::NETWORK_COMMAND_DESPAWN) + ((is_raw ? 1 : 0) << BYTE_OR_ZERO_SHIFT);
- encode_uint64(p_scene_id, &ptr[1]);
- if (p_data.get_type() == Variant::PACKED_BYTE_ARRAY) {
- const PackedByteArray pba = p_data;
- memcpy(&ptr[SPAWN_CMD_OFFSET], pba.ptr(), pba.size());
- } else if (data_size) {
- encode_variant(p_data, &ptr[SPAWN_CMD_OFFSET], data_size);
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(0);
- peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
- return peer->put_packet(ptr, SPAWN_CMD_OFFSET + data_size);
-}
-
-Error MultiplayerReplicator::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, true);
- } else {
- ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual despawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
- NodePath path = p_path;
- Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
- if (path.is_empty() && obj) {
- Node *node = Object::cast_to<Node>(obj);
- if (node && node->is_inside_tree()) {
- path = node->get_path();
- }
- }
- ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Despawn default implementation requires a despawn path, or the data to be a node inside the SceneTree");
- return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, false);
- }
-}
-
-Error MultiplayerReplicator::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, const NodePath &p_path) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- return _send_spawn_despawn(p_peer_id, p_scene_id, p_data, false);
- } else {
- ERR_FAIL_COND_V_MSG(cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server(), ERR_UNAVAILABLE, "Manual spawn is restricted in default server mode implementation. Use custom mode if you desire control over server spawn requests.");
- NodePath path = p_path;
- Object *obj = p_data.get_type() == Variant::OBJECT ? p_data.get_validated_object() : nullptr;
- ERR_FAIL_COND_V_MSG(!obj, ERR_INVALID_PARAMETER, "Spawn default implementation requires the data to be an object.");
- if (path.is_empty()) {
- Node *node = Object::cast_to<Node>(obj);
- if (node && node->is_inside_tree()) {
- path = node->get_path();
- }
- }
- ERR_FAIL_COND_V_MSG(path.is_empty(), ERR_INVALID_PARAMETER, "Spawn default implementation requires a spawn path, or the data to be a node inside the SceneTree");
- return _send_default_spawn_despawn(p_peer_id, p_scene_id, obj, path, true);
- }
-}
-
-Error MultiplayerReplicator::_spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn) {
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
-
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_spawn_despawn_send.is_valid()) {
- Variant args[4];
- args[0] = p_peer;
- args[1] = p_scene_id;
- args[2] = p_obj;
- args[3] = p_spawn;
- const Variant *argp[] = { &args[0], &args[1], &args[2], &args[3] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_spawn_despawn_send.call(argp, 4, ret, ce);
- ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom send function failed");
- return OK;
- } else {
- Node *node = Object::cast_to<Node>(p_obj);
- ERR_FAIL_COND_V_MSG(!p_obj, ERR_INVALID_PARAMETER, "Only nodes can be replicated by the default implementation");
- return _send_default_spawn_despawn(p_peer, p_scene_id, node, node->get_path(), p_spawn);
- }
-}
-
-Error MultiplayerReplicator::spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
- return _spawn_despawn(p_scene_id, p_obj, p_peer, true);
-}
-
-Error MultiplayerReplicator::despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer) {
- return _spawn_despawn(p_scene_id, p_obj, p_peer, false);
-}
-
-PackedByteArray MultiplayerReplicator::encode_state(const ResourceUID::ID &p_scene_id, const Object *p_obj, bool p_initial) {
- PackedByteArray state;
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), state, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- int len = 0;
- List<Variant> state_vars;
- const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
- Error err = _get_state(props, p_obj, state_vars);
- ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to retrieve object state.");
- err = _encode_state(state_vars, nullptr, len);
- ERR_FAIL_COND_V_MSG(err != OK, state, "Unable to encode object state.");
- state.resize(len);
- _encode_state(state_vars, state.ptrw(), len);
- return state;
-}
-
-Error MultiplayerReplicator::decode_state(const ResourceUID::ID &p_scene_id, Object *p_obj, const PackedByteArray p_data, bool p_initial) {
- ERR_FAIL_COND_V_MSG(!replications.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- const List<StringName> props = p_initial ? cfg.properties : cfg.sync_properties;
- int size;
- return _decode_state(props, p_obj, p_data.ptr(), p_data.size(), size);
-}
-
-void MultiplayerReplicator::scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter) {
- if (!multiplayer->has_multiplayer_peer()) {
- return;
- }
- Node *root_node = multiplayer->get_root_node();
- ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node);
- NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path());
- if (path.is_empty()) {
- return;
- }
- ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene);
- if (!replications.has(id)) {
- return;
- }
- const SceneConfig &cfg = replications[id];
- if (p_enter) {
- if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server()) {
- replicated_nodes[p_node->get_instance_id()] = id;
- _track(id, p_node);
- spawn(id, p_node, 0);
- }
- emit_signal(SNAME("replicated_instance_added"), id, p_node);
- } else {
- if (cfg.mode == REPLICATION_MODE_SERVER && multiplayer->is_server() && replicated_nodes.has(p_node->get_instance_id())) {
- replicated_nodes.erase(p_node->get_instance_id());
- _untrack(id, p_node);
- despawn(id, p_node, 0);
- }
- emit_signal(SNAME("replicated_instance_removed"), id, p_node);
- }
-}
-
-void MultiplayerReplicator::spawn_all(int p_peer) {
- for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) {
- // Only server mode adds to replicated_nodes, no need to check it.
- Object *obj = ObjectDB::get_instance(E.key);
- ERR_CONTINUE(!obj);
- Node *node = Object::cast_to<Node>(obj);
- ERR_CONTINUE(!node);
- spawn(E.value, node, p_peer);
- }
-}
-
-void MultiplayerReplicator::poll() {
- for (KeyValue<ResourceUID::ID, SceneConfig> &E : replications) {
- if (!E.value.sync_interval) {
- continue;
- }
- if (E.value.mode == REPLICATION_MODE_SERVER && !multiplayer->is_server()) {
- continue;
- }
- uint64_t time = OS::get_singleton()->get_ticks_usec();
- if (E.value.sync_last + E.value.sync_interval <= time) {
- sync_all(E.key, 0);
- E.value.sync_last = time;
- }
- // Handle wrapping.
- if (E.value.sync_last > time) {
- E.value.sync_last = time;
- }
- }
-}
-
-void MultiplayerReplicator::track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!replications.has(p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
- _track(p_scene_id, p_obj);
-}
-
-void MultiplayerReplicator::_track(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!p_obj);
- ERR_FAIL_COND(!replications.has(p_scene_id));
- if (!tracked_objects.has(p_scene_id)) {
- tracked_objects[p_scene_id] = List<ObjectID>();
- }
- tracked_objects[p_scene_id].push_back(p_obj->get_instance_id());
-}
-
-void MultiplayerReplicator::untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!replications.has(p_scene_id));
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_MSG(cfg.mode == REPLICATION_MODE_SERVER, "Manual object tracking is not allowed in server mode.");
- _untrack(p_scene_id, p_obj);
-}
-
-void MultiplayerReplicator::_untrack(const ResourceUID::ID &p_scene_id, Object *p_obj) {
- ERR_FAIL_COND(!p_obj);
- ERR_FAIL_COND(!replications.has(p_scene_id));
- if (tracked_objects.has(p_scene_id)) {
- tracked_objects[p_scene_id].erase(p_obj->get_instance_id());
- }
-}
-
-Error MultiplayerReplicator::sync_all(const ResourceUID::ID &p_scene_id, int p_peer) {
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- if (!tracked_objects.has(p_scene_id)) {
- return OK;
- }
- const SceneConfig &cfg = replications[p_scene_id];
- if (cfg.on_sync_send.is_valid()) {
- Array objs;
- if (tracked_objects.has(p_scene_id)) {
- objs.resize(tracked_objects[p_scene_id].size());
- int idx = 0;
- for (const ObjectID &obj_id : tracked_objects[p_scene_id]) {
- objs[idx++] = ObjectDB::get_instance(obj_id);
- }
- }
- Variant args[3] = { p_scene_id, objs, p_peer };
- Variant *argp[3] = { args, &args[1], &args[2] };
- Callable::CallError ce;
- Variant ret;
- cfg.on_sync_send.call((const Variant **)argp, 3, ret, ce);
- ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, FAILED, "Custom sync function failed");
- return OK;
- } else if (cfg.sync_properties.size()) {
- return _sync_all_default(p_scene_id, p_peer);
- }
- return OK;
-}
-
-Error MultiplayerReplicator::send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
- ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
- ERR_FAIL_COND_V(!replications.has(p_scene_id), ERR_INVALID_PARAMETER);
- const SceneConfig &cfg = replications[p_scene_id];
- ERR_FAIL_COND_V_MSG(!cfg.on_sync_send.is_valid(), ERR_UNCONFIGURED, "Sending raw sync messages is only available with custom functions");
- MAKE_ROOM(SYNC_CMD_OFFSET + p_data.size());
- uint8_t *ptr = packet_cache.ptrw();
- ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
- encode_uint64(p_scene_id, &ptr[1]);
- if (p_data.size()) {
- memcpy(&ptr[SYNC_CMD_OFFSET], p_data.ptr(), p_data.size());
- }
- Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
- peer->set_target_peer(p_peer_id);
- peer->set_transfer_channel(p_channel);
- peer->set_transfer_mode(p_transfer_mode);
- return peer->put_packet(ptr, SYNC_CMD_OFFSET + p_data.size());
-}
-
-void MultiplayerReplicator::clear() {
- tracked_objects.clear();
- replicated_nodes.clear();
-}
-
-void MultiplayerReplicator::_bind_methods() {
- ClassDB::bind_method(D_METHOD("spawn_config", "scene_id", "spawn_mode", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::spawn_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
- ClassDB::bind_method(D_METHOD("sync_config", "scene_id", "interval", "properties", "custom_send", "custom_receive"), &MultiplayerReplicator::sync_config, DEFVAL(TypedArray<StringName>()), DEFVAL(Callable()), DEFVAL(Callable()));
- ClassDB::bind_method(D_METHOD("despawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::despawn, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("spawn", "scene_id", "object", "peer_id"), &MultiplayerReplicator::spawn, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_despawn, DEFVAL(Variant()), DEFVAL(NodePath()));
- ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "data", "path"), &MultiplayerReplicator::send_spawn, DEFVAL(Variant()), DEFVAL(NodePath()));
- ClassDB::bind_method(D_METHOD("send_sync", "peer_id", "scene_id", "data", "transfer_mode", "channel"), &MultiplayerReplicator::send_sync, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("sync_all", "scene_id", "peer_id"), &MultiplayerReplicator::sync_all, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("track", "scene_id", "object"), &MultiplayerReplicator::track);
- ClassDB::bind_method(D_METHOD("untrack", "scene_id", "object"), &MultiplayerReplicator::untrack);
- ClassDB::bind_method(D_METHOD("encode_state", "scene_id", "object", "initial"), &MultiplayerReplicator::encode_state, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("decode_state", "scene_id", "object", "data", "initial"), &MultiplayerReplicator::decode_state, DEFVAL(true));
-
- ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("despawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
- ADD_SIGNAL(MethodInfo("spawn_requested", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data")));
- ADD_SIGNAL(MethodInfo("replicated_instance_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
- ADD_SIGNAL(MethodInfo("replicated_instance_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
-
- BIND_ENUM_CONSTANT(REPLICATION_MODE_NONE);
- BIND_ENUM_CONSTANT(REPLICATION_MODE_SERVER);
- BIND_ENUM_CONSTANT(REPLICATION_MODE_CUSTOM);
-}
diff --git a/core/multiplayer/multiplayer_replicator.h b/core/multiplayer/multiplayer_replicator.h
deleted file mode 100644
index a9cd6e211e..0000000000
--- a/core/multiplayer/multiplayer_replicator.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*************************************************************************/
-/* multiplayer_replicator.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_REPLICATOR_H
-#define MULTIPLAYER_REPLICATOR_H
-
-#include "core/multiplayer/multiplayer_api.h"
-
-#include "core/io/resource_uid.h"
-#include "core/templates/hash_map.h"
-#include "core/variant/typed_array.h"
-
-class MultiplayerReplicator : public Object {
- GDCLASS(MultiplayerReplicator, Object);
-
-public:
- enum {
- SPAWN_CMD_OFFSET = 9,
- SYNC_CMD_OFFSET = 9,
- };
-
- enum ReplicationMode {
- REPLICATION_MODE_NONE,
- REPLICATION_MODE_SERVER,
- REPLICATION_MODE_CUSTOM,
- };
-
- struct SceneConfig {
- ReplicationMode mode;
- uint64_t sync_interval = 0;
- uint64_t sync_last = 0;
- uint8_t sync_recv = 0;
- List<StringName> properties;
- List<StringName> sync_properties;
- Callable on_spawn_despawn_send;
- Callable on_spawn_despawn_receive;
- Callable on_sync_send;
- Callable on_sync_receive;
- };
-
-protected:
- static void _bind_methods();
-
-private:
- enum {
- BYTE_OR_ZERO_SHIFT = MultiplayerAPI::CMD_FLAG_0_SHIFT,
- };
-
- enum {
- BYTE_OR_ZERO_FLAG = 1 << BYTE_OR_ZERO_SHIFT,
- };
-
- MultiplayerAPI *multiplayer = nullptr;
- Vector<uint8_t> packet_cache;
- Map<ResourceUID::ID, SceneConfig> replications;
- Map<ObjectID, ResourceUID::ID> replicated_nodes;
- HashMap<ResourceUID::ID, List<ObjectID>> tracked_objects;
-
- // Encoding
- Error _get_state(const List<StringName> &p_properties, const Object *p_obj, List<Variant> &r_variant);
- Error _encode_state(const List<Variant> &p_variants, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr);
- Error _decode_state(const List<StringName> &p_cfg, Object *p_obj, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false);
-
- // Spawn
- Error _spawn_despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer, bool p_spawn);
- Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data, bool p_spawn);
- void _process_default_spawn_despawn(int p_from, const ResourceUID::ID &p_scene_id, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
- Error _send_default_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, Object *p_obj, const NodePath &p_path, bool p_spawn);
-
- // Sync
- void _process_default_sync(const ResourceUID::ID &p_id, const uint8_t *p_packet, int p_packet_len);
- Error _sync_all_default(const ResourceUID::ID &p_scene_id, int p_peer);
- void _track(const ResourceUID::ID &p_scene_id, Object *p_object);
- void _untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
-
-public:
- void clear();
-
- // Encoding
- PackedByteArray encode_state(const ResourceUID::ID &p_scene_id, const Object *p_node, bool p_initial);
- Error decode_state(const ResourceUID::ID &p_scene_id, Object *p_node, PackedByteArray p_data, bool p_initial);
-
- // Spawn
- Error spawn_config(const ResourceUID::ID &p_id, ReplicationMode p_mode, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
- Error spawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
- Error despawn(ResourceUID::ID p_scene_id, Object *p_obj, int p_peer = 0);
- Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
- Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const Variant &p_data = Variant(), const NodePath &p_path = NodePath());
-
- // Sync
- Error sync_config(const ResourceUID::ID &p_id, uint64_t p_interval, const TypedArray<StringName> &p_props = TypedArray<StringName>(), const Callable &p_on_send = Callable(), const Callable &p_on_recv = Callable());
- Error sync_all(const ResourceUID::ID &p_scene_id, int p_peer);
- Error send_sync(int p_peer_id, const ResourceUID::ID &p_scene_id, PackedByteArray p_data, Multiplayer::TransferMode p_mode, int p_channel);
- void track(const ResourceUID::ID &p_scene_id, Object *p_object);
- void untrack(const ResourceUID::ID &p_scene_id, Object *p_object);
-
- // Used by MultiplayerAPI
- void spawn_all(int p_peer);
- void process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn);
- void process_sync(int p_from, const uint8_t *p_packet, int p_packet_len);
- void scene_enter_exit_notify(const String &p_scene, Node *p_node, bool p_enter);
- void poll();
-
- MultiplayerReplicator(MultiplayerAPI *p_multiplayer) {
- multiplayer = p_multiplayer;
- }
-};
-
-VARIANT_ENUM_CAST(MultiplayerReplicator::ReplicationMode);
-
-#endif // MULTIPLAYER_REPLICATOR_H
diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index 53410a9acf..3cd9ad3819 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -51,6 +51,14 @@ protected:
void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size);
public:
+ virtual StringName get_method() const {
+#ifdef DEBUG_METHODS_ENABLED
+ return StringName(text);
+#else
+ return StringName();
+#endif
+ }
+
#ifdef DEBUG_METHODS_ENABLED
void set_text(const char *p_text) {
text = p_text;
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 03774bc36a..3df4db9c5e 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -732,7 +732,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
String enum_name = p_enum;
if (!enum_name.is_empty()) {
- if (enum_name.find(".") != -1) {
+ if (enum_name.contains(".")) {
enum_name = enum_name.get_slicec('.', 1);
}
@@ -1632,7 +1632,8 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
// Some properties may have an instantiated Object as default value,
// (like Path2D's `curve` used to have), but that's not a good practice.
// Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT
- // to be auto-instantiated when created in the editor.
+ // to be auto-instantiated when created in the editor with the following method:
+ // EditorNode::get_editor_data().instantiate_object_properties(obj);
if (var.get_type() == Variant::OBJECT) {
Object *obj = var.get_validated_object();
if (obj) {
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index e961745d96..5de1b49026 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -123,9 +123,9 @@ def generate_version(argcount, const=False, returns=False):
callargtext += ","
callargtext += " m_ret& r_ret"
s = s.replace("$CALLSIBEGIN", "Variant ret = ")
- s = s.replace("$CALLSIRET", "r_ret = ret;")
+ s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);")
s = s.replace("$CALLPTRRETPASS", "&ret")
- s = s.replace("$CALLPTRRET", "r_ret = ret;")
+ s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;")
else:
s = s.replace("$CALLSIBEGIN", "")
s = s.replace("$CALLSIRET", "")
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index a18ec4d6ad..388368d181 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -69,7 +69,6 @@
#include "core/math/triangle_mesh.h"
#include "core/multiplayer/multiplayer_api.h"
#include "core/multiplayer/multiplayer_peer.h"
-#include "core/multiplayer/multiplayer_replicator.h"
#include "core/object/class_db.h"
#include "core/object/undo_redo.h"
#include "core/os/main_loop.h"
@@ -200,7 +199,6 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer);
GDREGISTER_CLASS(MultiplayerPeerExtension);
- GDREGISTER_VIRTUAL_CLASS(MultiplayerReplicator);
GDREGISTER_CLASS(MultiplayerAPI);
GDREGISTER_CLASS(MainLoop);
GDREGISTER_CLASS(Translation);
diff --git a/core/string/char_utils.h b/core/string/char_utils.h
new file mode 100644
index 0000000000..0afd058f01
--- /dev/null
+++ b/core/string/char_utils.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* char_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CHAR_UTILS_H
+#define CHAR_UTILS_H
+
+#include "core/typedefs.h"
+
+static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) {
+ return (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) {
+ return (c >= 'a' && c <= 'z');
+}
+
+static _FORCE_INLINE_ bool is_digit(char32_t c) {
+ return (c >= '0' && c <= '9');
+}
+
+static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
+ return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+static _FORCE_INLINE_ bool is_binary_digit(char32_t c) {
+ return (c == '0' || c == '1');
+}
+
+static _FORCE_INLINE_ bool is_ascii_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
+}
+
+static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+static _FORCE_INLINE_ bool is_symbol(char32_t c) {
+ return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
+}
+
+static _FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f);
+}
+
+static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+static _FORCE_INLINE_ bool is_punct(char32_t p_char) {
+ return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f);
+}
+
+static _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+ return (p_char == '_');
+}
+
+#endif // CHAR_UTILS_H
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 61742ac582..11674629fc 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -84,12 +84,15 @@ void StringName::cleanup() {
for (int i = 0; i < STRING_TABLE_LEN; i++) {
while (_table[i]) {
_Data *d = _table[i];
- lost_strings++;
- if (d->static_count.get() != d->refcount.get() && OS::get_singleton()->is_stdout_verbose()) {
- if (d->cname) {
- print_line("Orphan StringName: " + String(d->cname));
- } else {
- print_line("Orphan StringName: " + String(d->name));
+ if (d->static_count.get() != d->refcount.get()) {
+ lost_strings++;
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ if (d->cname) {
+ print_line("Orphan StringName: " + String(d->cname));
+ } else {
+ print_line("Orphan StringName: " + String(d->name));
+ }
}
}
diff --git a/core/string/string_name.h b/core/string/string_name.h
index 9653d2b4cf..f767f3e1ec 100644
--- a/core/string/string_name.h
+++ b/core/string/string_name.h
@@ -181,6 +181,18 @@ bool operator!=(const char *p_name, const StringName &p_string_name);
StringName _scs_create(const char *p_chr, bool p_static = false);
+/*
+ * The SNAME macro is used to speed up StringName creation, as it allows caching it after the first usage in a very efficient way.
+ * It should NOT be used everywhere, but instead in places where high performance is required and the creation of a StringName
+ * can be costly. Places where it should be used are:
+ * - Control::get_theme_*(<name> and Window::get_theme_*(<name> functions.
+ * - emit_signal(<name>,..) function
+ * - call_deferred(<name>,..) function
+ * - Comparisons to a StringName in overriden _set and _get methods.
+ *
+ * Use in places that can be called hundreds of times per frame (or more) is recommended, but this situation is very rare. If in doubt, do not use.
+ */
+
#define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })()
#endif // STRING_NAME_H
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 355ee238e8..eeac8b0acf 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -213,14 +213,6 @@ static _character_accent_pair _character_to_accented[] = {
{ 'z', U"ź" },
};
-static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
-}
-
-static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
-}
-
Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
Map<String, String> TranslationServer::language_map;
@@ -309,15 +301,15 @@ String TranslationServer::standardize_locale(const String &p_locale) const {
Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
lang = locale_elements[0];
if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
script = locale_elements[1];
}
- if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
country = locale_elements[1];
}
}
if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
country = locale_elements[2];
} else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) {
variant = locale_elements[2].to_lower();
@@ -434,15 +426,15 @@ String TranslationServer::get_locale_name(const String &p_locale) const {
Vector<String> locale_elements = locale.split("_");
lang = locale_elements[0];
if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
script = locale_elements[1];
}
- if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
country = locale_elements[1];
}
}
if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
country = locale_elements[2];
}
}
@@ -911,7 +903,7 @@ String TranslationServer::add_padding(String &p_message, int p_length) const {
}
const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) {
+ if (!is_ascii_char(p_character)) {
return nullptr;
}
@@ -933,6 +925,7 @@ bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
void TranslationServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
+ ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale);
ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 6e0a7c7022..c4edc8c086 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -54,34 +54,14 @@
static const int MAX_DECIMALS = 32;
-static _FORCE_INLINE_ bool is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
- return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
-}
-
-static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
-}
-
static _FORCE_INLINE_ char32_t lower_case(char32_t c) {
- return (is_upper_case(c) ? (c + ('a' - 'A')) : c);
+ return (is_ascii_upper_case(c) ? (c + ('a' - 'A')) : c);
}
const char CharString::_null = 0;
const char16_t Char16String::_null = 0;
const char32_t String::_null = 0;
-bool is_symbol(char32_t c) {
- return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
-}
-
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) {
const String &s = p_s;
int beg = CLAMP(p_col, 0, s.length());
@@ -974,21 +954,21 @@ String String::camelcase_to_underscore(bool lowercase) const {
int start_index = 0;
for (int i = 1; i < this->size(); i++) {
- bool is_upper = is_upper_case(cstr[i]);
+ bool is_upper = is_ascii_upper_case(cstr[i]);
bool is_number = is_digit(cstr[i]);
bool are_next_2_lower = false;
bool is_next_lower = false;
bool is_next_number = false;
- bool was_precedent_upper = is_upper_case(cstr[i - 1]);
+ bool was_precedent_upper = is_ascii_upper_case(cstr[i - 1]);
bool was_precedent_number = is_digit(cstr[i - 1]);
if (i + 2 < this->size()) {
- are_next_2_lower = is_lower_case(cstr[i + 1]) && is_lower_case(cstr[i + 2]);
+ are_next_2_lower = is_ascii_lower_case(cstr[i + 1]) && is_ascii_lower_case(cstr[i + 2]);
}
if (i + 1 < this->size()) {
- is_next_lower = is_lower_case(cstr[i + 1]);
+ is_next_lower = is_ascii_lower_case(cstr[i + 1]);
is_next_number = is_digit(cstr[i + 1]);
}
@@ -2212,7 +2192,7 @@ bool String::is_numeric() const {
return false;
}
dot = true;
- } else if (c < '0' || c > '9') {
+ } else if (!is_digit(c)) {
return false;
}
}
@@ -3691,7 +3671,7 @@ bool String::is_valid_identifier() const {
}
}
- bool valid_char = is_digit(str[i]) || is_lower_case(str[i]) || is_upper_case(str[i]) || str[i] == '_';
+ bool valid_char = is_ascii_identifier_char(str[i]);
if (!valid_char) {
return false;
@@ -3716,7 +3696,7 @@ String String::uri_encode() const {
String res;
for (int i = 0; i < temp.length(); ++i) {
char ord = temp[i];
- if (ord == '.' || ord == '-' || ord == '_' || ord == '~' || is_lower_case(ord) || is_upper_case(ord) || is_digit(ord)) {
+ if (ord == '.' || ord == '-' || ord == '~' || is_ascii_identifier_char(ord)) {
res += ord;
} else {
char h_Val[3];
@@ -3738,9 +3718,9 @@ String String::uri_decode() const {
for (int i = 0; i < src.length(); ++i) {
if (src[i] == '%' && i + 2 < src.length()) {
char ord1 = src[i + 1];
- if (is_digit(ord1) || is_upper_case(ord1)) {
+ if (is_digit(ord1) || is_ascii_upper_case(ord1)) {
char ord2 = src[i + 2];
- if (is_digit(ord2) || is_upper_case(ord2)) {
+ if (is_digit(ord2) || is_ascii_upper_case(ord2)) {
char bytes[3] = { (char)ord1, (char)ord2, 0 };
res += (char)strtol(bytes, nullptr, 16);
i += 2;
@@ -3867,7 +3847,7 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch
for (int i = 2; i < p_src_len; i++) {
eat = i + 1;
char32_t ct = p_src[i];
- if (ct == ';' || ct < '0' || ct > '9') {
+ if (ct == ';' || !is_digit(ct)) {
break;
}
}
@@ -3997,7 +3977,7 @@ String String::pad_zeros(int p_digits) const {
int begin = 0;
- while (begin < end && (s[begin] < '0' || s[begin] > '9')) {
+ while (begin < end && !is_digit(s[begin])) {
begin++;
}
@@ -4042,7 +4022,7 @@ bool String::is_valid_int() const {
}
for (int i = from; i < len; i++) {
- if (operator[](i) < '0' || operator[](i) > '9') {
+ if (!is_digit(operator[](i))) {
return false; // no start with number plz
}
}
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 6df87280a4..1d302b65a7 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -32,6 +32,7 @@
#define USTRING_GODOT_H
// Note: Renamed to avoid conflict with ICU header with the same name.
+#include "core/string/char_utils.h"
#include "core/templates/cowdata.h"
#include "core/templates/vector.h"
#include "core/typedefs.h"
@@ -394,6 +395,8 @@ public:
Vector<uint8_t> sha256_buffer() const;
_FORCE_INLINE_ bool is_empty() const { return length() == 0; }
+ _FORCE_INLINE_ bool contains(const char *p_str) const { return find(p_str) != -1; }
+ _FORCE_INLINE_ bool contains(const String &p_str) const { return find(p_str) != -1; }
// path functions
bool is_absolute_path() const;
@@ -531,7 +534,6 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St
String RTR(const String &p_text, const String &p_context = "");
String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "");
-bool is_symbol(char32_t c);
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
_FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) {
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index 3ed81e76fd..95632cdec2 100644
--- a/core/templates/rid_owner.h
+++ b/core/templates/rid_owner.h
@@ -292,43 +292,32 @@ public:
_FORCE_INLINE_ uint32_t get_rid_count() const {
return alloc_count;
}
-
- _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
- ERR_FAIL_UNSIGNED_INDEX_V(p_index, alloc_count, nullptr);
+ void get_owned_list(List<RID> *p_owned) {
if (THREAD_SAFE) {
spin_lock.lock();
}
- uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
- T *ptr = &chunks[idx / elements_in_chunk][idx % elements_in_chunk];
- if (THREAD_SAFE) {
- spin_lock.unlock();
- }
- return ptr;
- }
-
- _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
- ERR_FAIL_INDEX_V(p_index, alloc_count, RID());
- if (THREAD_SAFE) {
- spin_lock.lock();
+ for (size_t i = 0; i < max_alloc; i++) {
+ uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator != 0xFFFFFFFF) {
+ p_owned->push_back(_make_from_id((validator << 32) | i));
+ }
}
- uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
- uint64_t validator = validator_chunks[idx / elements_in_chunk][idx % elements_in_chunk];
-
- RID rid = _make_from_id((validator << 32) | idx);
if (THREAD_SAFE) {
spin_lock.unlock();
}
- return rid;
}
- void get_owned_list(List<RID> *p_owned) {
+ //used for fast iteration in the elements or RIDs
+ void fill_owned_buffer(RID *p_rid_buffer) {
if (THREAD_SAFE) {
spin_lock.lock();
}
+ uint32_t idx = 0;
for (size_t i = 0; i < max_alloc; i++) {
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
if (validator != 0xFFFFFFFF) {
- p_owned->push_back(_make_from_id((validator << 32) | i));
+ p_rid_buffer[idx] = _make_from_id((validator << 32) | i);
+ idx++;
}
}
if (THREAD_SAFE) {
@@ -425,18 +414,14 @@ public:
return alloc.get_rid_count();
}
- _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
- return alloc.get_rid_by_index(p_index);
- }
-
- _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
- return *alloc.get_ptr_by_index(p_index);
- }
-
_FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
return alloc.get_owned_list(p_owned);
}
+ void fill_owned_buffer(RID *p_rid_buffer) {
+ alloc.fill_owned_buffer(p_rid_buffer);
+ }
+
void set_description(const char *p_descrption) {
alloc.set_description(p_descrption);
}
@@ -485,17 +470,12 @@ public:
return alloc.get_rid_count();
}
- _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
- return alloc.get_rid_by_index(p_index);
- }
-
- _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
- return alloc.get_ptr_by_index(p_index);
- }
-
_FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
return alloc.get_owned_list(p_owned);
}
+ void fill_owned_buffer(RID *p_rid_buffer) {
+ alloc.fill_owned_buffer(p_rid_buffer);
+ }
void set_description(const char *p_descrption) {
alloc.set_description(p_descrption);
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index c6a67f01d5..27792ce111 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -114,8 +114,9 @@ ObjectID Callable::get_object_id() const {
}
StringName Callable::get_method() const {
- ERR_FAIL_COND_V_MSG(is_custom(), StringName(),
- vformat("Can't get method on CallableCustom \"%s\".", operator String()));
+ if (is_custom()) {
+ return get_custom()->get_method();
+ }
return method;
}
@@ -310,6 +311,10 @@ Callable::~Callable() {
}
}
+StringName CallableCustom::get_method() const {
+ ERR_FAIL_V_MSG(StringName(), vformat("Can't get method on CallableCustom \"%s\".", get_as_text()));
+}
+
void CallableCustom::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_call_error.argument = 0;
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 855ffa9129..c61870f194 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -125,6 +125,7 @@ public:
virtual String get_as_text() const = 0;
virtual CompareEqualFunc get_compare_equal_func() const = 0;
virtual CompareLessFunc get_compare_less_func() const = 0;
+ virtual StringName get_method() const;
virtual ObjectID get_object() const = 0; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0;
virtual void rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const;
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 4579621760..797e8afede 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -70,12 +70,19 @@ bool CallableCustomBind::_less_func(const CallableCustom *p_a, const CallableCus
CallableCustom::CompareEqualFunc CallableCustomBind::get_compare_equal_func() const {
return _equal_func;
}
+
CallableCustom::CompareLessFunc CallableCustomBind::get_compare_less_func() const {
return _less_func;
}
+
+StringName CallableCustomBind::get_method() const {
+ return callable.get_method();
+}
+
ObjectID CallableCustomBind::get_object() const {
return callable.get_object_id();
}
+
const Callable *CallableCustomBind::get_base_comparator() const {
return &callable;
}
@@ -140,12 +147,19 @@ bool CallableCustomUnbind::_less_func(const CallableCustom *p_a, const CallableC
CallableCustom::CompareEqualFunc CallableCustomUnbind::get_compare_equal_func() const {
return _equal_func;
}
+
CallableCustom::CompareLessFunc CallableCustomUnbind::get_compare_less_func() const {
return _less_func;
}
+
+StringName CallableCustomUnbind::get_method() const {
+ return callable.get_method();
+}
+
ObjectID CallableCustomUnbind::get_object() const {
return callable.get_object_id();
}
+
const Callable *CallableCustomUnbind::get_base_comparator() const {
return &callable;
}
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index ac5797e05f..4f79a29629 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -47,6 +47,7 @@ public:
virtual String get_as_text() const;
virtual CompareEqualFunc get_compare_equal_func() const;
virtual CompareLessFunc get_compare_less_func() const;
+ virtual StringName get_method() const;
virtual ObjectID get_object() const; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;
@@ -71,6 +72,7 @@ public:
virtual String get_as_text() const;
virtual CompareEqualFunc get_compare_equal_func() const;
virtual CompareLessFunc get_compare_less_func() const;
+ virtual StringName get_method() const;
virtual ObjectID get_object() const; //must always be able to provide an object
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;
diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h
index 75a93ac4c8..d0acf60c22 100644
--- a/core/variant/method_ptrcall.h
+++ b/core/variant/method_ptrcall.h
@@ -31,7 +31,6 @@
#ifndef METHOD_PTRCALL_H
#define METHOD_PTRCALL_H
-#include "core/math/transform_2d.h"
#include "core/object/object_id.h"
#include "core/typedefs.h"
#include "core/variant/variant.h"
diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h
index fe541c8d4b..8e9fbbc0a4 100644
--- a/core/variant/native_ptr.h
+++ b/core/variant/native_ptr.h
@@ -53,22 +53,36 @@ struct GDNativePtr {
operator Variant() const { return uint64_t(data); }
};
-#define GDVIRTUAL_NATIVE_PTR(m_type) \
- template <> \
- struct GDNativeConstPtr<const m_type> { \
- const m_type *data = nullptr; \
- GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \
- static const char *get_name() { return "const " #m_type; } \
- operator const m_type *() const { return data; } \
- operator Variant() const { return uint64_t(data); } \
- }; \
- template <> \
- struct GDNativePtr<m_type> { \
- m_type *data = nullptr; \
- GDNativePtr(m_type *p_assign) { data = p_assign; } \
- static const char *get_name() { return #m_type; } \
- operator m_type *() const { return data; } \
- operator Variant() const { return uint64_t(data); } \
+#define GDVIRTUAL_NATIVE_PTR(m_type) \
+ template <> \
+ struct GDNativeConstPtr<const m_type> { \
+ const m_type *data = nullptr; \
+ GDNativeConstPtr() {} \
+ GDNativeConstPtr(const m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return "const " #m_type; } \
+ operator const m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ }; \
+ template <> \
+ struct VariantCaster<GDNativeConstPtr<const m_type>> { \
+ static _FORCE_INLINE_ GDNativeConstPtr<const m_type> cast(const Variant &p_variant) { \
+ return GDNativeConstPtr<const m_type>((const m_type *)p_variant.operator uint64_t()); \
+ } \
+ }; \
+ template <> \
+ struct GDNativePtr<m_type> { \
+ m_type *data = nullptr; \
+ GDNativePtr() {} \
+ GDNativePtr(m_type *p_assign) { data = p_assign; } \
+ static const char *get_name() { return #m_type; } \
+ operator m_type *() const { return data; } \
+ operator Variant() const { return uint64_t(data); } \
+ }; \
+ template <> \
+ struct VariantCaster<GDNativePtr<m_type>> { \
+ static _FORCE_INLINE_ GDNativePtr<m_type> cast(const Variant &p_variant) { \
+ return GDNativePtr<m_type>((m_type *)p_variant.operator uint64_t()); \
+ } \
};
template <class T>
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 36fa755647..b75882a87c 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -39,8 +39,12 @@
#include "core/math/face3.h"
#include "core/math/plane.h"
#include "core/math/quaternion.h"
+#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
#include "core/math/transform_2d.h"
#include "core/math/transform_3d.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "core/math/vector3.h"
#include "core/math/vector3i.h"
#include "core/object/object_id.h"
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index aecc6e9a26..a5e89eec80 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1371,6 +1371,8 @@ static void _register_variant_builtin_methods() {
bind_method(String, length, sarray(), varray());
bind_method(String, substr, sarray("from", "len"), varray(-1));
bind_method(String, get_slice, sarray("delimiter", "slice"), varray());
+ bind_method(String, get_slicec, sarray("delimiter", "slice"), varray());
+ bind_method(String, get_slice_count, sarray("delimiter"), varray());
bind_methodv(String, find, static_cast<int (String::*)(const String &, int) const>(&String::find), sarray("what", "from"), varray(0));
bind_method(String, count, sarray("what", "from", "to"), varray(0, 0));
bind_method(String, countn, sarray("what", "from", "to"), varray(0, 0));
@@ -1421,6 +1423,7 @@ static void _register_variant_builtin_methods() {
bind_method(String, sha1_buffer, sarray(), varray());
bind_method(String, sha256_buffer, sarray(), varray());
bind_method(String, is_empty, sarray(), varray());
+ bind_methodv(String, contains, static_cast<bool (String::*)(const String &) const>(&String::contains), sarray("what"), varray());
bind_method(String, is_absolute_path, sarray(), varray());
bind_method(String, is_relative_path, sarray(), varray());
@@ -1666,6 +1669,7 @@ static void _register_variant_builtin_methods() {
/* RID */
+ bind_method(RID, is_valid, sarray(), varray());
bind_method(RID, get_id, sarray(), varray());
/* NodePath */
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index e0ffcc9d11..cd1ae9f41f 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -45,6 +45,126 @@ void register_op(Variant::Operator p_op, Variant::Type p_type_a, Variant::Type p
ptr_operator_evaluator_table[p_op][p_type_a][p_type_b] = T::ptr_evaluate;
}
+// Special cases that can't be done otherwise because of the forced casting to float.
+
+template <>
+class OperatorEvaluatorMul<Vector2, Vector2i, double> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+ *r_ret = Vector2(a.x, a.y) * b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(left)->x, VariantGetInternalPtr<Vector2i>::get_ptr(left)->y) * *VariantGetInternalPtr<double>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(left).x, PtrToArg<Vector2i>::convert(left).y) * PtrToArg<double>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorMul<Vector2, double, Vector2i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_right);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_left);
+ *r_ret = Vector2(a.x, a.y) * b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(right)->x, VariantGetInternalPtr<Vector2i>::get_ptr(right)->y) * *VariantGetInternalPtr<double>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(right).x, PtrToArg<Vector2i>::convert(right).y) * PtrToArg<double>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorDivNZ<Vector2, Vector2i, double> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector2i &a = *VariantGetInternalPtr<Vector2i>::get_ptr(&p_left);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+ if (unlikely(b == 0)) {
+ r_valid = false;
+ *r_ret = "Division by zero error";
+ return;
+ }
+ *r_ret = Vector2(a.x, a.y) / b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector2>::get_ptr(r_ret) = Vector2(VariantGetInternalPtr<Vector2i>::get_ptr(left)->x, VariantGetInternalPtr<Vector2i>::get_ptr(left)->y) / *VariantGetInternalPtr<double>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector2>::encode(Vector2(PtrToArg<Vector2i>::convert(left).x, PtrToArg<Vector2i>::convert(left).y) / PtrToArg<double>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector2>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorMul<Vector3, Vector3i, double> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+ *r_ret = Vector3(a.x, a.y, a.z) * b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(left)->x, VariantGetInternalPtr<Vector3i>::get_ptr(left)->y, VariantGetInternalPtr<Vector3i>::get_ptr(left)->z) * *VariantGetInternalPtr<double>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(left).x, PtrToArg<Vector3i>::convert(left).y, PtrToArg<Vector3i>::convert(left).z) * PtrToArg<double>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorMul<Vector3, double, Vector3i> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_right);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_left);
+ *r_ret = Vector3(a.x, a.y, a.z) * b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(right)->x, VariantGetInternalPtr<Vector3i>::get_ptr(right)->y, VariantGetInternalPtr<Vector3i>::get_ptr(right)->z) * *VariantGetInternalPtr<double>::get_ptr(left);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(right).x, PtrToArg<Vector3i>::convert(right).y, PtrToArg<Vector3i>::convert(right).z) * PtrToArg<double>::convert(left), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; }
+};
+
+template <>
+class OperatorEvaluatorDivNZ<Vector3, Vector3i, double> {
+public:
+ static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
+ const Vector3i &a = *VariantGetInternalPtr<Vector3i>::get_ptr(&p_left);
+ const double &b = *VariantGetInternalPtr<double>::get_ptr(&p_right);
+ if (unlikely(b == 0)) {
+ r_valid = false;
+ *r_ret = "Division by zero error";
+ return;
+ }
+ *r_ret = Vector3(a.x, a.y, a.z) / b;
+ r_valid = true;
+ }
+ static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
+ *VariantGetInternalPtr<Vector3>::get_ptr(r_ret) = Vector3(VariantGetInternalPtr<Vector3i>::get_ptr(left)->x, VariantGetInternalPtr<Vector3i>::get_ptr(left)->y, VariantGetInternalPtr<Vector3i>::get_ptr(left)->z) / *VariantGetInternalPtr<double>::get_ptr(right);
+ }
+ static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
+ PtrToArg<Vector3>::encode(Vector3(PtrToArg<Vector3i>::convert(left).x, PtrToArg<Vector3i>::convert(left).y, PtrToArg<Vector3i>::convert(left).z) / PtrToArg<double>::convert(right), r_ret);
+ }
+ static Variant::Type get_return_type() { return GetTypeInfo<Vector3>::VARIANT_TYPE; }
+};
+
void Variant::_register_variant_operators() {
memset(operator_return_type_table, 0, sizeof(operator_return_type_table));
memset(operator_evaluator_table, 0, sizeof(operator_evaluator_table));
@@ -56,6 +176,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorAdd<double, double, int64_t>>(Variant::OP_ADD, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorAdd<double, double, double>>(Variant::OP_ADD, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorAdd<String, String, String>>(Variant::OP_ADD, Variant::STRING, Variant::STRING);
+ register_op<OperatorEvaluatorAdd<String, char32_t, String>>(Variant::OP_ADD, Variant::INT, Variant::STRING);
register_op<OperatorEvaluatorAdd<Vector2, Vector2, Vector2>>(Variant::OP_ADD, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorAdd<Vector2i, Vector2i, Vector2i>>(Variant::OP_ADD, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorAdd<Vector3, Vector3, Vector3>>(Variant::OP_ADD, Variant::VECTOR3, Variant::VECTOR3);
@@ -94,9 +215,9 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorMul<double, double, double>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorMul<double, double, int64_t>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorMul<Vector2, double, Vector2>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2);
- register_op<OperatorEvaluatorMul<Vector2i, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I);
+ register_op<OperatorEvaluatorMul<Vector2, double, Vector2i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR2I);
register_op<OperatorEvaluatorMul<Vector3, double, Vector3>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3);
- register_op<OperatorEvaluatorMul<Vector3i, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I);
+ register_op<OperatorEvaluatorMul<Vector3, double, Vector3i>>(Variant::OP_MULTIPLY, Variant::FLOAT, Variant::VECTOR3I);
register_op<OperatorEvaluatorMul<Vector2, Vector2, Vector2>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorMul<Vector2, Vector2, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2, Variant::INT);
@@ -104,7 +225,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorMul<Vector2i, Vector2i, Vector2i>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorMul<Vector2i, Vector2i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::INT);
- register_op<OperatorEvaluatorMul<Vector2i, Vector2i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::FLOAT);
+ register_op<OperatorEvaluatorMul<Vector2, Vector2i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR2I, Variant::FLOAT);
register_op<OperatorEvaluatorMul<Vector3, Vector3, Vector3>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::VECTOR3);
register_op<OperatorEvaluatorMul<Vector3, Vector3, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3, Variant::INT);
@@ -112,7 +233,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorMul<Vector3i, Vector3i, Vector3i>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::VECTOR3I);
register_op<OperatorEvaluatorMul<Vector3i, Vector3i, int64_t>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::INT);
- register_op<OperatorEvaluatorMul<Vector3i, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT);
+ register_op<OperatorEvaluatorMul<Vector3, Vector3i, double>>(Variant::OP_MULTIPLY, Variant::VECTOR3I, Variant::FLOAT);
register_op<OperatorEvaluatorMul<Quaternion, Quaternion, Quaternion>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::QUATERNION);
register_op<OperatorEvaluatorMul<Quaternion, Quaternion, int64_t>>(Variant::OP_MULTIPLY, Variant::QUATERNION, Variant::INT);
@@ -172,7 +293,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT);
register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, Vector2i>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::VECTOR2I);
- register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT);
+ register_op<OperatorEvaluatorDivNZ<Vector2, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT);
register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::INT);
register_op<OperatorEvaluatorDiv<Vector2, Vector2, Vector2>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::VECTOR2);
@@ -184,7 +305,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorDiv<Vector3, Vector3, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::INT);
register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, Vector3i>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::VECTOR3I);
- register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT);
+ register_op<OperatorEvaluatorDivNZ<Vector3, Vector3i, double>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::FLOAT);
register_op<OperatorEvaluatorDivNZ<Vector3i, Vector3i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3I, Variant::INT);
register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT);
@@ -502,6 +623,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorLess<double, int64_t>>(Variant::OP_LESS, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorLess<double, double>>(Variant::OP_LESS, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorLess<String, String>>(Variant::OP_LESS, Variant::STRING, Variant::STRING);
+ register_op<OperatorEvaluatorLess<StringName, StringName>>(Variant::OP_LESS, Variant::STRING_NAME, Variant::STRING_NAME);
register_op<OperatorEvaluatorLess<Vector2, Vector2>>(Variant::OP_LESS, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorLess<Vector2i, Vector2i>>(Variant::OP_LESS, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorLess<Vector3, Vector3>>(Variant::OP_LESS, Variant::VECTOR3, Variant::VECTOR3);
@@ -514,6 +636,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorLessEqual<double, int64_t>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorLessEqual<double, double>>(Variant::OP_LESS_EQUAL, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorLessEqual<String, String>>(Variant::OP_LESS_EQUAL, Variant::STRING, Variant::STRING);
+ register_op<OperatorEvaluatorLessEqual<StringName, StringName>>(Variant::OP_LESS_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME);
register_op<OperatorEvaluatorLessEqual<Vector2, Vector2>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorLessEqual<Vector2i, Vector2i>>(Variant::OP_LESS_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorLessEqual<Vector3, Vector3>>(Variant::OP_LESS_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
@@ -527,6 +650,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorGreater<double, int64_t>>(Variant::OP_GREATER, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorGreater<double, double>>(Variant::OP_GREATER, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorGreater<String, String>>(Variant::OP_GREATER, Variant::STRING, Variant::STRING);
+ register_op<OperatorEvaluatorGreater<StringName, StringName>>(Variant::OP_GREATER, Variant::STRING_NAME, Variant::STRING_NAME);
register_op<OperatorEvaluatorGreater<Vector2, Vector2>>(Variant::OP_GREATER, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorGreater<Vector2i, Vector2i>>(Variant::OP_GREATER, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorGreater<Vector3, Vector3>>(Variant::OP_GREATER, Variant::VECTOR3, Variant::VECTOR3);
@@ -539,6 +663,7 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorGreaterEqual<double, int64_t>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::INT);
register_op<OperatorEvaluatorGreaterEqual<double, double>>(Variant::OP_GREATER_EQUAL, Variant::FLOAT, Variant::FLOAT);
register_op<OperatorEvaluatorGreaterEqual<String, String>>(Variant::OP_GREATER_EQUAL, Variant::STRING, Variant::STRING);
+ register_op<OperatorEvaluatorGreaterEqual<StringName, StringName>>(Variant::OP_GREATER_EQUAL, Variant::STRING_NAME, Variant::STRING_NAME);
register_op<OperatorEvaluatorGreaterEqual<Vector2, Vector2>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2, Variant::VECTOR2);
register_op<OperatorEvaluatorGreaterEqual<Vector2i, Vector2i>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR2I, Variant::VECTOR2I);
register_op<OperatorEvaluatorGreaterEqual<Vector3, Vector3>>(Variant::OP_GREATER_EQUAL, Variant::VECTOR3, Variant::VECTOR3);
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 96cdc0678e..e889a1bb40 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -188,7 +188,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
if (p_stream->is_eof()) {
r_token.type = TK_EOF;
return OK;
- } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
+ } else if (is_hex_digit(ch)) {
color_str += ch;
} else {
@@ -265,13 +265,13 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
r_err_str = "Malformed hex constant in string";
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -381,7 +381,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
while (true) {
switch (reading) {
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
//pass
} else if (c == '.') {
reading = READING_DEC;
@@ -395,7 +395,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
case READING_DEC: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
} else {
@@ -404,7 +404,7 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
} break;
case READING_EXP: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -433,11 +433,11 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri
r_token.value = num.as_int();
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || is_underscore(cchar)) {
StringBuffer<> id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) {
+ while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) {
id += cchar;
cchar = p_stream->get_char();
first = false;
@@ -1495,7 +1495,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::FLOAT: {
String s = rtos_fix(p_variant.operator double());
if (s != "inf" && s != "inf_neg" && s != "nan") {
- if (s.find(".") == -1 && s.find("e") == -1) {
+ if (!s.contains(".") && !s.contains("e")) {
s += ".0";
}
}
diff --git a/core/version.h b/core/version.h
index c718d0f4d7..e22922fa66 100644
--- a/core/version.h
+++ b/core/version.h
@@ -68,4 +68,7 @@
// Example: "Godot v3.1.4.stable.official.mono"
#define VERSION_FULL_NAME "" VERSION_NAME " v" VERSION_FULL_BUILD
+// Git commit hash, generated at build time in `core/version_hash.gen.cpp`.
+extern const char *const VERSION_HASH;
+
#endif // VERSION_H
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index d8c9ca08e8..4c0f89f14d 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1267,6 +1267,12 @@
<constant name="INLINE_ALIGNMENT_BOTTOM" value="14" enum="InlineAlignment">
Aligns bottom of the inline object (e.g. image, table) to the bottom of the text. Equivalent to [code]INLINE_ALIGNMENT_BOTTOM_TO | INLINE_ALIGNMENT_TO_BOTTOM[/code].
</constant>
+ <constant name="INLINE_ALIGNMENT_IMAGE_MASK" value="3" enum="InlineAlignment">
+ A bit mask for [code]INLINE_ALIGNMENT_*_TO[/code] alignment constants.
+ </constant>
+ <constant name="INLINE_ALIGNMENT_TEXT_MASK" value="12" enum="InlineAlignment">
+ A bit mask for [code]INLINE_ALIGNMENT_TO_*[/code] alignment constants.
+ </constant>
<constant name="KEY_SPECIAL" value="16777216" enum="Key">
Keycodes with this bit applied are non-printable.
</constant>
diff --git a/doc/classes/ArrayOccluder3D.xml b/doc/classes/ArrayOccluder3D.xml
new file mode 100644
index 0000000000..993393cf50
--- /dev/null
+++ b/doc/classes/ArrayOccluder3D.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ArrayOccluder3D" inherits="Occluder3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="set_arrays">
+ <return type="void" />
+ <argument index="0" name="vertices" type="PackedVector3Array" />
+ <argument index="1" name="indices" type="PackedInt32Array" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="indices" type="PackedInt32Array" setter="set_indices" getter="get_indices" default="PackedInt32Array()">
+ </member>
+ <member name="vertices" type="PackedVector3Array" setter="set_vertices" getter="get_vertices" default="PackedVector3Array()">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/AudioStreamRandomPitch.xml b/doc/classes/AudioStreamRandomPitch.xml
deleted file mode 100644
index 0f580699e9..0000000000
--- a/doc/classes/AudioStreamRandomPitch.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AudioStreamRandomPitch" inherits="AudioStream" version="4.0">
- <brief_description>
- Plays audio with random pitch shifting.
- </brief_description>
- <description>
- Randomly varies pitch on each start.
- </description>
- <tutorials>
- </tutorials>
- <members>
- <member name="audio_stream" type="AudioStream" setter="set_audio_stream" getter="get_audio_stream">
- The current [AudioStream].
- </member>
- <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.1">
- The intensity of random pitch variation.
- </member>
- </members>
-</class>
diff --git a/doc/classes/AudioStreamRandomizer.xml b/doc/classes/AudioStreamRandomizer.xml
new file mode 100644
index 0000000000..90471a033e
--- /dev/null
+++ b/doc/classes/AudioStreamRandomizer.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamRandomizer" inherits="AudioStream" version="4.0">
+ <brief_description>
+ Wraps a pool of audio streams with pitch and volume shifting.
+ </brief_description>
+ <description>
+ Picks a random AudioStream from the pool, depending on the playback mode, and applies random pitch shifting and volume shifting during playback.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_stream">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Insert a stream at the specified index.
+ </description>
+ </method>
+ <method name="get_stream" qualifiers="const">
+ <return type="AudioStream" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Returns the stream at the specified index.
+ </description>
+ </method>
+ <method name="get_stream_probability_weight" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Returns the probability weight associated with the stream at the given index.
+ </description>
+ </method>
+ <method name="move_stream">
+ <return type="void" />
+ <argument index="0" name="index_from" type="int" />
+ <argument index="1" name="index_to" type="int" />
+ <description>
+ Move a stream from one index to another.
+ </description>
+ </method>
+ <method name="remove_stream">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Remove the stream at the specified index.
+ </description>
+ </method>
+ <method name="set_stream">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="stream" type="AudioStream" />
+ <description>
+ Set the AudioStream at the specified index.
+ </description>
+ </method>
+ <method name="set_stream_probability_weight">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="weight" type="float" />
+ <description>
+ Set the probability weight of the stream at the specified index. The higher this value, the more likely that the randomizer will choose this stream during random playback modes.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="playback_mode" type="int" setter="set_playback_mode" getter="get_playback_mode" enum="AudioStreamRandomizer.PlaybackMode" default="0">
+ Controls how this AudioStreamRandomizer picks which AudioStream to play next.
+ </member>
+ <member name="random_pitch" type="float" setter="set_random_pitch" getter="get_random_pitch" default="1.1">
+ The intensity of random pitch variation. A value of 1 means no variation.
+ </member>
+ <member name="random_volume_offset_db" type="float" setter="set_random_volume_offset_db" getter="get_random_volume_offset_db" default="5.0">
+ The intensity of random volume variation. A value of 0 means no variation.
+ </member>
+ <member name="streams_count" type="int" setter="set_streams_count" getter="get_streams_count" default="0">
+ The number of streams in the stream pool.
+ </member>
+ </members>
+ <constants>
+ <constant name="PLAYBACK_RANDOM_NO_REPEATS" value="0" enum="PlaybackMode">
+ Pick a stream at random according to the probability weights chosen for each stream, but avoid playing the same stream twice in a row whenever possible.
+ </constant>
+ <constant name="PLAYBACK_RANDOM" value="1" enum="PlaybackMode">
+ Pick a stream at random according to the probability weights chosen for each stream.
+ </constant>
+ <constant name="PLAYBACK_SEQUENTIAL" value="2" enum="PlaybackMode">
+ Play streams in the order they appear in the stream pool.
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/BoxOccluder3D.xml b/doc/classes/BoxOccluder3D.xml
new file mode 100644
index 0000000000..8c3b597193
--- /dev/null
+++ b/doc/classes/BoxOccluder3D.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="BoxOccluder3D" inherits="Occluder3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(1, 1, 1)">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index ef4eba62b2..af42f72c66 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -115,7 +115,7 @@
<theme_item name="font_pressed_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Text [Color] used when the [Button] is being pressed.
</theme_item>
- <theme_item name="icon_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
+ <theme_item name="icon_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.4)">
Icon modulate [Color] used when the [Button] is disabled.
</theme_item>
<theme_item name="icon_focus_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 44845947b1..16aa7309cc 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -334,7 +334,7 @@
<method name="get_global_mouse_position" qualifiers="const">
<return type="Vector2" />
<description>
- Returns the global position of the mouse.
+ Returns the mouse's position in the [CanvasLayer] that this [CanvasItem] is in using the coordinate system of the [CanvasLayer].
</description>
</method>
<method name="get_global_transform" qualifiers="const">
@@ -352,7 +352,7 @@
<method name="get_local_mouse_position" qualifiers="const">
<return type="Vector2" />
<description>
- Returns the mouse position relative to this item's position.
+ Returns the mouse's position in this [CanvasItem] using the local coordinate system of this [CanvasItem].
</description>
</method>
<method name="get_transform" qualifiers="const">
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index 9ee5ce0dcb..614bd558e8 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -44,5 +44,16 @@
<member name="transform" type="Transform2D" setter="set_transform" getter="get_transform" default="Transform2D(1, 0, 0, 1, 0, 0)">
The layer's transform.
</member>
+ <member name="visible" type="bool" setter="set_visible" getter="is_visible" default="true">
+ If [code]false[/code], any [CanvasItem] under this [CanvasLayer] will be hidden.
+ Unlike [member CanvasItem.visible], visibility of a [CanvasLayer] isn't propagated to underlying layers.
+ </member>
</members>
+ <signals>
+ <signal name="visibility_changed">
+ <description>
+ Emitted when visibility of the layer is changed. See [member visible].
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index f3fcd90f51..4e73d4d9d8 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -218,12 +218,45 @@
<return type="Color" />
<argument index="0" name="rgba" type="String" />
<description>
+ Returns a new color from [code]rgba[/code], an HTML hexadecimal color string. [code]rgba[/code] is not case sensitive, and may be prefixed with a '#' character.
+ [code]rgba[/code] must be a valid three-digit or six-digit hexadecimal color string, and may contain an alpha channel value. If [code]rgba[/code] does not contain an alpha channel value, an alpha channel value of 1.0 is applied.
+ If [code]rgba[/code] is invalid a Color(0.0, 0.0, 0.0, 1.0) is returned.
+ [codeblocks]
+ [gdscript]
+ var green = Color.html("#00FF00FF") # set green to Color(0.0, 1.0, 0.0, 1.0)
+ var blue = Color.html("#0000FF") # set blue to Color(0.0, 0.0, 1.0, 1.0)
+ [/gdscript]
+ [csharp]
+ var green = Color.Html("#00FF00FF"); // set green to Color(0.0, 1.0, 0.0, 1.0)
+ var blue = Color.Html("#0000FF"); // set blue to Color(0.0, 0.0, 1.0, 1.0)
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="html_is_valid" qualifiers="static">
<return type="bool" />
<argument index="0" name="color" type="String" />
<description>
+ Returns [code]true[/code] if [code]color[/code] is a valid HTML hexadecimal color string. [code]color[/code] is not case sensitive, and may be prefixed with a '#' character.
+ For a string to be valid it must be three-digit or six-digit hexadecimal, and may contain an alpha channel value.
+ [codeblocks]
+ [gdscript]
+ var result = Color.html_is_valid("#55aaFF") # result is true
+ result = Color.html_is_valid("#55AAFF20") # result is true
+ result = Color.html_is_valid("55AAFF") # result is true
+ result = Color.html_is_valid("#F2C") # result is true
+ result = Color.html_is_valid("#AABBC) # result is false
+ result = Color.html_is_valid("#55aaFF5") # result is false
+ [/gdscript]
+ [csharp]
+ var result = Color.HtmlIsValid("#55AAFF"); // result is true
+ result = Color.HtmlIsValid("#55AAFF20"); // result is true
+ result = Color.HtmlIsValid("55AAFF); // result is true
+ result = Color.HtmlIsValid("#F2C"); // result is true
+ result = Color.HtmlIsValid("#AABBC"); // result is false
+ result = Color.HtmlIsValid("#55aaFF5"); // result is false
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="inverted" qualifiers="const">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 8be944b105..281c218d0d 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -399,6 +399,14 @@
<description>
</description>
</method>
+ <method name="screen_get_refresh_rate" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="screen" type="int" default="-1" />
+ <description>
+ Returns the current refresh rate of the specified screen. If [code]screen[/code] is [code]SCREEN_OF_MAIN_WINDOW[/code] (the default value), a screen with the main window will be used.
+ [b]Note:[/b] Returns [code]60.0[/code] if the DisplayServer fails to find the refresh rate for the specified screen. On HTML5, [method screen_get_refresh_rate] will always return [code]60.0[/code] as there is no way to retrieve the refresh rate on that platform.
+ </description>
+ </method>
<method name="screen_get_scale" qualifiers="const">
<return type="float" />
<argument index="0" name="screen" type="int" default="-1" />
@@ -900,6 +908,11 @@
Fullscreen window mode. Note that this is not [i]exclusive[/i] fullscreen. On Windows and Linux, a borderless window is used to emulate fullscreen. On macOS, a new desktop is used to display the running project.
Regardless of the platform, enabling fullscreen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling fullscreen mode.
</constant>
+ <constant name="WINDOW_MODE_EXCLUSIVE_FULLSCREEN" value="4" enum="WindowMode">
+ Exclusive fullscreen window mode. This mode is implemented on Windows only. On other platforms, it is equivalent to [constant WINDOW_MODE_FULLSCREEN].
+ Only one window in exclusive fullscreen mode can be visible on a given screen at a time. If multiple windows are in exclusive fullscreen mode for the same screen, the last one being set to this mode takes precedence.
+ Regardless of the platform, enabling fullscreen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling fullscreen mode.
+ </constant>
<constant name="WINDOW_FLAG_RESIZE_DISABLED" value="0" enum="WindowFlags">
</constant>
<constant name="WINDOW_FLAG_BORDERLESS" value="1" enum="WindowFlags">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index cc1243898f..b9267bc577 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -379,8 +379,10 @@
<method name="add_import_plugin">
<return type="void" />
<argument index="0" name="importer" type="EditorImportPlugin" />
+ <argument index="1" name="first_priority" type="bool" default="false" />
<description>
Registers a new [EditorImportPlugin]. Import plugins are used to import custom and unsupported assets as a custom [Resource] type.
+ If [code]first_priority[/code] is [code]true[/code], the new import plugin is inserted first in the list and takes precedence over pre-existing plugins.
[b]Note:[/b] If you want to import custom 3D asset formats use [method add_scene_format_importer_plugin] instead.
See [method add_inspector_plugin] for an example of how to register a plugin.
</description>
@@ -408,15 +410,19 @@
<method name="add_scene_format_importer_plugin">
<return type="void" />
<argument index="0" name="scene_format_importer" type="EditorSceneFormatImporter" />
+ <argument index="1" name="first_priority" type="bool" default="false" />
<description>
Registers a new [EditorSceneFormatImporter]. Scene importers are used to import custom 3D asset formats as scenes.
+ If [code]first_priority[/code] is [code]true[/code], the new import plugin is inserted first in the list and takes precedence over pre-existing plugins.
</description>
</method>
<method name="add_scene_post_import_plugin">
<return type="void" />
<argument index="0" name="scene_import_plugin" type="EditorScenePostImportPlugin" />
+ <argument index="1" name="first_priority" type="bool" default="false" />
<description>
Add a [EditorScenePostImportPlugin]. These plugins allow customizing the import process of 3D assets by adding new options to the import dialogs.
+ If [code]first_priority[/code] is [code]true[/code], the new import plugin is inserted first in the list and takes precedence over pre-existing plugins.
</description>
</method>
<method name="add_spatial_gizmo_plugin">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 60d4b664d2..2f4a0079c9 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -88,6 +88,15 @@
<description>
</description>
</method>
+ <method name="compute_image_metrics">
+ <return type="Dictionary" />
+ <argument index="0" name="compared_image" type="Image" />
+ <argument index="1" name="use_luma" type="bool" />
+ <description>
+ Compute image metrics on the current image and the compared image.
+ The dictionary contains [code]max[/code], [code]mean[/code], [code]mean_squared[/code], [code]root_mean_squared[/code] and [code]peak_snr[/code].
+ </description>
+ </method>
<method name="convert">
<return type="void" />
<argument index="0" name="format" type="int" enum="Image.Format" />
diff --git a/doc/classes/InputEventMouse.xml b/doc/classes/InputEventMouse.xml
index 5215c29b4a..054b3dbb33 100644
--- a/doc/classes/InputEventMouse.xml
+++ b/doc/classes/InputEventMouse.xml
@@ -14,10 +14,12 @@
The mouse button mask identifier, one of or a bitwise combination of the [enum MouseButton] button masks.
</member>
<member name="global_position" type="Vector2" setter="set_global_position" getter="get_global_position" default="Vector2(0, 0)">
- The global mouse position relative to the current [Viewport]. If used in [method Control._gui_input] and if the current [Control] is not under the mouse, moving it will not update this value.
+ When received in [method Node._input] or [method Node._unhandled_input], returns the mouse's position in the root [Viewport] using the coordinate system of the root [Viewport].
+ When received in [method Control._gui_input], returns the mouse's position in the [CanvasLayer] that the [Control] is in using the coordinate system of the [CanvasLayer].
</member>
<member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)">
- The local mouse position relative to the [Viewport]. If used in [method Control._gui_input], the position is relative to the current [Control] which is under the mouse. If the current [Control] is not under the mouse, moving it will not update this value.
+ When received in [method Node._input] or [method Node._unhandled_input], returns the mouse's position in the [Viewport] this [Node] is in using the coordinate system of this [Viewport].
+ When received in [method Control._gui_input], returns the mouse's position in the [Control] using the local coordinate system of the [Control].
</member>
</members>
</class>
diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml
index aeaf8ac1f5..4ea60ad867 100644
--- a/doc/classes/JavaScript.xml
+++ b/doc/classes/JavaScript.xml
@@ -53,5 +53,27 @@
Returns an interface to a JavaScript object that can be used by scripts. The [code]interface[/code] must be a valid property of the JavaScript [code]window[/code]. The callback must accept a single [Array] argument, which will contain the JavaScript [code]arguments[/code]. See [JavaScriptObject] for usage.
</description>
</method>
+ <method name="pwa_needs_update" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if a new version of the progressive web app is waiting to be activated.
+ [b]Note:[/b] Only relevant when exported as a Progressive Web App.
+ </description>
+ </method>
+ <method name="pwa_update">
+ <return type="int" enum="Error" />
+ <description>
+ Performs the live update of the progressive web app. Forcing the new version to be installed and the page to be reloaded.
+ [b]Note:[/b] Your application will be [b]reloaded in all browser tabs[/b].
+ [b]Note:[/b] Only relevant when exported as a Progressive Web App and [method pwa_needs_update] returns [code]true[/code].
+ </description>
+ </method>
</methods>
+ <signals>
+ <signal name="pwa_update_available">
+ <description>
+ Emitted when an update for this progressive web app has been detected but is waiting to be activated because a previous version is active. See [method pwa_update] to force the update to take place immediately.
+ </description>
+ </signal>
+ </signals>
</class>
diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml
index c774528a39..8fbafcdb51 100644
--- a/doc/classes/Mesh.xml
+++ b/doc/classes/Mesh.xml
@@ -206,7 +206,7 @@
<constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat">
Mesh array uses indices.
</constant>
- <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="2147475463" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat">
</constant>
<constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat">
</constant>
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
index e0da08f5bd..642e000efc 100644
--- a/doc/classes/MultiplayerAPI.xml
+++ b/doc/classes/MultiplayerAPI.xml
@@ -79,10 +79,8 @@
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" default="false">
If [code]true[/code], the MultiplayerAPI's [member multiplayer_peer] refuses new incoming connections.
</member>
- <member name="replicator" type="MultiplayerReplicator" setter="" getter="get_replicator">
- </member>
- <member name="root_node" type="Node" setter="set_root_node" getter="get_root_node">
- The root node to use for RPCs. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
+ <member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;&quot;)">
+ The root path to use for RPCs and replication. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed.
This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene.
</member>
</members>
diff --git a/doc/classes/MultiplayerReplicator.xml b/doc/classes/MultiplayerReplicator.xml
deleted file mode 100644
index c2e93ddeab..0000000000
--- a/doc/classes/MultiplayerReplicator.xml
+++ /dev/null
@@ -1,191 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="MultiplayerReplicator" inherits="Object" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="decode_state">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <argument index="2" name="data" type="PackedByteArray" />
- <argument index="3" name="initial" type="bool" default="true" />
- <description>
- Decode the given [code]data[/code] representing a spawnable state into [code]object[/code] using the configuration associated with the provided [code]scene_id[/code]. This function is called automatically when a client receives a server spawn for a scene with [constant REPLICATION_MODE_SERVER]. See [method spawn_config].
- Tip: You may find this function useful in servers when parsing spawn requests from clients, or when implementing your own logic with [constant REPLICATION_MODE_CUSTOM].
- </description>
- </method>
- <method name="despawn">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <argument index="2" name="peer_id" type="int" default="0" />
- <description>
- Request a despawn for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code]. This will either trigger the default behavior, or invoke the custom spawn/despawn callables specified in [method spawn_config]. See [method send_despawn] for the default behavior.
- </description>
- </method>
- <method name="encode_state">
- <return type="PackedByteArray" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <argument index="2" name="initial" type="bool" default="true" />
- <description>
- Encode the given [code]object[/code] using the configuration associated with the provided [code]scene_id[/code]. This function is called automatically when the server spawns scenes with [constant REPLICATION_MODE_SERVER]. See [method spawn_config].
- Tip: You may find this function useful when requesting spawns from clients to server, or when implementing your own logic with [constant REPLICATION_MODE_CUSTOM].
- </description>
- </method>
- <method name="send_despawn">
- <return type="int" enum="Error" />
- <argument index="0" name="peer_id" type="int" />
- <argument index="1" name="scene_id" type="int" />
- <argument index="2" name="data" type="Variant" default="null" />
- <argument index="3" name="path" type="NodePath" default="NodePath(&quot;&quot;)" />
- <description>
- Sends a despawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_server]), the receiving peer(s) will automatically queue for deletion the node at [code]path[/code] and emit the signal [signal despawned]. In all other cases no deletion happens, and the signal [signal despawn_requested] is emitted instead.
- </description>
- </method>
- <method name="send_spawn">
- <return type="int" enum="Error" />
- <argument index="0" name="peer_id" type="int" />
- <argument index="1" name="scene_id" type="int" />
- <argument index="2" name="data" type="Variant" default="null" />
- <argument index="3" name="path" type="NodePath" default="NodePath(&quot;&quot;)" />
- <description>
- Sends a spawn request for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). If the scene is configured as [constant REPLICATION_MODE_SERVER] (see [method spawn_config]) and the request is sent by the server (see [method MultiplayerAPI.is_server]), the receiving peer(s) will automatically instantiate that scene, add it to the [SceneTree] at the given [code]path[/code] and emit the signal [signal spawned]. In all other cases no instantiation happens, and the signal [signal spawn_requested] is emitted instead.
- </description>
- </method>
- <method name="send_sync">
- <return type="int" enum="Error" />
- <argument index="0" name="peer_id" type="int" />
- <argument index="1" name="scene_id" type="int" />
- <argument index="2" name="data" type="PackedByteArray" />
- <argument index="3" name="transfer_mode" type="int" enum="TransferMode" default="2" />
- <argument index="4" name="channel" type="int" default="0" />
- <description>
- Sends a sync request for the instances of the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code] (see [method MultiplayerPeer.set_target_peer]). This function can only be called manually when overriding the send and receive sync functions (see [method sync_config]).
- </description>
- </method>
- <method name="spawn">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <argument index="2" name="peer_id" type="int" default="0" />
- <description>
- Request a spawn for the scene identified by [code]scene_id[/code] to the given [code]peer_id[/code]. This will either trigger the default behavior, or invoke the custom spawn/despawn callables specified in [method spawn_config]. See [method send_spawn] for the default behavior.
- </description>
- </method>
- <method name="spawn_config">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="spawn_mode" type="int" enum="MultiplayerReplicator.ReplicationMode" />
- <argument index="2" name="properties" type="StringName[]" default="[]" />
- <argument index="3" name="custom_send" type="Callable" />
- <argument index="4" name="custom_receive" type="Callable" />
- <description>
- Configures the MultiplayerReplicator to track instances of the [PackedScene] identified by [code]scene_id[/code] (see [method ResourceLoader.get_resource_uid]) for the purpose of network replication. When [code]mode[/code] is [constant REPLICATION_MODE_SERVER], the specified [code]properties[/code] will also be replicated to clients during the initial spawn. You can optionally specify a [code]custom_send[/code] and a [code]custom_receive[/code] to override the default behavior and customize the spawn/despawn proecess.
- Tip: You can use a custom property in the scene main script to return a customly optimized state representation.
- </description>
- </method>
- <method name="sync_all">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="peer_id" type="int" default="0" />
- <description>
- Manually request a sync for all the instances of the scene identified by [code]scene_id[/code]. This function will trigger the default sync behavior, or call your send custom send callable if specified in [method sync_config].
- [b]Note:[/b] The default implementation only allow syncing from server to clients.
- </description>
- </method>
- <method name="sync_config">
- <return type="int" enum="Error" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="interval" type="int" />
- <argument index="2" name="properties" type="StringName[]" default="[]" />
- <argument index="3" name="custom_send" type="Callable" />
- <argument index="4" name="custom_receive" type="Callable" />
- <description>
- Configures the MultiplayerReplicator to sync instances of the [PackedScene] identified by [code]scene_id[/code] (see [method ResourceLoader.get_resource_uid]) for the purpose of network replication at the desired [code]interval[/code] (in milliseconds). The specified [code]properties[/code] will be part of the state sync. You can optionally specify a [code]custom_send[/code] and a [code]custom_receive[/code] to override the default behavior and customize the synchronization proecess.
- Tip: You can use a custom property in the scene main script to return a customly optimized state representation (having a single property that returns a PackedByteArray is highly recommended when dealing with many instances).
- </description>
- </method>
- <method name="track">
- <return type="void" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <description>
- Track the given [code]object[/code] as an instance of the scene identified by [code]scene_id[/code]. This object will be passed to your custom sync callables (see [method sync_config]). Tracking and untracking is automatic in [constant REPLICATION_MODE_SERVER].
- </description>
- </method>
- <method name="untrack">
- <return type="void" />
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="object" type="Object" />
- <description>
- Untrack the given [code]object[/code]. This object will no longer be passed to your custom sync callables (see [method sync_config]). Tracking and untracking is automatic in [constant REPLICATION_MODE_SERVER].
- </description>
- </method>
- </methods>
- <signals>
- <signal name="despawn_requested">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="scene_id" type="int" />
- <argument index="2" name="parent" type="Node" />
- <argument index="3" name="name" type="String" />
- <argument index="4" name="data" type="PackedByteArray" />
- <description>
- Emitted when a network despawn request has been received from a client, or for a [PackedScene] that has been configured as [constant REPLICATION_MODE_CUSTOM].
- </description>
- </signal>
- <signal name="despawned">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
- <description>
- Emitted on a client before deleting a local Node upon receiving a despawn request from the server.
- </description>
- </signal>
- <signal name="replicated_instance_added">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
- <description>
- Emitted when an instance of a [PackedScene] that has been configured for networking enters the [SceneTree]. See [method spawn_config].
- </description>
- </signal>
- <signal name="replicated_instance_removed">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
- <description>
- Emitted when an instance of a [PackedScene] that has been configured for networking leaves the [SceneTree]. See [method spawn_config].
- </description>
- </signal>
- <signal name="spawn_requested">
- <argument index="0" name="id" type="int" />
- <argument index="1" name="scene_id" type="int" />
- <argument index="2" name="parent" type="Node" />
- <argument index="3" name="name" type="String" />
- <argument index="4" name="data" type="PackedByteArray" />
- <description>
- Emitted when a network spawn request has been received from a client, or for a [PackedScene] that has been configured as [constant REPLICATION_MODE_CUSTOM].
- </description>
- </signal>
- <signal name="spawned">
- <argument index="0" name="scene_id" type="int" />
- <argument index="1" name="node" type="Node" />
- <description>
- Emitted on a client after a new Node is instantiated locally and added to the SceneTree upon receiving a spawn request from the server.
- </description>
- </signal>
- </signals>
- <constants>
- <constant name="REPLICATION_MODE_NONE" value="0" enum="ReplicationMode">
- Used with [method spawn_config] to identify a [PackedScene] that should not be replicated.
- </constant>
- <constant name="REPLICATION_MODE_SERVER" value="1" enum="ReplicationMode">
- Used with [method spawn_config] to identify a [PackedScene] that should be automatically replicated from server to clients.
- </constant>
- <constant name="REPLICATION_MODE_CUSTOM" value="2" enum="ReplicationMode">
- Used with [method spawn_config] to identify a [PackedScene] that can be manually replicated among peers.
- </constant>
- </constants>
-</class>
diff --git a/doc/classes/MultiplayerSpawner.xml b/doc/classes/MultiplayerSpawner.xml
new file mode 100644
index 0000000000..8bfecfce41
--- /dev/null
+++ b/doc/classes/MultiplayerSpawner.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerSpawner" inherits="Node" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_spawn_custom" qualifiers="virtual">
+ <return type="Object" />
+ <argument index="0" name="data" type="Variant" />
+ <description>
+ </description>
+ </method>
+ <method name="spawn">
+ <return type="Node" />
+ <argument index="0" name="data" type="Variant" default="null" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="auto_spawn" type="bool" setter="set_auto_spawning" getter="is_auto_spawning" default="false">
+ </member>
+ <member name="replication" type="PackedScene[]" setter="set_spawnable_scenes" getter="get_spawnable_scenes" default="[]">
+ </member>
+ <member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
+ </member>
+ <member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
+ </member>
+ </members>
+ <signals>
+ <signal name="despawned">
+ <argument index="0" name="scene_id" type="int" />
+ <argument index="1" name="node" type="Node" />
+ <description>
+ </description>
+ </signal>
+ <signal name="spawned">
+ <argument index="0" name="scene_id" type="int" />
+ <argument index="1" name="node" type="Node" />
+ <description>
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/doc/classes/MultiplayerSynchronizer.xml b/doc/classes/MultiplayerSynchronizer.xml
new file mode 100644
index 0000000000..242d4589a4
--- /dev/null
+++ b/doc/classes/MultiplayerSynchronizer.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerSynchronizer" inherits="Node" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="replication_interval" type="float" setter="set_replication_interval" getter="get_replication_interval" default="0.0">
+ </member>
+ <member name="resource" type="SceneReplicationConfig" setter="set_replication_config" getter="get_replication_config">
+ </member>
+ <member name="root_path" type="NodePath" setter="set_root_path" getter="get_root_path" default="NodePath(&quot;&quot;)">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index d714fbc0d5..89bc905e69 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -52,7 +52,7 @@
It is only called if input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_input].
To consume the input event and stop it propagating further to other nodes, [method Viewport.set_input_as_handled] can be called.
For gameplay input, [method _unhandled_input] and [method _unhandled_key_input] are usually a better fit as they allow the GUI to intercept the events first.
- [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan).
+ [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="_physics_process" qualifiers="virtual">
@@ -62,7 +62,7 @@
Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [code]delta[/code] variable should be constant. [code]delta[/code] is in seconds.
It is only called if physics processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_physics_process].
Corresponds to the [constant NOTIFICATION_PHYSICS_PROCESS] notification in [method Object._notification].
- [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan).
+ [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="_process" qualifiers="virtual">
@@ -72,38 +72,38 @@
Called during the processing step of the main loop. Processing happens at every frame and as fast as possible, so the [code]delta[/code] time since the previous frame is not constant. [code]delta[/code] is in seconds.
It is only called if processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process].
Corresponds to the [constant NOTIFICATION_PROCESS] notification in [method Object._notification].
- [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan).
+ [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="_ready" qualifiers="virtual">
<return type="void" />
<description>
Called when the node is "ready", i.e. when both the node and its children have entered the scene tree. If the node has children, their [method _ready] callbacks get triggered first, and the parent node will receive the ready notification afterwards.
- Corresponds to the [constant NOTIFICATION_READY] notification in [method Object._notification]. See also the [code]onready[/code] keyword for variables.
+ Corresponds to the [constant NOTIFICATION_READY] notification in [method Object._notification]. See also the [code]@onready[/code] annotation for variables.
Usually used for initialization. For even earlier initialization, [method Object._init] may be used. See also [method _enter_tree].
- [b]Note:[/b] [method _ready] may be called only once for each node. After removing a node from the scene tree and adding again, [code]_ready[/code] will not be called for the second time. This can be bypassed with requesting another call with [method request_ready], which may be called anywhere before adding the node again.
+ [b]Note:[/b] [method _ready] may be called only once for each node. After removing a node from the scene tree and adding it again, [code]_ready[/code] will not be called a second time. This can be bypassed by requesting another call with [method request_ready], which may be called anywhere before adding the node again.
</description>
</method>
<method name="_unhandled_input" qualifiers="virtual">
<return type="void" />
<argument index="0" name="event" type="InputEvent" />
<description>
- Called when an [InputEvent] hasn't been consumed by [method _input] or any GUI. The input event propagates up through the node tree until a node consumes it.
+ Called when an [InputEvent] hasn't been consumed by [method _input] or any GUI [Control] item. The input event propagates up through the node tree until a node consumes it.
It is only called if unhandled input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_input].
To consume the input event and stop it propagating further to other nodes, [method Viewport.set_input_as_handled] can be called.
For gameplay input, this and [method _unhandled_key_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first.
- [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan).
+ [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="_unhandled_key_input" qualifiers="virtual">
<return type="void" />
<argument index="0" name="event" type="InputEvent" />
<description>
- Called when an [InputEventKey] or [InputEventShortcut] hasn't been consumed by [method _input] or any GUI. The input event propagates up through the node tree until a node consumes it.
+ Called when an [InputEventKey] or [InputEventShortcut] hasn't been consumed by [method _input] or any GUI [Control] item. The input event propagates up through the node tree until a node consumes it.
It is only called if unhandled key input processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process_unhandled_key_input].
To consume the input event and stop it propagating further to other nodes, [method Viewport.set_input_as_handled] can be called.
For gameplay input, this and [method _unhandled_input] are usually a better fit than [method _input] as they allow the GUI to intercept the events first.
- [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not orphan).
+ [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan).
</description>
</method>
<method name="add_child">
@@ -726,6 +726,18 @@
</member>
</members>
<signals>
+ <signal name="child_entered_tree">
+ <argument index="0" name="node" type="Node" />
+ <description>
+ Emitted when a child node enters the scene tree, either because it entered on its own or because this node entered with it.
+ </description>
+ </signal>
+ <signal name="child_exited_tree">
+ <argument index="0" name="node" type="Node" />
+ <description>
+ Emitted when a child node exits the scene tree, either because it exited on its own or because this node exited.
+ </description>
+ </signal>
<signal name="ready">
<description>
Emitted when the node is ready. Comes after [method _ready] callback and follows the same rules.
diff --git a/doc/classes/Occluder3D.xml b/doc/classes/Occluder3D.xml
index 69fb3002e3..6c6c410bc3 100644
--- a/doc/classes/Occluder3D.xml
+++ b/doc/classes/Occluder3D.xml
@@ -6,10 +6,16 @@
</description>
<tutorials>
</tutorials>
- <members>
- <member name="indices" type="PackedInt32Array" setter="set_indices" getter="get_indices" default="PackedInt32Array()">
- </member>
- <member name="vertices" type="PackedVector3Array" setter="set_vertices" getter="get_vertices" default="PackedVector3Array()">
- </member>
- </members>
+ <methods>
+ <method name="get_indices" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_vertices" qualifiers="const">
+ <return type="PackedVector3Array" />
+ <description>
+ </description>
+ </method>
+ </methods>
</class>
diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml
index d97aa4312f..32e48f9a70 100644
--- a/doc/classes/OccluderInstance3D.xml
+++ b/doc/classes/OccluderInstance3D.xml
@@ -26,6 +26,8 @@
<members>
<member name="bake_mask" type="int" setter="set_bake_mask" getter="get_bake_mask" default="4294967295">
</member>
+ <member name="bake_simplification_distance" type="float" setter="set_bake_simplification_distance" getter="get_bake_simplification_distance" default="0.1">
+ </member>
<member name="occluder" type="Occluder3D" setter="set_occluder" getter="get_occluder">
</member>
</members>
diff --git a/doc/classes/Panel.xml b/doc/classes/Panel.xml
index 21ac7dfac1..3246fa7eba 100644
--- a/doc/classes/Panel.xml
+++ b/doc/classes/Panel.xml
@@ -11,16 +11,6 @@
<link title="2D Finite State Machine Demo">https://godotengine.org/asset-library/asset/516</link>
<link title="3D Inverse Kinematics Demo">https://godotengine.org/asset-library/asset/523</link>
</tutorials>
- <members>
- <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="Panel.Mode" default="0">
- </member>
- </members>
- <constants>
- <constant name="MODE_BACKGROUND" value="0" enum="Mode">
- </constant>
- <constant name="MODE_FOREGROUND" value="1" enum="Mode">
- </constant>
- </constants>
<theme_items>
<theme_item name="panel" data_type="style" type="StyleBox">
The style of this [Panel].
diff --git a/doc/classes/PolygonOccluder3D.xml b/doc/classes/PolygonOccluder3D.xml
new file mode 100644
index 0000000000..a4d910c983
--- /dev/null
+++ b/doc/classes/PolygonOccluder3D.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="PolygonOccluder3D" inherits="Occluder3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array()">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index eb1b0aada7..b316f822f0 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -331,6 +331,13 @@
[b]Note:[/b] The indices of items after the removed item will be shifted by one.
</description>
</method>
+ <method name="scroll_to_item">
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <description>
+ Moves the scroll view to make the item at the given [code]index[/code] visible.
+ </description>
+ </method>
<method name="set_current_index">
<return type="void" />
<argument index="0" name="index" type="int" />
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index dc40d2fd1b..ed124d1d15 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -356,6 +356,8 @@
<member name="debug/gdscript/warnings/incompatible_ternary" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables warnings when a ternary operator may emit values with incompatible types.
</member>
+ <member name="debug/gdscript/warnings/int_assigned_to_enum" type="bool" setter="" getter="" default="true">
+ </member>
<member name="debug/gdscript/warnings/integer_division" type="bool" setter="" getter="" default="true">
If [code]true[/code], enables warnings when dividing an integer by another integer (the decimal part will be discarded).
</member>
diff --git a/doc/classes/QuadOccluder3D.xml b/doc/classes/QuadOccluder3D.xml
new file mode 100644
index 0000000000..c1b89149f5
--- /dev/null
+++ b/doc/classes/QuadOccluder3D.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="QuadOccluder3D" inherits="Occluder3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(1, 1)">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/RID.xml b/doc/classes/RID.xml
index 695b0933fa..990e82593e 100644
--- a/doc/classes/RID.xml
+++ b/doc/classes/RID.xml
@@ -30,6 +30,12 @@
Returns the ID of the referenced resource.
</description>
</method>
+ <method name="is_valid" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if [RID] is valid.
+ </description>
+ </method>
</methods>
<operators>
<operator name="operator !=">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 446db40dd8..82728c0570 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3626,7 +3626,7 @@
<constant name="ARRAY_FORMAT_INDEX" value="4096" enum="ArrayFormat">
Flag used to mark an index array.
</constant>
- <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="2147475463" enum="ArrayFormat">
+ <constant name="ARRAY_FORMAT_BLEND_SHAPE_MASK" value="7" enum="ArrayFormat">
</constant>
<constant name="ARRAY_FORMAT_CUSTOM_BASE" value="13" enum="ArrayFormat">
</constant>
diff --git a/doc/classes/SceneReplicationConfig.xml b/doc/classes/SceneReplicationConfig.xml
new file mode 100644
index 0000000000..e846740dd3
--- /dev/null
+++ b/doc/classes/SceneReplicationConfig.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SceneReplicationConfig" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_property">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="index" type="int" default="-1" />
+ <description>
+ </description>
+ </method>
+ <method name="get_properties" qualifiers="const">
+ <return type="NodePath[]" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_index" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_spawn">
+ <return type="bool" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_get_sync">
+ <return type="bool" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ <method name="property_set_spawn">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="property_set_sync">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="enabled" type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="remove_property">
+ <return type="void" />
+ <argument index="0" name="path" type="NodePath" />
+ <description>
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/SphereOccluder3D.xml b/doc/classes/SphereOccluder3D.xml
new file mode 100644
index 0000000000..1ffa51e170
--- /dev/null
+++ b/doc/classes/SphereOccluder3D.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SphereOccluder3D" inherits="Occluder3D" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
+ </member>
+ </members>
+</class>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 1a9b9ccdcc..a6182f5dab 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -103,6 +103,13 @@
<description>
</description>
</method>
+ <method name="contains" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="what" type="String" />
+ <description>
+ Returns [code]true[/code] if the string contains the given string.
+ </description>
+ </method>
<method name="count" qualifiers="const">
<return type="int" />
<argument index="0" name="what" type="String" />
@@ -215,6 +222,22 @@
[/codeblock]
</description>
</method>
+ <method name="get_slice_count" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="delimiter" type="String" />
+ <description>
+ Splits a string using a [code]delimiter[/code] and returns a number of slices.
+ </description>
+ </method>
+ <method name="get_slicec" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="delimiter" type="int" />
+ <argument index="1" name="slice" type="int" />
+ <description>
+ Splits a string using a Unicode character with code [code]delimiter[/code] and returns a substring at index [code]slice[/code]. Returns an empty string if the index doesn't exist.
+ This is a more performant alternative to [method split] for cases when you need only one element from the array at a fixed index.
+ </description>
+ </method>
<method name="hash" qualifiers="const">
<return type="int" />
<description>
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index b32665a09f..85c4d7593e 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -48,6 +48,18 @@
<description>
</description>
</operator>
+ <operator name="operator &lt;">
+ <return type="bool" />
+ <argument index="0" name="right" type="StringName" />
+ <description>
+ </description>
+ </operator>
+ <operator name="operator &lt;=">
+ <return type="bool" />
+ <argument index="0" name="right" type="StringName" />
+ <description>
+ </description>
+ </operator>
<operator name="operator ==">
<return type="bool" />
<description>
@@ -65,5 +77,17 @@
<description>
</description>
</operator>
+ <operator name="operator &gt;">
+ <return type="bool" />
+ <argument index="0" name="right" type="StringName" />
+ <description>
+ </description>
+ </operator>
+ <operator name="operator &gt;=">
+ <return type="bool" />
+ <argument index="0" name="right" type="StringName" />
+ <description>
+ </description>
+ </operator>
</operators>
</class>
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index 5967340f8c..b500bd5658 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -202,7 +202,7 @@
</description>
</method>
<method name="_font_get_hinting" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="TextServer.Hinting" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns the font hinting mode. Used by dynamic fonts only.
@@ -1028,7 +1028,7 @@
</description>
</method>
<method name="_shaped_text_get_direction" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="TextServer.Direction" />
<argument index="0" name="shaped" type="RID" />
<description>
Returns direction of the text.
@@ -1051,9 +1051,8 @@
</description>
</method>
<method name="_shaped_text_get_ellipsis_glyphs" qualifiers="virtual const">
- <return type="void" />
+ <return type="Glyph*" />
<argument index="0" name="shaped" type="RID" />
- <argument index="1" name="r_glyphs" type="void*" />
<description>
Returns array of the glyphs in the ellipsis.
</description>
@@ -1073,9 +1072,8 @@
</description>
</method>
<method name="_shaped_text_get_glyphs" qualifiers="virtual const">
- <return type="void" />
+ <return type="Glyph*" />
<argument index="0" name="shaped" type="RID" />
- <argument index="1" name="r_glyphs" type="void*" />
<description>
Copies text glyphs in the visual order, into preallocated array of the size returned by [method _shaped_text_get_glyph_count].
</description>
@@ -1089,7 +1087,7 @@
</description>
</method>
<method name="_shaped_text_get_inferred_direction" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="TextServer.Direction" />
<argument index="0" name="shaped" type="RID" />
<description>
Returns direction of the text, inferred by the BiDi algorithm.
@@ -1132,7 +1130,7 @@
</description>
</method>
<method name="_shaped_text_get_orientation" qualifiers="virtual const">
- <return type="int" />
+ <return type="int" enum="TextServer.Orientation" />
<argument index="0" name="shaped" type="RID" />
<description>
Returns text orientation.
@@ -1337,9 +1335,8 @@
</description>
</method>
<method name="_shaped_text_sort_logical" qualifiers="virtual">
- <return type="void" />
+ <return type="Glyph*" />
<argument index="0" name="shaped" type="RID" />
- <argument index="1" name="r_glyphs" type="void*" />
<description>
Copies text glyphs in the logical order, into preallocated array of the size returned by [method _shaped_text_get_glyph_count].
</description>
diff --git a/doc/classes/TextureButton.xml b/doc/classes/TextureButton.xml
index 476ab2d1bf..5f081b95f5 100644
--- a/doc/classes/TextureButton.xml
+++ b/doc/classes/TextureButton.xml
@@ -12,17 +12,17 @@
<link title="3D Voxel Demo">https://godotengine.org/asset-library/asset/676</link>
</tutorials>
<members>
- <member name="expand" type="bool" setter="set_expand" getter="get_expand" default="false">
- If [code]true[/code], the texture stretches to the edges of the node's bounding rectangle using the [member stretch_mode]. If [code]false[/code], the texture will not scale with the node.
- </member>
<member name="flip_h" type="bool" setter="set_flip_h" getter="is_flipped_h" default="false">
If [code]true[/code], texture is flipped horizontally.
</member>
<member name="flip_v" type="bool" setter="set_flip_v" getter="is_flipped_v" default="false">
If [code]true[/code], texture is flipped vertically.
</member>
- <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureButton.StretchMode" default="0">
- Controls the texture's behavior when you resize the node's bounding rectangle, [b]only if[/b] [member expand] is [code]true[/code]. Set it to one of the [enum StretchMode] constants. See the constants to learn more.
+ <member name="ignore_texture_size" type="bool" setter="set_ignore_texture_size" getter="get_ignore_texture_size" default="false">
+ If [code]true[/code], the size of the texture won't be considered for minimum size calculation, so the [TextureButton] can be shrunk down past the texture size.
+ </member>
+ <member name="stretch_mode" type="int" setter="set_stretch_mode" getter="get_stretch_mode" enum="TextureButton.StretchMode" default="2">
+ Controls the texture's behavior when you resize the node's bounding rectangle. See the [enum StretchMode] constants for available options.
</member>
<member name="texture_click_mask" type="BitMap" setter="set_click_mask" getter="get_click_mask">
Pure black and white [BitMap] image to use for click detection. On the mask, white pixels represent the button's clickable area. Use it to create buttons with curved shapes.
diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml
index d1a48fda55..b1367be263 100644
--- a/doc/classes/Theme.xml
+++ b/doc/classes/Theme.xml
@@ -1,20 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Theme" inherits="Resource" version="4.0">
<brief_description>
- Theme for controls.
+ Theme resource for styling/skinning [Control]s and [Window]s.
</brief_description>
<description>
- A theme for skinning controls. Controls can be skinned individually, but for complex applications, it's more practical to just create a global theme that defines everything. This theme can be applied to any [Control]; the Control and its children will automatically use it.
- Theme resources can alternatively be loaded by writing them in a [code].theme[/code] file, see the documentation for more information.
+ A theme resource is used for styling/skinning [Control] and [Window] nodes. While individual controls can be styled using their local theme overrides (see [method Control.add_theme_color_override]), theme resources allow you to store and apply the same settings between all controls sharing the same type (e.g. style all [Button]s the same). One theme resource can be used for the entire project, but you can also set a separate theme resource to a branch of control nodes. A theme resources assigned to a control node applies to the control itself, as well as all of its direct and indirect children (as long as a chain of controls is uninterrupted).
+ Use [member ProjectSettings.gui/theme/custom] to set up a project-scope theme that will be available to every control in your project.
+ Use [member Control.theme] of any control node to set up a theme that will be available to that control and all of its direct and indirect children.
</description>
<tutorials>
<link title="GUI skinning">$DOCS_URL/tutorials/ui/gui_skinning.html</link>
+ <link title="Using the theme editor">$DOCS_URL/tutorials/ui/gui_using_theme_editor.html</link>
</tutorials>
<methods>
<method name="clear">
<return type="void" />
<description>
- Clears all values on the theme.
+ Removes all the theme properties defined on the theme resource.
</description>
</method>
<method name="clear_color">
@@ -22,7 +24,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears the [Color] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the [Color] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_color] to check for existence.
</description>
</method>
<method name="clear_constant">
@@ -30,7 +33,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears the constant at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the constant property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_constant] to check for existence.
</description>
</method>
<method name="clear_font">
@@ -38,7 +42,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears the [Font] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the [Font] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_font] to check for existence.
</description>
</method>
<method name="clear_font_size">
@@ -46,7 +51,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears the font size [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the font size property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_font_size] to check for existence.
</description>
</method>
<method name="clear_icon">
@@ -54,7 +60,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears the icon at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the icon property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_icon] to check for existence.
</description>
</method>
<method name="clear_stylebox">
@@ -62,7 +69,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Clears [StyleBox] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the [StyleBox] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_stylebox] to check for existence.
</description>
</method>
<method name="clear_theme_item">
@@ -71,14 +79,16 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Clears the theme item of [code]data_type[/code] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Removes the theme property of [code]data_type[/code] defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Fails if it doesn't exist. Use [method has_theme_item] to check for existence.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="clear_type_variation">
<return type="void" />
<argument index="0" name="theme_type" type="StringName" />
<description>
- Unmarks [code]theme_type[/code] as being a variation of any other type.
+ Unmarks [code]theme_type[/code] as being a variation of another theme type. See [method set_type_variation].
</description>
</method>
<method name="get_color" qualifiers="const">
@@ -86,20 +96,21 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the [Color] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Returns the [Color] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the default color value if the property doesn't exist. Use [method has_color] to check for existence.
</description>
</method>
<method name="get_color_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the [Color]s as a [PackedStringArray] filled with each [Color]'s name, for use in [method get_color], if the theme has [code]theme_type[/code].
+ Returns a list of names for [Color] properties defined with [code]theme_type[/code]. Use [method get_color_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_color_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the [Color] types as a [PackedStringArray] filled with unique type names, for use in [method get_color] and/or [method get_color_list].
+ Returns a list of all unique theme type names for [Color] properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_constant" qualifiers="const">
@@ -107,20 +118,21 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the constant at [code]name[/code] if the theme has [code]theme_type[/code].
+ Returns the constant property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns [code]0[/code] if the property doesn't exist. Use [method has_constant] to check for existence.
</description>
</method>
<method name="get_constant_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the constants as a [PackedStringArray] filled with each constant's name, for use in [method get_constant], if the theme has [code]theme_type[/code].
+ Returns a list of names for constant properties defined with [code]theme_type[/code]. Use [method get_constant_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_constant_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the constant types as a [PackedStringArray] filled with unique type names, for use in [method get_constant] and/or [method get_constant_list].
+ Returns a list of all unique theme type names for constant properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_font" qualifiers="const">
@@ -128,14 +140,16 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the [Font] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Returns the [Font] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the default theme font if the property doesn't exist and the default theme font is set up (see [member default_font]). Use [method has_font] to check for existence of the property and [method has_default_font] to check for existence of the default theme font.
+ Returns the engine fallback font value, if neither exist.
</description>
</method>
<method name="get_font_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the [Font]s as a [PackedStringArray] filled with each [Font]'s name, for use in [method get_font], if the theme has [code]theme_type[/code].
+ Returns a list of names for [Font] properties defined with [code]theme_type[/code]. Use [method get_font_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_font_size" qualifiers="const">
@@ -143,26 +157,28 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the font size at [code]name[/code] if the theme has [code]theme_type[/code].
+ Returns the font size property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the default theme font size if the property doesn't exist and the default theme font size is set up (see [member default_font_size]). Use [method has_font_size] to check for existence of the property and [method has_default_font_size] to check for existence of the default theme font.
+ Returns the engine fallback font size value, if neither exist.
</description>
</method>
<method name="get_font_size_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the font sizes as a [PackedStringArray] filled with each font size name, for use in [method get_font_size], if the theme has [code]theme_type[/code].
+ Returns a list of names for font size properties defined with [code]theme_type[/code]. Use [method get_font_size_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_font_size_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the font size types as a [PackedStringArray] filled with unique type names, for use in [method get_font_size] and/or [method get_font_size_list].
+ Returns a list of all unique theme type names for font size properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_font_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the [Font] types as a [PackedStringArray] filled with unique type names, for use in [method get_font] and/or [method get_font_list].
+ Returns a list of all unique theme type names for [Font] properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_icon" qualifiers="const">
@@ -170,20 +186,21 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the icon [Texture2D] at [code]name[/code] if the theme has [code]theme_type[/code].
+ Returns the icon property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the engine fallback icon value if the property doesn't exist. Use [method has_icon] to check for existence.
</description>
</method>
<method name="get_icon_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the icons as a [PackedStringArray] filled with each [Texture2D]'s name, for use in [method get_icon], if the theme has [code]theme_type[/code].
+ Returns a list of names for icon properties defined with [code]theme_type[/code]. Use [method get_icon_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_icon_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the icon types as a [PackedStringArray] filled with unique type names, for use in [method get_icon] and/or [method get_icon_list].
+ Returns a list of all unique theme type names for icon properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_stylebox" qualifiers="const">
@@ -191,22 +208,21 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns the [StyleBox] at [code]name[/code] if the theme has [code]theme_type[/code].
- Valid [code]name[/code]s may be found using [method get_stylebox_list]. Valid [code]theme_type[/code]s may be found using [method get_stylebox_type_list].
+ Returns the [StyleBox] property defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the engine fallback stylebox value if the property doesn't exist. Use [method has_stylebox] to check for existence.
</description>
</method>
<method name="get_stylebox_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="theme_type" type="String" />
<description>
- Returns all the [StyleBox]s as a [PackedStringArray] filled with each [StyleBox]'s name, for use in [method get_stylebox], if the theme has [code]theme_type[/code].
- Valid [code]theme_type[/code]s may be found using [method get_stylebox_type_list].
+ Returns a list of names for [StyleBox] properties defined with [code]theme_type[/code]. Use [method get_stylebox_type_list] to get a list of possible theme type names.
</description>
</method>
<method name="get_stylebox_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the [StyleBox] types as a [PackedStringArray] filled with unique type names, for use in [method get_stylebox] and/or [method get_stylebox_list].
+ Returns a list of all unique theme type names for [StyleBox] properties. Use [method get_type_list] to get a list of all unique theme types.
</description>
</method>
<method name="get_theme_item" qualifiers="const">
@@ -215,8 +231,9 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Returns the theme item of [code]data_type[/code] at [code]name[/code] if the theme has [code]theme_type[/code].
- Valid [code]name[/code]s may be found using [method get_theme_item_list] or a data type specific method. Valid [code]theme_type[/code]s may be found using [method get_theme_item_type_list] or a data type specific method.
+ Returns the theme property of [code]data_type[/code] defined by [code]name[/code] and [code]theme_type[/code], if it exists.
+ Returns the engine fallback icon value if the property doesn't exist. Use [method has_theme_item] to check for existence.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="get_theme_item_list" qualifiers="const">
@@ -224,35 +241,36 @@
<argument index="0" name="data_type" type="int" enum="Theme.DataType" />
<argument index="1" name="theme_type" type="String" />
<description>
- Returns all the theme items of [code]data_type[/code] as a [PackedStringArray] filled with each theme items's name, for use in [method get_theme_item] or a data type specific method, if the theme has [code]theme_type[/code].
- Valid [code]theme_type[/code]s may be found using [method get_theme_item_type_list] or a data type specific method.
+ Returns a list of names for properties of [code]data_type[/code] defined with [code]theme_type[/code]. Use [method get_theme_item_type_list] to get a list of possible theme type names.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="get_theme_item_type_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="data_type" type="int" enum="Theme.DataType" />
<description>
- Returns all the theme items of [code]data_type[/code] types as a [PackedStringArray] filled with unique type names, for use in [method get_theme_item], [method get_theme_item_list] or data type specific methods.
+ Returns a list of all unique theme type names for [code]data_type[/code] properties. Use [method get_type_list] to get a list of all unique theme types.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="get_type_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
- Returns all the theme types as a [PackedStringArray] filled with unique type names, for use in other [code]get_*[/code] functions of this theme.
+ Returns a list of all unique theme type names. Use the appropriate [code]get_*_type_list[/code] method to get a list of unique theme types for a single data type.
</description>
</method>
<method name="get_type_variation_base" qualifiers="const">
<return type="StringName" />
<argument index="0" name="theme_type" type="StringName" />
<description>
- Returns the base theme type if [code]theme_type[/code] is a valid variation type. Returns an empty string otherwise.
+ Returns the name of the base theme type if [code]theme_type[/code] is a valid variation type. Returns an empty string otherwise.
</description>
</method>
<method name="get_type_variation_list" qualifiers="const">
<return type="PackedStringArray" />
<argument index="0" name="base_type" type="StringName" />
<description>
- Returns a list of all variation for the given [code]base_type[/code].
+ Returns a list of all type variations for the given [code]base_type[/code].
</description>
</method>
<method name="has_color" qualifiers="const">
@@ -260,8 +278,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if [Color] with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the [Color] property defined by [code]name[/code] and [code]theme_type[/code] exists.
+ Returns [code]false[/code] if it doesn't exist. Use [method set_color] to define it.
</description>
</method>
<method name="has_constant" qualifiers="const">
@@ -269,26 +287,29 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if constant with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the constant property defined by [code]name[/code] and [code]theme_type[/code] exists.
+ Returns [code]false[/code] if it doesn't exist. Use [method set_constant] to define it.
</description>
</method>
<method name="has_default_base_scale" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this theme has a valid [member default_base_scale] value.
+ Returns [code]true[/code] if [member default_base_scale] has a valid value.
+ Returns [code]false[/code] if it doesn't. The value must be greater than [code]0.0[/code] to be considered valid.
</description>
</method>
<method name="has_default_font" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this theme has a valid [member default_font] value.
+ Returns [code]true[/code] if [member default_font] has a valid value.
+ Returns [code]false[/code] if it doesn't.
</description>
</method>
<method name="has_default_font_size" qualifiers="const">
<return type="bool" />
<description>
- Returns [code]true[/code] if this theme has a valid [member default_font_size] value.
+ Returns [code]true[/code] if [member default_font_size] has a valid value.
+ Returns [code]false[/code] if it doesn't. The value must be greater than [code]0[/code] to be considered valid.
</description>
</method>
<method name="has_font" qualifiers="const">
@@ -296,8 +317,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if [Font] with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the [Font] property defined by [code]name[/code] and [code]theme_type[/code] exists, or if the default theme font is set up (see [method has_default_font]).
+ Returns [code]false[/code] if neither exist. Use [method set_font] to define the property.
</description>
</method>
<method name="has_font_size" qualifiers="const">
@@ -305,8 +326,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if font size with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the font size property defined by [code]name[/code] and [code]theme_type[/code] exists, or if the default theme font size is set up (see [method has_default_font_size]).
+ Returns [code]false[/code] if neither exist. Use [method set_font_size] to define the property.
</description>
</method>
<method name="has_icon" qualifiers="const">
@@ -314,8 +335,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if icon [Texture2D] with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the icon property defined by [code]name[/code] and [code]theme_type[/code] exists.
+ Returns [code]false[/code] if it doesn't exist. Use [method set_icon] to define it.
</description>
</method>
<method name="has_stylebox" qualifiers="const">
@@ -323,8 +344,8 @@
<argument index="0" name="name" type="StringName" />
<argument index="1" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if [StyleBox] with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the [StyleBox] property defined by [code]name[/code] and [code]theme_type[/code] exists.
+ Returns [code]false[/code] if it doesn't exist. Use [method set_stylebox] to define it.
</description>
</method>
<method name="has_theme_item" qualifiers="const">
@@ -333,8 +354,9 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Returns [code]true[/code] if a theme item of [code]data_type[/code] with [code]name[/code] is in [code]theme_type[/code].
- Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+ Returns [code]true[/code] if the theme property of [code]data_type[/code] defined by [code]name[/code] and [code]theme_type[/code] exists.
+ Returns [code]false[/code] if it doesn't exist. Use [method set_theme_item] to define it.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="is_type_variation" qualifiers="const">
@@ -342,14 +364,14 @@
<argument index="0" name="theme_type" type="StringName" />
<argument index="1" name="base_type" type="StringName" />
<description>
- Returns [code]true[/code] if [code]theme_type[/code] is marked as a variation of [code]base_type[/code] in this theme.
+ Returns [code]true[/code] if [code]theme_type[/code] is marked as a variation of [code]base_type[/code].
</description>
</method>
<method name="merge_with">
<return type="void" />
<argument index="0" name="other" type="Theme" />
<description>
- Adds missing and overrides existing definitions with values from the [code]other[/code] [Theme].
+ Adds missing and overrides existing definitions with values from the [code]other[/code] theme resource.
[b]Note:[/b] This modifies the current theme. If you want to merge two themes together without modifying either one, create a new empty theme and merge the other two into it one after another.
</description>
</method>
@@ -359,7 +381,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames the [Color] at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the [Color] property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_color] to check for existence, and [method clear_color] to remove the existing property.
</description>
</method>
<method name="rename_constant">
@@ -368,7 +391,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames the constant at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the constant property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_constant] to check for existence, and [method clear_constant] to remove the existing property.
</description>
</method>
<method name="rename_font">
@@ -377,7 +401,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames the [Font] at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the [Font] property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_font] to check for existence, and [method clear_font] to remove the existing property.
</description>
</method>
<method name="rename_font_size">
@@ -386,7 +411,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames the font size [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the font size property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_font_size] to check for existence, and [method clear_font_size] to remove the existing property.
</description>
</method>
<method name="rename_icon">
@@ -395,7 +421,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames the icon at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the icon property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_icon] to check for existence, and [method clear_icon] to remove the existing property.
</description>
</method>
<method name="rename_stylebox">
@@ -404,7 +431,8 @@
<argument index="1" name="name" type="StringName" />
<argument index="2" name="theme_type" type="StringName" />
<description>
- Renames [StyleBox] at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the [StyleBox] property defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_stylebox] to check for existence, and [method clear_stylebox] to remove the existing property.
</description>
</method>
<method name="rename_theme_item">
@@ -414,7 +442,9 @@
<argument index="2" name="name" type="StringName" />
<argument index="3" name="theme_type" type="StringName" />
<description>
- Renames the theme item of [code]data_type[/code] at [code]old_name[/code] to [code]name[/code] if the theme has [code]theme_type[/code]. If [code]name[/code] is already taken, this method fails.
+ Renames the theme property of [code]data_type[/code] defined by [code]old_name[/code] and [code]theme_type[/code] to [code]name[/code], if it exists.
+ Fails if it doesn't exist, or if a similar property with the new name already exists. Use [method has_theme_item] to check for existence, and [method clear_theme_item] to remove the existing property.
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="set_color">
@@ -423,8 +453,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="color" type="Color" />
<description>
- Sets the theme's [Color] to [code]color[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the [Color] property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_color] to remove the property.
</description>
</method>
<method name="set_constant">
@@ -433,8 +462,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="constant" type="int" />
<description>
- Sets the theme's constant to [code]constant[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the constant property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_constant] to remove the property.
</description>
</method>
<method name="set_font">
@@ -443,8 +471,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="font" type="Font" />
<description>
- Sets the theme's [Font] to [code]font[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the [Font] property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_font] to remove the property.
</description>
</method>
<method name="set_font_size">
@@ -453,8 +480,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="font_size" type="int" />
<description>
- Sets the theme's font size to [code]font_size[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the font size property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_font_size] to remove the property.
</description>
</method>
<method name="set_icon">
@@ -463,8 +489,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="texture" type="Texture2D" />
<description>
- Sets the theme's icon [Texture2D] to [code]texture[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the icon property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_icon] to remove the property.
</description>
</method>
<method name="set_stylebox">
@@ -473,8 +498,7 @@
<argument index="1" name="theme_type" type="StringName" />
<argument index="2" name="texture" type="StyleBox" />
<description>
- Sets theme's [StyleBox] to [code]stylebox[/code] at [code]name[/code] in [code]theme_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the [StyleBox] property defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_stylebox] to remove the property.
</description>
</method>
<method name="set_theme_item">
@@ -484,9 +508,9 @@
<argument index="2" name="theme_type" type="StringName" />
<argument index="3" name="value" type="Variant" />
<description>
- Sets the theme item of [code]data_type[/code] to [code]value[/code] at [code]name[/code] in [code]theme_type[/code].
- Does nothing if the [code]value[/code] type does not match [code]data_type[/code].
- Creates [code]theme_type[/code] if the theme does not have it.
+ Creates or changes the value of the theme property of [code]data_type[/code] defined by [code]name[/code] and [code]theme_type[/code]. Use [method clear_theme_item] to remove the property.
+ Fails if the [code]value[/code] type is not accepted by [code]data_type[/code].
+ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic.
</description>
</method>
<method name="set_type_variation">
@@ -494,25 +518,25 @@
<argument index="0" name="theme_type" type="StringName" />
<argument index="1" name="base_type" type="StringName" />
<description>
- Marks [code]theme_type[/code] as being a variation of [code]base_type[/code].
+ Marks [code]theme_type[/code] as a variation of [code]base_type[/code].
This adds [code]theme_type[/code] as a suggested option for [member Control.theme_type_variation] on a [Control] that is of the [code]base_type[/code] class.
- Variations can also be nested, i.e. [code]base_type[/code] can be another variation. If a chain of variations ends with a [code]base_type[/code] matching a class of a [Control], the whole chain is going to be suggested as options.
- [b]Note:[/b] Suggestions only show up if this [Theme] is set as the project default theme. See [member ProjectSettings.gui/theme/custom].
+ Variations can also be nested, i.e. [code]base_type[/code] can be another variation. If a chain of variations ends with a [code]base_type[/code] matching the class of the [Control], the whole chain is going to be suggested as options.
+ [b]Note:[/b] Suggestions only show up if this theme resource is set as the project default theme. See [member ProjectSettings.gui/theme/custom].
</description>
</method>
</methods>
<members>
<member name="default_base_scale" type="float" setter="set_default_base_scale" getter="get_default_base_scale" default="0.0">
- The default base scale factor of this [Theme] resource. Used by some controls to scale their visual properties based on a global scale factor. If this value is set to [code]0.0[/code], the global scale factor is used.
+ The default base scale factor of this theme resource. Used by some controls to scale their visual properties based on the global scale factor. If this value is set to [code]0.0[/code], the global scale factor is used.
Use [method has_default_base_scale] to check if this value is valid.
</member>
<member name="default_font" type="Font" setter="set_default_font" getter="get_default_font">
- The default font of this [Theme] resource. Used as a fallback value for font items defined in this theme, but having invalid values. If this value is also invalid, the global default value is used.
+ The default font of this theme resource. Used as the default value when trying to fetch a font resource that doesn't exist in this theme or is in invalid state. If the default font is also missing or invalid, the engine fallback value is used.
Use [method has_default_font] to check if this value is valid.
</member>
<member name="default_font_size" type="int" setter="set_default_font_size" getter="get_default_font_size" default="-1">
- The default font size of this [Theme] resource. Used as a fallback value for font size items defined in this theme, but having invalid values. If this value is set to [code]-1[/code], the global default value is used.
- Use [method has_default_font_size] to check if this value is valid.
+ The default font size of this theme resource. Used as the default value when trying to fetch a font size value that doesn't exist in this theme or is in invalid state. If the default font size is also missing or invalid, the engine fallback value is used.
+ Values below [code]0[/code] are invalid and can be used to unset the property. Use [method has_default_font_size] to check if this value is valid.
</member>
</members>
<constants>
diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml
index c90cb2987c..6ece42da6b 100644
--- a/doc/classes/TranslationServer.xml
+++ b/doc/classes/TranslationServer.xml
@@ -91,6 +91,13 @@
Returns readable script name for the [code]script[/code] code.
</description>
</method>
+ <method name="get_tool_locale">
+ <return type="String" />
+ <description>
+ Returns the current locale of the editor.
+ [b]Note:[/b] When called from an exported project returns the same value as [method get_locale].
+ </description>
+ </method>
<method name="get_translation_object">
<return type="Translation" />
<argument index="0" name="locale" type="String" />
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 766c740a2c..4b051c4938 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -240,6 +240,7 @@
<method name="scroll_to_item">
<return type="void" />
<argument index="0" name="item" type="TreeItem" />
+ <argument index="1" name="center_on_item" type="bool" default="false" />
<description>
Causes the [Tree] to jump to the specified [TreeItem].
</description>
diff --git a/doc/classes/Vector2i.xml b/doc/classes/Vector2i.xml
index 721c73d603..d7e010cc53 100644
--- a/doc/classes/Vector2i.xml
+++ b/doc/classes/Vector2i.xml
@@ -174,12 +174,12 @@
</description>
</operator>
<operator name="operator *">
- <return type="Vector2i" />
+ <return type="Vector2" />
<argument index="0" name="right" type="float" />
<description>
- Multiplies each component of the [Vector2i] by the given [float] truncated to an integer.
+ Multiplies each component of the [Vector2i] by the given [float]. Returns a [Vector2].
[codeblock]
- print(Vector2i(10, 20) * 0.9) # Prints "(0, 0)"
+ print(Vector2i(10, 15) * 0.9) # Prints "(9, 13.5)"
[/codeblock]
</description>
</operator>
@@ -221,10 +221,10 @@
</description>
</operator>
<operator name="operator /">
- <return type="Vector2i" />
+ <return type="Vector2" />
<argument index="0" name="right" type="float" />
<description>
- Divides each component of the [Vector2i] by the given [float] truncated to an integer.
+ Divides each component of the [Vector2i] by the given [float]. Returns a [Vector2].
[codeblock]
print(Vector2i(10, 20) / 2.9) # Prints "(5, 10)"
[/codeblock]
diff --git a/doc/classes/Vector3i.xml b/doc/classes/Vector3i.xml
index da729e1ec2..e0b8a53a3c 100644
--- a/doc/classes/Vector3i.xml
+++ b/doc/classes/Vector3i.xml
@@ -48,6 +48,7 @@
<method name="abs" qualifiers="const">
<return type="Vector3i" />
<description>
+ Returns a new vector with all components in absolute values (i.e. positive).
</description>
</method>
<method name="clamp" qualifiers="const">
@@ -180,12 +181,12 @@
</description>
</operator>
<operator name="operator *">
- <return type="Vector3i" />
+ <return type="Vector3" />
<argument index="0" name="right" type="float" />
<description>
- Multiplies each component of the [Vector3i] by the given [float] truncated to an integer.
+ Multiplies each component of the [Vector3i] by the given [float]. Returns a [Vector3].
[codeblock]
- print(Vector3i(10, 20, 30) * 0.9) # Prints "(0, 0, 0)"
+ print(Vector3i(10, 15, 20) * 0.9) # Prints "(9, 13.5, 18)"
[/codeblock]
</description>
</operator>
@@ -227,10 +228,10 @@
</description>
</operator>
<operator name="operator /">
- <return type="Vector3i" />
+ <return type="Vector3" />
<argument index="0" name="right" type="float" />
<description>
- Divides each component of the [Vector3i] by the given [float] truncated to an integer.
+ Divides each component of the [Vector3i] by the given [float]. Returns a [Vector3].
[codeblock]
print(Vector3i(10, 20, 30) / 2.9) # Prints "(5, 10, 15)"
[/codeblock]
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 1b37cab68e..7a60ca9fa6 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -55,7 +55,7 @@
<method name="get_mouse_position" qualifiers="const">
<return type="Vector2" />
<description>
- Returns the mouse position relative to the viewport.
+ Returns the mouse's positon in this [Viewport] using the coordinate system of this [Viewport].
</description>
</method>
<method name="get_render_info">
@@ -180,7 +180,7 @@
<return type="void" />
<argument index="0" name="to_position" type="Vector2" />
<description>
- Warps the mouse to a position relative to the viewport.
+ Moves the mouse pointer to the specified position in this [Viewport] using the coordinate system of this [Viewport].
</description>
</method>
</methods>
diff --git a/doc/classes/VisualShaderNode.xml b/doc/classes/VisualShaderNode.xml
index 0067240820..332620f915 100644
--- a/doc/classes/VisualShaderNode.xml
+++ b/doc/classes/VisualShaderNode.xml
@@ -75,7 +75,7 @@
<constant name="PORT_TYPE_VECTOR_2D" value="2" enum="PortType">
2D vector of floating-point values. Translated to [code]vec2[/code] type in shader code.
</constant>
- <constant name="PORT_TYPE_VECTOR" value="3" enum="PortType">
+ <constant name="PORT_TYPE_VECTOR_3D" value="3" enum="PortType">
3D vector of floating-point values. Translated to [code]vec3[/code] type in shader code.
</constant>
<constant name="PORT_TYPE_BOOLEAN" value="4" enum="PortType">
diff --git a/doc/classes/VisualShaderNodeParticleRandomness.xml b/doc/classes/VisualShaderNodeParticleRandomness.xml
index 2dec41105c..880208b136 100644
--- a/doc/classes/VisualShaderNodeParticleRandomness.xml
+++ b/doc/classes/VisualShaderNodeParticleRandomness.xml
@@ -8,14 +8,20 @@
</tutorials>
<members>
<member name="op_type" type="int" setter="set_op_type" getter="get_op_type" enum="VisualShaderNodeParticleRandomness.OpType" default="0">
+ A type of operands and returned value.
</member>
</members>
<constants>
<constant name="OP_TYPE_SCALAR" value="0" enum="OpType">
+ A floating-point scalar.
</constant>
- <constant name="OP_TYPE_VECTOR" value="1" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_2D" value="1" enum="OpType">
+ A 2D vector type.
</constant>
- <constant name="OP_TYPE_MAX" value="2" enum="OpType">
+ <constant name="OP_TYPE_VECTOR_3D" value="2" enum="OpType">
+ A 3D vector type.
+ </constant>
+ <constant name="OP_TYPE_MAX" value="3" enum="OpType">
Represents the size of the [enum OpType] enum.
</constant>
</constants>
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 3bb6603646..82bb74683f 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -398,6 +398,11 @@
Fullscreen window mode. Note that this is not [i]exclusive[/i] fullscreen. On Windows and Linux, a borderless window is used to emulate fullscreen. On macOS, a new desktop is used to display the running project.
Regardless of the platform, enabling fullscreen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling fullscreen mode.
</constant>
+ <constant name="MODE_EXCLUSIVE_FULLSCREEN" value="4" enum="Mode">
+ Exclusive fullscreen window mode. This mode is implemented on Windows only. On other platforms, it is equivalent to [constant MODE_FULLSCREEN].
+ Only one window in exclusive fullscreen mode can be visible on a given screen at a time. If multiple windows are in exclusive fullscreen mode for the same screen, the last one being set to this mode takes precedence.
+ Regardless of the platform, enabling fullscreen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling fullscreen mode.
+ </constant>
<constant name="FLAG_RESIZE_DISABLED" value="0" enum="Flags">
The window's ability to be resized.
</constant>
@@ -430,7 +435,7 @@
<constant name="CONTENT_SCALE_ASPECT_EXPAND" value="4" enum="ContentScaleAspect">
</constant>
<constant name="LAYOUT_DIRECTION_INHERITED" value="0" enum="LayoutDirection">
- Automatic layout direction, determined from the parent control layout direction.
+ Automatic layout direction, determined from the parent window layout direction.
</constant>
<constant name="LAYOUT_DIRECTION_LOCALE" value="1" enum="LayoutDirection">
Automatic layout direction, determined from the current locale.
diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml
index 49f2d2dd7f..243d19d94f 100644
--- a/doc/classes/bool.xml
+++ b/doc/classes/bool.xml
@@ -52,7 +52,7 @@
[codeblocks]
[gdscript]
var _can_shoot = true
- onready var _cool_down = $CoolDownTimer
+ @onready var _cool_down = $CoolDownTimer
func shoot():
if _can_shoot and Input.is_action_pressed("shoot"):
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index 9effe9d5bf..2dae7275b5 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -89,12 +89,12 @@
</description>
</operator>
<operator name="operator *">
- <return type="Vector2i" />
+ <return type="Vector2" />
<argument index="0" name="right" type="Vector2i" />
<description>
- Multiplies each component of the [Vector2i] by the given [float] truncated to an integer.
+ Multiplies each component of the [Vector2i] by the given [float]. Returns a [Vector2].
[codeblock]
- print(0.9 * Vector2i(10, 20)) # Prints "(0, 0)"
+ print(0.9 * Vector2i(10, 15)) # Prints "(9, 13.5)"
[/codeblock]
</description>
</operator>
@@ -106,12 +106,12 @@
</description>
</operator>
<operator name="operator *">
- <return type="Vector3i" />
+ <return type="Vector3" />
<argument index="0" name="right" type="Vector3i" />
<description>
- Multiplies each component of the [Vector3i] by the given [float] truncated to an integer.
+ Multiplies each component of the [Vector3i] by the given [float]. Returns a [Vector3].
[codeblock]
- print(0.9 * Vector3i(10, 20, 30)) # Prints "(0, 0, 0)"
+ print(0.9 * Vector3i(10, 15, 20)) # Prints "(9, 13.5, 18)"
[/codeblock]
</description>
</operator>
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index d212fe42bf..006dc7eb29 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -177,6 +177,13 @@
</description>
</operator>
<operator name="operator +">
+ <return type="String" />
+ <argument index="0" name="right" type="String" />
+ <description>
+ Adds Unicode character with code [int] to the [String].
+ </description>
+ </operator>
+ <operator name="operator +">
<return type="float" />
<argument index="0" name="right" type="float" />
<description>
diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp
index 44455b927b..689d76ba26 100644
--- a/drivers/vulkan/vulkan_context.cpp
+++ b/drivers/vulkan/vulkan_context.cpp
@@ -273,9 +273,9 @@ Error VulkanContext::_obtain_vulkan_version() {
uint32_t api_version;
VkResult res = func(&api_version);
if (res == VK_SUCCESS) {
- vulkan_major = VK_VERSION_MAJOR(api_version);
- vulkan_minor = VK_VERSION_MINOR(api_version);
- vulkan_patch = VK_VERSION_PATCH(api_version);
+ vulkan_major = VK_API_VERSION_MAJOR(api_version);
+ vulkan_minor = VK_API_VERSION_MINOR(api_version);
+ vulkan_patch = VK_API_VERSION_PATCH(api_version);
} else {
// according to the documentation this shouldn't fail with anything except a memory allocation error
// in which case we're in deep trouble anyway
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index e36d0b846b..2f33619a52 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1723,15 +1723,15 @@ void AnimationTimelineEdit::update_values() {
switch (animation->get_loop_mode()) {
case Animation::LoopMode::LOOP_NONE: {
- loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
+ loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
loop->set_pressed(false);
} break;
case Animation::LoopMode::LOOP_LINEAR: {
- loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
+ loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons")));
loop->set_pressed(true);
} break;
case Animation::LoopMode::LOOP_PINGPONG: {
- loop->set_icon(get_theme_icon("PingPongLoop", "EditorIcons"));
+ loop->set_icon(get_theme_icon(SNAME("PingPongLoop"), SNAME("EditorIcons")));
loop->set_pressed(true);
} break;
default:
@@ -4516,7 +4516,7 @@ void AnimationTrackEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons")));
snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
- view_group->set_icon(get_theme_icon(view_group->is_pressed() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons"));
+ view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons")));
selected_filter->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons")));
imported_anim_warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons")));
main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
@@ -5493,8 +5493,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) {
String text;
Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons"));
if (node) {
- if (has_theme_icon(node->get_class(), "EditorIcons")) {
- icon = get_theme_icon(node->get_class(), "EditorIcons");
+ if (has_theme_icon(node->get_class(), SNAME("EditorIcons"))) {
+ icon = get_theme_icon(node->get_class(), SNAME("EditorIcons"));
}
text = node->get_name();
@@ -5907,7 +5907,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
void AnimationTrackEditor::_view_group_toggle() {
_update_tracks();
- view_group->set_icon(get_theme_icon(view_group->is_pressed() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons"));
+ view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons")));
}
bool AnimationTrackEditor::is_grouping_tracks() {
diff --git a/editor/array_property_edit.cpp b/editor/array_property_edit.cpp
index d9c6b72241..6be8a7e564 100644
--- a/editor/array_property_edit.cpp
+++ b/editor/array_property_edit.cpp
@@ -121,7 +121,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) {
}
} else if (pn.begins_with("indices")) {
- if (pn.find("_") != -1) {
+ if (pn.contains("_")) {
//type
int idx = pn.get_slicec('/', 1).get_slicec('_', 0).to_int();
@@ -178,7 +178,7 @@ bool ArrayPropertyEdit::_get(const StringName &p_name, Variant &r_ret) const {
return true;
}
} else if (pn.begins_with("indices")) {
- if (pn.find("_") != -1) {
+ if (pn.contains("_")) {
//type
int idx = pn.get_slicec('/', 1).get_slicec('_', 0).to_int();
bool valid;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 967f7e0d1f..2627baaea8 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -884,8 +884,8 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptCodeCompletionOp
Ref<Texture2D> tex;
switch (p_option.kind) {
case ScriptCodeCompletionOption::KIND_CLASS: {
- if (has_theme_icon(p_option.display, "EditorIcons")) {
- tex = get_theme_icon(p_option.display, "EditorIcons");
+ if (has_theme_icon(p_option.display, SNAME("EditorIcons"))) {
+ tex = get_theme_icon(p_option.display, SNAME("EditorIcons"));
} else {
tex = get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
}
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 1d5fa9cbbb..8efcd60210 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -310,7 +310,7 @@ void ConnectDialog::set_dst_node(Node *p_node) {
StringName ConnectDialog::get_dst_method_name() const {
String txt = dst_method->get_text();
- if (txt.find("(") != -1) {
+ if (txt.contains("(")) {
txt = txt.left(txt.find("(")).strip_edges();
}
return txt;
@@ -764,7 +764,7 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) {
String node_name = selected_node->get_name();
for (int i = 0; i < node_name.length(); i++) { // TODO: Regex filter may be cleaner.
char32_t c = node_name[i];
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+ if (!is_ascii_identifier_char(c)) {
if (c == ' ') {
// Replace spaces with underlines.
c = '_';
@@ -988,8 +988,8 @@ void ConnectionsDock::update_tree() {
name = scr->get_class();
}
- if (has_theme_icon(scr->get_class(), "EditorIcons")) {
- icon = get_theme_icon(scr->get_class(), "EditorIcons");
+ if (has_theme_icon(scr->get_class(), SNAME("EditorIcons"))) {
+ icon = get_theme_icon(scr->get_class(), SNAME("EditorIcons"));
}
}
} else {
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index e8fd97fa1a..61ec8abacf 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -446,14 +446,14 @@ void CreateDialog::_notification(int p_what) {
}
}
-void CreateDialog::select_type(const String &p_type) {
+void CreateDialog::select_type(const String &p_type, bool p_center_on_item) {
if (!search_options_types.has(p_type)) {
return;
}
TreeItem *to_select = search_options_types[p_type];
to_select->select(0);
- search_options->scroll_to_item(to_select);
+ search_options->scroll_to_item(to_select, p_center_on_item);
if (EditorHelp::get_doc_data()->class_list.has(p_type) && !DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description).is_empty()) {
// Display both class name and description, since the help bit may be displayed
@@ -503,24 +503,14 @@ Variant CreateDialog::instance_selected() {
} else {
obj = ClassDB::instantiate(selected->get_text(0));
}
-
- // Check if any Object-type property should be instantiated.
- List<PropertyInfo> pinfo;
- ((Object *)obj)->get_property_list(&pinfo);
-
- for (const PropertyInfo &pi : pinfo) {
- if (pi.type == Variant::OBJECT && pi.usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) {
- Object *prop = ClassDB::instantiate(pi.class_name);
- ((Object *)obj)->set(pi.name, prop);
- }
- }
+ EditorNode::get_editor_data().instantiate_object_properties(obj);
return obj;
}
void CreateDialog::_item_selected() {
String name = get_selected_type();
- select_type(name);
+ select_type(name, false);
}
void CreateDialog::_hide_requested() {
diff --git a/editor/create_dialog.h b/editor/create_dialog.h
index f905160df3..a82c4db191 100644
--- a/editor/create_dialog.h
+++ b/editor/create_dialog.h
@@ -79,7 +79,7 @@ class CreateDialog : public ConfirmationDialog {
void _sbox_input(const Ref<InputEvent> &p_ie);
void _text_changed(const String &p_newtext);
- void select_type(const String &p_type);
+ void select_type(const String &p_type, bool p_center_on_item = true);
void _item_selected();
void _hide_requested();
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index 38bdbe2870..ad782c87cb 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -155,7 +155,7 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
if (pinfo.type == Variant::OBJECT) {
if (var.get_type() == Variant::STRING) {
String path = var;
- if (path.find("::") != -1) {
+ if (path.contains("::")) {
// built-in resource
String base_path = path.get_slice("::", 0);
RES dependency = ResourceLoader::load(base_path);
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index 79853b6809..8702e773f8 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -500,7 +500,7 @@ void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p
dbg->set_breakpoint(p_path, p_line, p_enabled);
});
- emit_signal("breakpoint_toggled", p_path, p_line, p_enabled);
+ emit_signal(SNAME("breakpoint_toggled"), p_path, p_line, p_enabled);
}
void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) {
@@ -683,7 +683,7 @@ EditorDebuggerNode::CameraOverride EditorDebuggerNode::get_camera_override() {
void EditorDebuggerNode::add_debugger_plugin(const Ref<Script> &p_script) {
ERR_FAIL_COND_MSG(debugger_plugins.has(p_script), "Debugger plugin already exists.");
ERR_FAIL_COND_MSG(p_script.is_null(), "Debugger plugin script is null");
- ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) == "", "Debugger plugin script has error.");
+ ERR_FAIL_COND_MSG(p_script->get_instance_base_type() == StringName(), "Debugger plugin script has error.");
ERR_FAIL_COND_MSG(String(p_script->get_instance_base_type()) != "EditorDebuggerPlugin", "Base type of debugger plugin is not 'EditorDebuggerPlugin'.");
ERR_FAIL_COND_MSG(!p_script->is_tool(), "Debugger plugin script is not in tool mode.");
debugger_plugins.insert(p_script);
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index ee844fff64..6a2cb8ee4a 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -36,7 +36,6 @@
#include "core/io/marshalls.h"
#include "core/string/ustring.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "editor/debugger/debug_adapter/debug_adapter_protocol.h"
#include "editor/debugger/editor_network_profiler.h"
#include "editor/debugger/editor_performance_profiler.h"
@@ -497,7 +496,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
TreeItem *error = error_tree->create_item(r);
error->set_collapsed(true);
- error->set_icon(0, get_theme_icon(oe.warning ? "Warning" : "Error", "EditorIcons"));
+ error->set_icon(0, get_theme_icon(oe.warning ? SNAME("Warning") : SNAME("Error"), SNAME("EditorIcons")));
error->set_text(0, time);
error->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
@@ -888,12 +887,12 @@ void ScriptEditorDebugger::_clear_execution() {
void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) {
Ref<Script> script = ResourceLoader::load(p_file);
- emit_signal("set_breakpoint", script, p_line - 1, p_enabled);
+ emit_signal(SNAME("set_breakpoint"), script, p_line - 1, p_enabled);
script.unref();
}
void ScriptEditorDebugger::_clear_breakpoints() {
- emit_signal("clear_breakpoints");
+ emit_signal(SNAME("clear_breakpoints"));
}
void ScriptEditorDebugger::_breakpoint_tree_clicked() {
@@ -1543,19 +1542,10 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
const int line_number = file_line_number[1].to_int();
// Construct a GitHub repository URL and open it in the user's default web browser.
- if (String(VERSION_HASH).length() >= 1) {
- // Git commit hash information available; use it for greater accuracy, including for development versions.
- OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",
- VERSION_HASH,
- file,
- line_number));
- } else {
- // Git commit hash information unavailable; fall back to tagged releases.
- OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s-stable/%s#L%d",
- VERSION_NUMBER,
- file,
- line_number));
- }
+ // If the commit hash is available, use it for greater accuracy. Otherwise fall back to tagged release.
+ String git_ref = String(VERSION_HASH).is_empty() ? String(VERSION_NUMBER) + "-stable" : String(VERSION_HASH);
+ OS::get_singleton()->shell_open(vformat("https://github.com/godotengine/godot/blob/%s/%s#L%d",
+ git_ref, file, line_number));
} break;
case ACTION_DELETE_BREAKPOINT: {
const TreeItem *selected = breakpoints_tree->get_selected();
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 9879671e7b..1fa5df9396 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -171,7 +171,7 @@ void DependencyEditor::_update_list() {
String path;
String type;
- if (n.find("::") != -1) {
+ if (n.contains("::")) {
path = n.get_slice("::", 0);
type = n.get_slice("::", 1);
} else {
diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp
index 54377971c6..4309f55a2b 100644
--- a/editor/editor_about.cpp
+++ b/editor/editor_about.cpp
@@ -29,13 +29,12 @@
/*************************************************************************/
#include "editor_about.h"
-#include "editor_node.h"
#include "core/authors.gen.h"
#include "core/donors.gen.h"
#include "core/license.gen.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
+#include "editor_node.h"
// The metadata key used to store and retrieve the version text to copy to the clipboard.
static const String META_TEXT_TO_COPY = "text_to_copy";
diff --git a/editor/editor_atlas_packer.cpp b/editor/editor_atlas_packer.cpp
index b6ec5d1bad..aad32968de 100644
--- a/editor/editor_atlas_packer.cpp
+++ b/editor/editor_atlas_packer.cpp
@@ -30,6 +30,9 @@
#include "editor_atlas_packer.h"
+#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+
void EditorAtlasPacker::_plot_triangle(Ref<BitMap> p_bitmap, Vector2i *vertices) {
int width = p_bitmap->get_size().width;
int height = p_bitmap->get_size().height;
diff --git a/editor/editor_atlas_packer.h b/editor/editor_atlas_packer.h
index 133c516d80..169a6bead8 100644
--- a/editor/editor_atlas_packer.h
+++ b/editor/editor_atlas_packer.h
@@ -31,11 +31,12 @@
#ifndef EDITOR_ATLAS_PACKER_H
#define EDITOR_ATLAS_PACKER_H
-#include "core/math/vector2.h"
-
#include "core/templates/vector.h"
#include "scene/resources/bit_map.h"
+struct Vector2;
+struct Vector2i;
+
class EditorAtlasPacker {
public:
struct Chart {
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index d74a6e12b2..6d31141be7 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -362,21 +362,21 @@ Node *EditorAutoloadSettings::_create_autoload(const String &p_path) {
RES res = ResourceLoader::load(p_path);
ERR_FAIL_COND_V_MSG(res.is_null(), nullptr, "Can't autoload: " + p_path + ".");
Node *n = nullptr;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instantiate();
- } else if (res->is_class("Script")) {
- Ref<Script> s = res;
- StringName ibt = s->get_instance_base_type();
+ Ref<PackedScene> scn = res;
+ Ref<Script> script = res;
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ } else if (script.is_valid()) {
+ StringName ibt = script->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
ERR_FAIL_COND_V_MSG(!valid_type, nullptr, "Script does not inherit a Node: " + p_path + ".");
Object *obj = ClassDB::instantiate(ibt);
- ERR_FAIL_COND_V_MSG(obj == nullptr, nullptr, "Cannot instance script for AutoLoad, expected 'Node' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_COND_V_MSG(!obj, nullptr, "Cannot instance script for AutoLoad, expected 'Node' inheritance, got: " + String(ibt) + ".");
n = Object::cast_to<Node>(obj);
- n->set_script(s);
+ n->set_script(script);
}
ERR_FAIL_COND_V_MSG(!n, nullptr, "Path in AutoLoad not a node or script: " + p_path + ".");
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 7a0a4abbff..625330ef37 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -520,6 +520,21 @@ void EditorData::remove_custom_type(const String &p_type) {
}
}
+void EditorData::instantiate_object_properties(Object *p_object) {
+ ERR_FAIL_NULL(p_object);
+ // Check if any Object-type property should be instantiated.
+ List<PropertyInfo> pinfo;
+ p_object->get_property_list(&pinfo);
+
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ PropertyInfo pi = E->get();
+ if (pi.type == Variant::OBJECT && pi.usage & PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT) {
+ Object *prop = ClassDB::instantiate(pi.class_name);
+ p_object->set(pi.name, prop);
+ }
+ }
+}
+
int EditorData::add_edited_scene(int p_at_pos) {
if (p_at_pos < 0) {
p_at_pos = edited_scene.size();
@@ -894,21 +909,12 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
return false;
}
- Ref<Script> script = script_class_load_script(p_class);
- if (script.is_null()) {
- return false;
- }
-
- String base = script_class_get_base(p_class);
- Ref<Script> base_script = script->get_base_script();
-
- while (p_inherits != base) {
+ String base = p_class;
+ while (base != p_inherits) {
if (ClassDB::class_exists(base)) {
return ClassDB::is_parent_class(base, p_inherits);
} else if (ScriptServer::is_global_class(base)) {
- base = script_class_get_base(base);
- } else if (base_script.is_valid()) {
- return ClassDB::is_parent_class(base_script->get_instance_base_type(), p_inherits);
+ base = ScriptServer::get_global_class_base(base);
} else {
return false;
}
diff --git a/editor/editor_data.h b/editor/editor_data.h
index ccf641ff26..e485d47bf6 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -182,6 +182,8 @@ public:
void remove_custom_type(const String &p_type);
const Map<String, Vector<CustomType>> &get_custom_types() const { return custom_types; }
+ void instantiate_object_properties(Object *p_object);
+
int add_edited_scene(int p_at_pos);
void move_edited_scene_index(int p_idx, int p_to_idx);
void remove_scene(int p_idx);
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 792897e451..632d67c061 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -404,9 +404,9 @@ Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const {
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
ERR_FAIL_COND_V(theme.is_null(), Ref<ImageTexture>());
if (EditorNode::get_singleton()->get_main_control()->is_layout_rtl()) {
- return theme->get_icon("PlayBackwards", "EditorIcons");
+ return theme->get_icon(SNAME("PlayBackwards"), SNAME("EditorIcons"));
} else {
- return theme->get_icon("Play", "EditorIcons");
+ return theme->get_icon(SNAME("Play"), SNAME("EditorIcons"));
}
}
@@ -818,7 +818,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
int v = 0;
if (i * 2 < script_key.length()) {
char32_t ct = script_key[i * 2];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
@@ -828,7 +828,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
if (i * 2 + 1 < script_key.length()) {
char32_t ct = script_key[i * 2 + 1];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
@@ -1215,7 +1215,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
int v = 0;
if (i * 2 < script_key.length()) {
char32_t ct = script_key[i * 2];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
@@ -1225,7 +1225,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
if (i * 2 + 1 < script_key.length()) {
char32_t ct = script_key[i * 2 + 1];
- if (ct >= '0' && ct <= '9') {
+ if (is_digit(ct)) {
ct = ct - '0';
} else if (ct >= 'a' && ct <= 'f') {
ct = 10 + ct - 'a';
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index 94262c2289..2fc29c46af 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -462,7 +462,7 @@ void EditorFeatureProfileManager::_erase_selected_profile() {
void EditorFeatureProfileManager::_create_new_profile() {
String name = new_profile_name->get_text().strip_edges();
- if (!name.is_valid_filename() || name.find(".") != -1) {
+ if (!name.is_valid_filename() || name.contains(".")) {
EditorNode::get_singleton()->show_warning(TTR("Profile must be a valid filename and must not contain '.'"));
return;
}
@@ -592,7 +592,15 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
List<PropertyInfo> props;
ClassDB::get_property_list(class_name, &props, true);
- if (props.size() > 0) {
+ bool has_editor_props = false;
+ for (const PropertyInfo &E : props) {
+ if (E.usage & PROPERTY_USAGE_EDITOR) {
+ has_editor_props = true;
+ break;
+ }
+ }
+
+ if (has_editor_props) {
TreeItem *properties = property_list->create_item(root);
properties->set_text(0, TTR("Class Properties:"));
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index bb76af8f9b..e80e9c43b0 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -45,19 +45,19 @@
DocTools *EditorHelp::doc = nullptr;
void EditorHelp::_update_theme() {
- text_color = get_theme_color("text_color", "EditorHelp");
- title_color = get_theme_color("title_color", "EditorHelp");
- headline_color = get_theme_color("headline_color", "EditorHelp");
- comment_color = get_theme_color("comment_color", "EditorHelp");
- symbol_color = get_theme_color("symbol_color", "EditorHelp");
- value_color = get_theme_color("value_color", "EditorHelp");
- qualifier_color = get_theme_color("qualifier_color", "EditorHelp");
- type_color = get_theme_color("type_color", "EditorHelp");
-
- class_desc->add_theme_color_override("selection_color", get_theme_color("selection_color", "EditorHelp"));
- class_desc->add_theme_constant_override("line_separation", get_theme_constant("line_separation", "EditorHelp"));
- class_desc->add_theme_constant_override("table_hseparation", get_theme_constant("table_hseparation", "EditorHelp"));
- class_desc->add_theme_constant_override("table_vseparation", get_theme_constant("table_vseparation", "EditorHelp"));
+ text_color = get_theme_color(SNAME("text_color"), SNAME("EditorHelp"));
+ title_color = get_theme_color(SNAME("title_color"), SNAME("EditorHelp"));
+ headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp"));
+ comment_color = get_theme_color(SNAME("comment_color"), SNAME("EditorHelp"));
+ symbol_color = get_theme_color(SNAME("symbol_color"), SNAME("EditorHelp"));
+ value_color = get_theme_color(SNAME("value_color"), SNAME("EditorHelp"));
+ qualifier_color = get_theme_color(SNAME("qualifier_color"), SNAME("EditorHelp"));
+ type_color = get_theme_color(SNAME("type_color"), SNAME("EditorHelp"));
+
+ class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp")));
+ class_desc->add_theme_constant_override("line_separation", get_theme_constant(SNAME("line_separation"), SNAME("EditorHelp")));
+ class_desc->add_theme_constant_override("table_hseparation", get_theme_constant(SNAME("table_hseparation"), SNAME("EditorHelp")));
+ class_desc->add_theme_constant_override("table_vseparation", get_theme_constant(SNAME("table_vseparation"), SNAME("EditorHelp")));
doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts"));
doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts"));
@@ -81,7 +81,7 @@ void EditorHelp::_class_desc_select(const String &p_select) {
if (p_select.begins_with("$")) { //enum
String select = p_select.substr(1, p_select.length());
String class_name;
- if (select.find(".") != -1) {
+ if (select.contains(".")) {
class_name = select.get_slice(".", 0);
select = select.get_slice(".", 1);
} else {
@@ -123,7 +123,7 @@ void EditorHelp::_class_desc_select(const String &p_select) {
return;
}
- if (link.find(".") != -1) {
+ if (link.contains(".")) {
emit_signal(SNAME("go_to_help"), topic + ":" + link.get_slice(".", 0) + ":" + link.get_slice(".", 1));
} else {
if (table->has(link)) {
@@ -185,7 +185,7 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
if (t.is_empty()) {
t = "void";
}
- bool can_ref = (t != "void" && t.find("*") == -1) || !p_enum.is_empty();
+ bool can_ref = (t != "void" && !t.contains("*")) || !p_enum.is_empty();
if (!p_enum.is_empty()) {
if (p_enum.get_slice_count(".") > 1) {
@@ -240,7 +240,7 @@ String EditorHelp::_fix_constant(const String &p_constant) const {
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
method_line[p_method.name] = class_desc->get_line_count() - 2; //gets overridden if description
- const bool is_vararg = p_method.qualifiers.find("vararg") != -1;
+ const bool is_vararg = p_method.qualifiers.contains("vararg");
if (p_overview) {
class_desc->push_cell();
@@ -364,7 +364,7 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods,
for (int i = 0; i < p_methods.size(); i++) {
const String &q = p_methods[i].qualifiers;
- if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
+ if ((pass == 0 && q.contains("virtual")) || (pass == 1 && !q.contains("virtual"))) {
m.push_back(p_methods[i]);
}
}
@@ -429,7 +429,7 @@ void EditorHelp::_update_method_descriptions(const DocData::ClassDoc p_classdoc,
for (int i = 0; i < p_methods.size(); i++) {
const String &q = p_methods[i].qualifiers;
- if ((pass == 0 && q.find("virtual") != -1) || (pass == 1 && q.find("virtual") == -1)) {
+ if ((pass == 0 && q.contains("virtual")) || (pass == 1 && !q.contains("virtual"))) {
methods_filtered.push_back(p_methods[i]);
}
}
@@ -820,7 +820,7 @@ void EditorHelp::_update_doc() {
}
}
// Ignore undocumented non virtual private.
- if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.is_empty() && cd.methods[i].qualifiers.find("virtual") == -1) {
+ if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.is_empty() && !cd.methods[i].qualifiers.contains("virtual")) {
continue;
}
methods.push_back(cd.methods[i]);
@@ -1923,7 +1923,7 @@ void EditorHelpBit::_meta_clicked(String p_select) {
String select = p_select.substr(1, p_select.length());
String class_name;
- if (select.find(".") != -1) {
+ if (select.contains(".")) {
class_name = select.get_slice(".", 0);
} else {
class_name = "@Global";
@@ -1936,7 +1936,7 @@ void EditorHelpBit::_meta_clicked(String p_select) {
} else if (p_select.begins_with("@")) {
String m = p_select.substr(1, p_select.length());
- if (m.find(".") != -1) {
+ if (m.contains(".")) {
_go_to_help("class_method:" + m.get_slice(".", 0) + ":" + m.get_slice(".", 0)); //must go somewhere else
}
}
@@ -2007,7 +2007,7 @@ FindBar::FindBar() {
hide_button = memnew(TextureButton);
add_child(hide_button);
hide_button->set_focus_mode(FOCUS_NONE);
- hide_button->set_expand(true);
+ hide_button->set_ignore_texture_size(true);
hide_button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
hide_button->connect("pressed", callable_mp(this, &FindBar::_hide_bar));
}
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index 19da6686a5..29bf22a478 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -529,8 +529,8 @@ TreeItem *EditorHelpSearch::Runner::_create_class_hierarchy(const ClassMatch &p_
TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const DocData::ClassDoc *p_doc, bool p_gray) {
Ref<Texture2D> icon = empty_icon;
- if (ui_service->has_theme_icon(p_doc->name, "EditorIcons")) {
- icon = ui_service->get_theme_icon(p_doc->name, "EditorIcons");
+ if (ui_service->has_theme_icon(p_doc->name, SNAME("EditorIcons"))) {
+ icon = ui_service->get_theme_icon(p_doc->name, SNAME("EditorIcons"));
} else if (ClassDB::class_exists(p_doc->name) && ClassDB::is_parent_class(p_doc->name, "Object")) {
icon = ui_service->get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
}
@@ -610,11 +610,6 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons
text = p_text;
} else {
icon = ui_service->get_theme_icon(p_icon, SNAME("EditorIcons"));
- /*// In flat mode, show the class icon.
-if (ui_service->has_icon(p_class_name, "EditorIcons"))
-icon = ui_service->get_icon(p_class_name, "EditorIcons");
-else if (ClassDB::is_parent_class(p_class_name, "Object"))
-icon = ui_service->get_icon("Object", "EditorIcons");*/
text = p_class_name + "." + p_text;
}
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 6f7508b10b..cbfd6ae6de 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -251,7 +251,7 @@ void EditorProperty::_notification(int p_what) {
} else {
color = get_theme_color(is_read_only() ? SNAME("readonly_color") : SNAME("property_color"));
}
- if (label.find(".") != -1) {
+ if (label.contains(".")) {
// FIXME: Move this to the project settings editor, as this is only used
// for project settings feature tag overrides.
color.a = 0.5;
@@ -1441,11 +1441,11 @@ void EditorInspectorArray::_add_button_pressed() {
}
void EditorInspectorArray::_first_page_button_pressed() {
- emit_signal("page_change_request", 0);
+ emit_signal(SNAME("page_change_request"), 0);
}
void EditorInspectorArray::_prev_page_button_pressed() {
- emit_signal("page_change_request", MAX(0, page - 1));
+ emit_signal(SNAME("page_change_request"), MAX(0, page - 1));
}
void EditorInspectorArray::_page_line_edit_text_submitted(String p_text) {
@@ -1453,18 +1453,18 @@ void EditorInspectorArray::_page_line_edit_text_submitted(String p_text) {
int new_page = p_text.to_int() - 1;
new_page = MIN(MAX(0, new_page), max_page);
page_line_edit->set_text(Variant(new_page));
- emit_signal("page_change_request", new_page);
+ emit_signal(SNAME("page_change_request"), new_page);
} else {
page_line_edit->set_text(Variant(page));
}
}
void EditorInspectorArray::_next_page_button_pressed() {
- emit_signal("page_change_request", MIN(max_page, page + 1));
+ emit_signal(SNAME("page_change_request"), MIN(max_page, page + 1));
}
void EditorInspectorArray::_last_page_button_pressed() {
- emit_signal("page_change_request", max_page);
+ emit_signal(SNAME("page_change_request"), max_page);
}
void EditorInspectorArray::_rmb_popup_id_pressed(int p_id) {
@@ -1532,7 +1532,7 @@ void EditorInspectorArray::_vbox_visibility_changed() {
void EditorInspectorArray::_panel_draw(int p_index) {
ERR_FAIL_INDEX(p_index, (int)array_elements.size());
- Ref<StyleBox> style = get_theme_stylebox("Focus", "EditorStyles");
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"));
if (!style.is_valid()) {
return;
}
@@ -1636,12 +1636,12 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) {
// Handle page change and update counts.
if (p_element_index < 0) {
int added_index = p_to_pos < 0 ? count : p_to_pos;
- emit_signal("page_change_request", added_index / page_lenght);
+ emit_signal(SNAME("page_change_request"), added_index / page_lenght);
count += 1;
} else if (p_to_pos < 0) {
count -= 1;
if (page == max_page && (MAX(0, count - 1) / page_lenght != max_page)) {
- emit_signal("page_change_request", max_page - 1);
+ emit_signal(SNAME("page_change_request"), max_page - 1);
}
}
begin_array_index = page * page_lenght;
@@ -1689,7 +1689,7 @@ void EditorInspectorArray::_clear_array() {
undo_redo->commit_action();
// Handle page change and update counts.
- emit_signal("page_change_request", 0);
+ emit_signal(SNAME("page_change_request"), 0);
count = 0;
begin_array_index = 0;
end_array_index = 0;
@@ -1762,7 +1762,7 @@ void EditorInspectorArray::_resize_array(int p_size) {
undo_redo->commit_action();
// Handle page change and update counts.
- emit_signal("page_change_request", 0);
+ emit_signal(SNAME("page_change_request"), 0);
/*
count = 0;
begin_array_index = 0;
@@ -1780,7 +1780,7 @@ Array EditorInspectorArray::_extract_properties_as_array(const List<PropertyInfo
int to_char_index = 0;
while (to_char_index < str.length()) {
- if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ if (!is_digit(str[to_char_index])) {
break;
}
to_char_index++;
@@ -1889,7 +1889,7 @@ void EditorInspectorArray::_setup() {
ae.margin = memnew(MarginContainer);
ae.margin->set_mouse_filter(MOUSE_FILTER_PASS);
if (is_inside_tree()) {
- Size2 min_size = get_theme_stylebox("Focus", "EditorStyles")->get_minimum_size();
+ Size2 min_size = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->get_minimum_size();
ae.margin->add_theme_constant_override("margin_left", min_size.x / 2);
ae.margin->add_theme_constant_override("margin_top", min_size.y / 2);
ae.margin->add_theme_constant_override("margin_right", min_size.x / 2);
@@ -1988,7 +1988,7 @@ void EditorInspectorArray::_notification(int p_what) {
ArrayElement &ae = array_elements[i];
ae.move_texture_rect->set_texture(get_theme_icon(SNAME("TripleBar"), SNAME("EditorIcons")));
- Size2 min_size = get_theme_stylebox("Focus", "EditorStyles")->get_minimum_size();
+ Size2 min_size = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->get_minimum_size();
ae.margin->add_theme_constant_override("margin_left", min_size.x / 2);
ae.margin->add_theme_constant_override("margin_top", min_size.y / 2);
ae.margin->add_theme_constant_override("margin_right", min_size.x / 2);
@@ -2425,7 +2425,7 @@ void EditorInspector::update_tree() {
if (!ClassDB::class_exists(type) && !ScriptServer::is_global_class(type) && p.hint_string.length() && FileAccess::exists(p.hint_string)) {
// If we have a category inside a script, search for the first script with a valid icon.
Ref<Script> script = ResourceLoader::load(p.hint_string, "Script");
- String base_type;
+ StringName base_type;
if (script.is_valid()) {
base_type = script->get_instance_base_type();
}
@@ -2512,7 +2512,7 @@ void EditorInspector::update_tree() {
String str = p.name.trim_prefix(array_prefix);
int to_char_index = 0;
while (to_char_index < str.length()) {
- if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ if (!is_digit(str[to_char_index])) {
break;
}
to_char_index++;
@@ -2566,7 +2566,7 @@ void EditorInspector::update_tree() {
}
// Get the property label's string.
- String property_label_string = (path.find("/") != -1) ? path.substr(path.rfind("/") + 1) : path;
+ String property_label_string = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path;
if (capitalize_paths) {
// Capitalize paths.
int dot = property_label_string.find(".");
@@ -2590,7 +2590,7 @@ void EditorInspector::update_tree() {
// Ignore properties that do not fit the filter.
if (use_filter && !filter.is_empty()) {
- if (!filter.is_subsequence_ofn(path) && !filter.is_subsequence_ofn(property_label_string) && property_prefix.to_lower().find(filter.to_lower()) == -1) {
+ if (!filter.is_subsequence_ofn(path) && !filter.is_subsequence_ofn(property_label_string) && !property_prefix.to_lower().contains(filter.to_lower())) {
continue;
}
}
@@ -2664,7 +2664,7 @@ void EditorInspector::update_tree() {
array_element_prefix = p.class_name;
editor_inspector_array = memnew(EditorInspectorArray);
- String array_label = (path.find("/") != -1) ? path.substr(path.rfind("/") + 1) : path;
+ String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path;
array_label = property_label_string.capitalize();
int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0;
editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding);
@@ -2903,7 +2903,7 @@ void EditorInspector::edit(Object *p_object) {
object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback));
update_tree();
}
- emit_signal("edited_object_changed");
+ emit_signal(SNAME("edited_object_changed"));
}
void EditorInspector::set_keying(bool p_active) {
@@ -3020,7 +3020,7 @@ void EditorInspector::_update_inspector_bg() {
n = n->get_parent();
}
count_subinspectors = MIN(15, count_subinspectors);
- add_theme_style_override("bg", get_theme_stylebox("sub_inspector_bg" + itos(count_subinspectors), "Editor"));
+ add_theme_style_override("bg", get_theme_stylebox("sub_inspector_bg" + itos(count_subinspectors), SNAME("Editor")));
} else {
add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
}
@@ -3479,10 +3479,14 @@ void EditorInspector::_update_script_class_properties(const Object &p_object, Li
String path = s->get_path();
String name = EditorNode::get_editor_data().script_class_get_name(path);
if (name.is_empty()) {
- if (!s->is_built_in()) {
- name = path.get_file();
+ if (s->is_built_in()) {
+ if (s->get_name().is_empty()) {
+ name = TTR("Built-in script");
+ } else {
+ name = vformat("%s (%s)", s->get_name(), TTR("Built-in"));
+ }
} else {
- name = TTR("Built-in script");
+ name = path.get_file();
}
}
diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp
index 5c4ece7065..48a326d6d4 100644
--- a/editor/editor_locale_dialog.cpp
+++ b/editor/editor_locale_dialog.cpp
@@ -37,14 +37,6 @@
#include "scene/gui/option_button.h"
#include "scene/gui/tree.h"
-static _FORCE_INLINE_ bool is_upper_case(char32_t c) {
- return (c >= 'A' && c <= 'Z');
-}
-
-static _FORCE_INLINE_ bool is_lower_case(char32_t c) {
- return (c >= 'a' && c <= 'z');
-}
-
void EditorLocaleDialog::_bind_methods() {
ADD_SIGNAL(MethodInfo("locale_selected", PropertyInfo(Variant::STRING, "locale")));
}
@@ -363,16 +355,16 @@ void EditorLocaleDialog::set_locale(const String &p_locale) {
Vector<String> locale_elements = p_locale.split("_");
lang_code->set_text(locale_elements[0]);
if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
script_code->set_text(locale_elements[1]);
advanced->set_pressed(true);
}
- if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) {
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
country_code->set_text(locale_elements[1]);
}
}
if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
country_code->set_text(locale_elements[2]);
} else {
variant_code->set_text(locale_elements[2].to_lower());
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 076a774efa..4b2f1c5104 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -47,7 +47,6 @@
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "main/main.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/gui/center_container.h"
@@ -128,10 +127,10 @@
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "editor/plugins/asset_library_editor_plugin.h"
#include "editor/plugins/audio_stream_editor_plugin.h"
+#include "editor/plugins/audio_stream_randomizer_editor_plugin.h"
#include "editor/plugins/camera_3d_editor_plugin.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "editor/plugins/collision_polygon_2d_editor_plugin.h"
-#include "editor/plugins/collision_polygon_3d_editor_plugin.h"
#include "editor/plugins/collision_shape_2d_editor_plugin.h"
#include "editor/plugins/cpu_particles_2d_editor_plugin.h"
#include "editor/plugins/cpu_particles_3d_editor_plugin.h"
@@ -162,6 +161,8 @@
#include "editor/plugins/path_3d_editor_plugin.h"
#include "editor/plugins/physical_bone_3d_editor_plugin.h"
#include "editor/plugins/polygon_2d_editor_plugin.h"
+#include "editor/plugins/polygon_3d_editor_plugin.h"
+#include "editor/plugins/replication_editor_plugin.h"
#include "editor/plugins/resource_preloader_editor_plugin.h"
#include "editor/plugins/root_motion_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
@@ -312,7 +313,7 @@ void EditorNode::_update_scene_tabs() {
DisplayServer::get_singleton()->global_menu_clear("_dock");
}
- // Get all scene names, which may be ambiguous
+ // Get all scene names, which may be ambiguous.
Vector<String> disambiguated_scene_names;
Vector<String> full_path_names;
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
@@ -349,34 +350,35 @@ void EditorNode::_update_scene_tabs() {
DisplayServer::get_singleton()->global_menu_add_item("_dock", TTR("New Window"), callable_mp(this, &EditorNode::_global_menu_new_window));
}
- scene_tabs->set_current_tab(editor_data.get_edited_scene());
+ if (scene_tabs->get_tab_count() > 0) {
+ scene_tabs->set_current_tab(editor_data.get_edited_scene());
+ }
if (scene_tabs->get_offset_buttons_visible()) {
- // move add button to fixed position on the tabbar
+ // Move the add button to a fixed position.
if (scene_tab_add->get_parent() == scene_tabs) {
- if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_position(Point2(tabbar_container->get_size().x - scene_tab_add->get_size().x, 0));
- } else {
- scene_tab_add->set_position(Point2(0, 0));
- }
scene_tabs->remove_child(scene_tab_add);
- tabbar_container->add_child(scene_tab_add);
- tabbar_container->move_child(scene_tab_add, 1);
+ scene_tab_add_ph->add_child(scene_tab_add);
+ scene_tab_add->set_position(Point2());
}
} else {
- // move add button to after last tab
- if (scene_tab_add->get_parent() == tabbar_container) {
- tabbar_container->remove_child(scene_tab_add);
+ // Move the add button to be after the last tab.
+ if (scene_tab_add->get_parent() == scene_tab_add_ph) {
+ scene_tab_add_ph->remove_child(scene_tab_add);
scene_tabs->add_child(scene_tab_add);
}
- Rect2 last_tab = Rect2();
- if (scene_tabs->get_tab_count() != 0) {
- last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
+
+ if (scene_tabs->get_tab_count() == 0) {
+ scene_tab_add->set_position(Point2());
+ return;
}
+
+ Rect2 last_tab = scene_tabs->get_tab_rect(scene_tabs->get_tab_count() - 1);
+ int hsep = scene_tabs->get_theme_constant(SNAME("hseparation"));
if (scene_tabs->is_layout_rtl()) {
- scene_tab_add->set_position(Point2(last_tab.get_position().x - scene_tab_add->get_size().x - 3, last_tab.get_position().y));
+ scene_tab_add->set_position(Point2(last_tab.position.x - scene_tab_add->get_size().x - hsep, last_tab.position.y));
} else {
- scene_tab_add->set_position(Point2(last_tab.get_position().x + last_tab.get_size().x + 3, last_tab.get_position().y));
+ scene_tab_add->set_position(Point2(last_tab.position.x + last_tab.size.width + hsep, last_tab.position.y));
}
}
}
@@ -624,6 +626,10 @@ void EditorNode::_notification(int p_what) {
editor_data.clear_edited_scenes();
} break;
+ case Control::NOTIFICATION_THEME_CHANGED: {
+ scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
+ } break;
+
case NOTIFICATION_READY: {
{
_initializing_addons = true;
@@ -720,8 +726,8 @@ void EditorNode::_notification(int p_what) {
if (icon.is_valid()) {
tb->set_icon(icon);
- } else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), "EditorIcons")) {
- tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), "EditorIcons"));
+ } else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))) {
+ tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), SNAME("EditorIcons")));
}
}
@@ -739,13 +745,12 @@ void EditorNode::_notification(int p_what) {
bottom_panel_raise->set_icon(gui_base->get_theme_icon(SNAME("ExpandBottomDock"), SNAME("EditorIcons")));
- // clear_button->set_icon(gui_base->get_icon("Close", "EditorIcons")); don't have access to that node. needs to become a class property
if (gui_base->is_layout_rtl()) {
- dock_tab_move_left->set_icon(theme->get_icon("Forward", "EditorIcons"));
- dock_tab_move_right->set_icon(theme->get_icon("Back", "EditorIcons"));
+ dock_tab_move_left->set_icon(theme->get_icon(SNAME("Forward"), SNAME("EditorIcons")));
+ dock_tab_move_right->set_icon(theme->get_icon(SNAME("Back"), SNAME("EditorIcons")));
} else {
- dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons"));
- dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons"));
+ dock_tab_move_left->set_icon(theme->get_icon(SNAME("Back"), SNAME("EditorIcons")));
+ dock_tab_move_right->set_icon(theme->get_icon(SNAME("Forward"), SNAME("EditorIcons")));
}
PopupMenu *p = help_menu->get_popup();
@@ -2274,7 +2279,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
if (main_plugin) {
// special case if use of external editor is true
Resource *current_res = Object::cast_to<Resource>(current_obj);
- if (main_plugin->get_name() == "Script" && current_obj->get_class_name() != StringName("VisualScript") && current_res && !current_res->is_built_in() && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
+ if (main_plugin->get_name() == "Script" && current_obj->is_class("VisualScript") && current_res && !current_res->is_built_in() && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
if (!changing_scene) {
main_plugin->edit(current_obj);
}
@@ -3174,8 +3179,8 @@ void EditorNode::add_editor_plugin(EditorPlugin *p_editor, bool p_config_changed
Ref<Texture2D> icon = p_editor->get_icon();
if (icon.is_valid()) {
tb->set_icon(icon);
- } else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), "EditorIcons")) {
- tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), "EditorIcons"));
+ } else if (singleton->gui_base->has_theme_icon(p_editor->get_name(), SNAME("EditorIcons"))) {
+ tb->set_icon(singleton->gui_base->get_theme_icon(p_editor->get_name(), SNAME("EditorIcons")));
}
tb->add_theme_font_override("font", singleton->gui_base->get_theme_font(SNAME("main_button_font"), SNAME("EditorFonts")));
@@ -3287,8 +3292,8 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
return;
}
- // Errors in the script cause the base_type to be an empty string.
- if (String(script->get_instance_base_type()) == "") {
+ // Errors in the script cause the base_type to be an empty StringName.
+ if (script->get_instance_base_type() == StringName()) {
show_warning(vformat(TTR("Unable to load addon script from path: '%s'. This might be due to a code error in that script.\nDisabling the addon at '%s' to prevent further errors."), script_path, p_addon));
_remove_plugin_from_enabled(p_addon);
return;
@@ -3931,7 +3936,7 @@ StringName EditorNode::get_object_custom_type_name(const Object *p_object) const
ERR_FAIL_COND_V(!p_object, StringName());
Ref<Script> script = p_object->get_script();
- if (script.is_null() && p_object->is_class("Script")) {
+ if (script.is_null() && Object::cast_to<Script>(p_object)) {
script = p_object;
}
@@ -4026,8 +4031,8 @@ Ref<Texture2D> EditorNode::get_object_icon(const Object *p_object, const String
return p_object->get_meta("_editor_icon");
}
- if (gui_base->has_theme_icon(p_object->get_class(), "EditorIcons")) {
- return gui_base->get_theme_icon(p_object->get_class(), "EditorIcons");
+ if (gui_base->has_theme_icon(p_object->get_class(), SNAME("EditorIcons"))) {
+ return gui_base->get_theme_icon(p_object->get_class(), SNAME("EditorIcons"));
}
if (p_fallback.length()) {
@@ -4056,7 +4061,7 @@ Ref<Texture2D> EditorNode::get_class_icon(const String &p_class, const String &p
}
if (icon.is_null()) {
- icon = gui_base->get_theme_icon(ScriptServer::get_global_class_base(name), "EditorIcons");
+ icon = gui_base->get_theme_icon(ScriptServer::get_global_class_base(name), SNAME("EditorIcons"));
}
}
@@ -4142,13 +4147,12 @@ Ref<Texture2D> EditorNode::_file_dialog_get_icon(const String &p_path) {
void EditorNode::_build_icon_type_cache() {
List<StringName> tl;
- StringName ei = "EditorIcons";
- theme_base->get_theme()->get_icon_list(ei, &tl);
+ theme_base->get_theme()->get_icon_list(SNAME("EditorIcons"), &tl);
for (const StringName &E : tl) {
if (!ClassDB::class_exists(E)) {
continue;
}
- icon_type_cache[E] = theme_base->get_theme()->get_icon(E, ei);
+ icon_type_cache[E] = theme_base->get_theme()->get_icon(E, SNAME("EditorIcons"));
}
}
@@ -4230,8 +4234,9 @@ void EditorNode::_dock_make_float() {
Control *dock = dock_slot[dock_popup_selected]->get_current_tab_control();
ERR_FAIL_COND(!dock);
- const Size2i borders = Size2i(4, 4) * EDSCALE;
- Size2 dock_size = dock->get_size() + borders * 2; // remember size
+ Size2 borders = Size2(4, 4) * EDSCALE;
+ // Remember size and position before removing it from the main window.
+ Size2 dock_size = dock->get_size() + borders * 2;
Point2 dock_screen_pos = dock->get_global_position() + get_tree()->get_root()->get_position() - borders;
int dock_index = dock->get_index();
@@ -4240,7 +4245,7 @@ void EditorNode::_dock_make_float() {
Window *window = memnew(Window);
window->set_title(dock->get_name());
Panel *p = memnew(Panel);
- p->set_mode(Panel::MODE_FOREGROUND);
+ p->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("PanelForeground"), SNAME("EditorStyles")));
p->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
window->add_child(p);
MarginContainer *margin = memnew(MarginContainer);
@@ -5598,7 +5603,7 @@ void EditorNode::_update_rendering_driver_color() {
if (rendering_driver->get_text() == "opengl3") {
rendering_driver->add_theme_color_override("font_color", Color::hex(0x5586a4ff));
} else if (rendering_driver->get_text() == "vulkan") {
- rendering_driver->add_theme_color_override("font_color", theme_base->get_theme_color("vulkan_color", "Editor"));
+ rendering_driver->add_theme_color_override("font_color", theme_base->get_theme_color(SNAME("vulkan_color"), SNAME("Editor")));
}
}
@@ -5671,6 +5676,7 @@ void EditorNode::_feature_profile_changed() {
void EditorNode::_bind_methods() {
GLOBAL_DEF("editor/scene/scene_naming", SCENE_NAME_CASING_SNAKE_CASE);
ProjectSettings::get_singleton()->set_custom_property_info("editor/scene/scene_naming", PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"));
+ ClassDB::bind_method("edit_current", &EditorNode::edit_current);
ClassDB::bind_method("_editor_select", &EditorNode::_editor_select);
ClassDB::bind_method("_node_renamed", &EditorNode::_node_renamed);
ClassDB::bind_method("edit_node", &EditorNode::edit_node);
@@ -6141,9 +6147,9 @@ EditorNode::EditorNode() {
dock_tab_move_left = memnew(Button);
dock_tab_move_left->set_flat(true);
if (gui_base->is_layout_rtl()) {
- dock_tab_move_left->set_icon(theme->get_icon("Forward", "EditorIcons"));
+ dock_tab_move_left->set_icon(theme->get_icon(SNAME("Forward"), SNAME("EditorIcons")));
} else {
- dock_tab_move_left->set_icon(theme->get_icon("Back", "EditorIcons"));
+ dock_tab_move_left->set_icon(theme->get_icon(SNAME("Back"), SNAME("EditorIcons")));
}
dock_tab_move_left->set_focus_mode(Control::FOCUS_NONE);
dock_tab_move_left->connect("pressed", callable_mp(this, &EditorNode::_dock_move_left));
@@ -6158,9 +6164,9 @@ EditorNode::EditorNode() {
dock_tab_move_right = memnew(Button);
dock_tab_move_right->set_flat(true);
if (gui_base->is_layout_rtl()) {
- dock_tab_move_right->set_icon(theme->get_icon("Back", "EditorIcons"));
+ dock_tab_move_right->set_icon(theme->get_icon(SNAME("Back"), SNAME("EditorIcons")));
} else {
- dock_tab_move_right->set_icon(theme->get_icon("Forward", "EditorIcons"));
+ dock_tab_move_right->set_icon(theme->get_icon(SNAME("Forward"), SNAME("EditorIcons")));
}
dock_tab_move_right->set_focus_mode(Control::FOCUS_NONE);
dock_tab_move_right->connect("pressed", callable_mp(this, &EditorNode::_dock_move_right));
@@ -6227,6 +6233,9 @@ EditorNode::EditorNode() {
tab_preview->set_position(Point2(2, 2) * EDSCALE);
tab_preview_panel->add_child(tab_preview);
+ tabbar_container = memnew(HBoxContainer);
+ srt->add_child(tabbar_container);
+
scene_tabs = memnew(TabBar);
scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles")));
scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles")));
@@ -6244,16 +6253,26 @@ EditorNode::EditorNode() {
scene_tabs->connect("gui_input", callable_mp(this, &EditorNode::_scene_tab_input));
scene_tabs->connect("active_tab_rearranged", callable_mp(this, &EditorNode::_reposition_active_tab));
scene_tabs->connect("resized", callable_mp(this, &EditorNode::_update_scene_tabs));
-
- tabbar_container = memnew(HBoxContainer);
scene_tabs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tabbar_container->add_child(scene_tabs);
scene_tabs_context_menu = memnew(PopupMenu);
tabbar_container->add_child(scene_tabs_context_menu);
scene_tabs_context_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option));
- srt->add_child(tabbar_container);
- tabbar_container->add_child(scene_tabs);
+ scene_tab_add = memnew(Button);
+ scene_tab_add->set_flat(true);
+ scene_tab_add->set_tooltip(TTR("Add a new scene."));
+ scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f));
+ scene_tabs->add_child(scene_tab_add);
+ scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(FILE_NEW_SCENE));
+
+ scene_tab_add_ph = memnew(Control);
+ scene_tab_add_ph->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
+ scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size());
+ tabbar_container->add_child(scene_tab_add_ph);
+
distraction_free = memnew(Button);
distraction_free->set_flat(true);
ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F11);
@@ -6263,15 +6282,7 @@ EditorNode::EditorNode() {
distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode));
distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")));
distraction_free->set_toggle_mode(true);
-
- scene_tab_add = memnew(Button);
- scene_tab_add->set_flat(true);
- tabbar_container->add_child(scene_tab_add);
tabbar_container->add_child(distraction_free);
- scene_tab_add->set_tooltip(TTR("Add a new scene."));
- scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f));
- scene_tab_add->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(FILE_NEW_SCENE));
scene_root_parent = memnew(PanelContainer);
scene_root_parent->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
@@ -6626,8 +6637,8 @@ EditorNode::EditorNode() {
rendering_driver->set_flat(true);
rendering_driver->set_focus_mode(Control::FOCUS_NONE);
rendering_driver->connect("item_selected", callable_mp(this, &EditorNode::_rendering_driver_selected));
- rendering_driver->add_theme_font_override("font", gui_base->get_theme_font("bold", "EditorFonts"));
- rendering_driver->add_theme_font_size_override("font_size", gui_base->get_theme_font_size("bold_size", "EditorFonts"));
+ rendering_driver->add_theme_font_override("font", gui_base->get_theme_font(SNAME("bold"), SNAME("EditorFonts")));
+ rendering_driver->add_theme_font_size_override("font_size", gui_base->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")));
right_menu_hb->add_child(rendering_driver);
@@ -6958,6 +6969,7 @@ EditorNode::EditorNode() {
// more visually meaningful to have this later
raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
+ add_editor_plugin(memnew(ReplicationEditorPlugin(this)));
add_editor_plugin(VersionControlEditorPlugin::get_singleton());
add_editor_plugin(memnew(ShaderEditorPlugin(this)));
@@ -7001,6 +7013,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(TextureLayeredEditorPlugin(this)));
add_editor_plugin(memnew(Texture3DEditorPlugin(this)));
add_editor_plugin(memnew(AudioStreamEditorPlugin(this)));
+ add_editor_plugin(memnew(AudioStreamRandomizerEditorPlugin(this)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(Skeleton3DEditorPlugin(this)));
add_editor_plugin(memnew(SkeletonIK3DEditorPlugin(this)));
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 5c013a18d9..8d322a1bfd 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -384,6 +384,7 @@ private:
HBoxContainer *tabbar_container;
Button *distraction_free;
Button *scene_tab_add;
+ Control *scene_tab_add_ph;
bool scene_distraction;
bool script_distraction;
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index a1c031aa1b..fe44486d0d 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -717,9 +717,9 @@ void EditorPlugin::remove_translation_parser_plugin(const Ref<EditorTranslationP
EditorTranslationParser::get_singleton()->remove_parser(p_parser, EditorTranslationParser::CUSTOM);
}
-void EditorPlugin::add_import_plugin(const Ref<EditorImportPlugin> &p_importer) {
+void EditorPlugin::add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority) {
ERR_FAIL_COND(!p_importer.is_valid());
- ResourceFormatImporter::get_singleton()->add_importer(p_importer);
+ ResourceFormatImporter::get_singleton()->add_importer(p_importer, p_first_priority);
EditorFileSystem::get_singleton()->call_deferred(SNAME("scan"));
}
@@ -759,9 +759,9 @@ void EditorPlugin::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_p
EditorInspector::remove_inspector_plugin(p_plugin);
}
-void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
+void EditorPlugin::add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority) {
ERR_FAIL_COND(!p_importer.is_valid());
- ResourceImporterScene::get_singleton()->add_importer(p_importer);
+ ResourceImporterScene::get_singleton()->add_importer(p_importer, p_first_priority);
}
void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer) {
@@ -769,9 +769,10 @@ void EditorPlugin::remove_scene_format_importer_plugin(const Ref<EditorSceneForm
ResourceImporterScene::get_singleton()->remove_importer(p_importer);
}
-void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
- ResourceImporterScene::get_singleton()->add_post_importer_plugin(p_plugin);
+void EditorPlugin::add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {
+ ResourceImporterScene::get_singleton()->add_post_importer_plugin(p_plugin, p_first_priority);
}
+
void EditorPlugin::remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
ResourceImporterScene::get_singleton()->remove_post_importer_plugin(p_plugin);
}
@@ -881,11 +882,11 @@ void EditorPlugin::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout);
ClassDB::bind_method(D_METHOD("add_translation_parser_plugin", "parser"), &EditorPlugin::add_translation_parser_plugin);
ClassDB::bind_method(D_METHOD("remove_translation_parser_plugin", "parser"), &EditorPlugin::remove_translation_parser_plugin);
- ClassDB::bind_method(D_METHOD("add_import_plugin", "importer"), &EditorPlugin::add_import_plugin);
+ ClassDB::bind_method(D_METHOD("add_import_plugin", "importer", "first_priority"), &EditorPlugin::add_import_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_import_plugin", "importer"), &EditorPlugin::remove_import_plugin);
- ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::add_scene_format_importer_plugin);
+ ClassDB::bind_method(D_METHOD("add_scene_format_importer_plugin", "scene_format_importer", "first_priority"), &EditorPlugin::add_scene_format_importer_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_scene_format_importer_plugin", "scene_format_importer"), &EditorPlugin::remove_scene_format_importer_plugin);
- ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::add_scene_post_import_plugin);
+ ClassDB::bind_method(D_METHOD("add_scene_post_import_plugin", "scene_import_plugin", "first_priority"), &EditorPlugin::add_scene_post_import_plugin, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_scene_post_import_plugin", "scene_import_plugin"), &EditorPlugin::remove_scene_post_import_plugin);
ClassDB::bind_method(D_METHOD("add_export_plugin", "plugin"), &EditorPlugin::add_export_plugin);
ClassDB::bind_method(D_METHOD("remove_export_plugin", "plugin"), &EditorPlugin::remove_export_plugin);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 06517190f6..f23c5f40f6 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -278,7 +278,7 @@ public:
void add_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
void remove_translation_parser_plugin(const Ref<EditorTranslationParserPlugin> &p_parser);
- void add_import_plugin(const Ref<EditorImportPlugin> &p_importer);
+ void add_import_plugin(const Ref<EditorImportPlugin> &p_importer, bool p_first_priority = false);
void remove_import_plugin(const Ref<EditorImportPlugin> &p_importer);
void add_export_plugin(const Ref<EditorExportPlugin> &p_exporter);
@@ -290,10 +290,10 @@ public:
void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
- void add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
+ void add_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer, bool p_first_priority = false);
void remove_scene_format_importer_plugin(const Ref<EditorSceneFormatImporter> &p_importer);
- void add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
+ void add_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer, bool p_first_priority = false);
void remove_scene_post_import_plugin(const Ref<EditorScenePostImportPlugin> &p_importer);
void add_autoload_singleton(const String &p_name, const String &p_path);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 5ff83649c0..ef1ceebabf 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -592,7 +592,7 @@ void EditorPropertyMember::_property_select() {
} else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) {
Variant::Type type = Variant::NIL;
String tname = hint_text;
- if (tname.find(".") != -1) {
+ if (tname.contains(".")) {
tname = tname.get_slice(".", 0);
}
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@@ -809,7 +809,7 @@ void EditorPropertyLayersGrid::_rename_operation_confirm() {
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
- } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
+ } else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
}
@@ -2883,7 +2883,7 @@ void EditorPropertyNodePath::update_property() {
Node *target_node = base_node->get_node(p);
ERR_FAIL_COND(!target_node);
- if (String(target_node->get_name()).find("@") != -1) {
+ if (String(target_node->get_name()).contains("@")) {
assign->set_icon(Ref<Texture2D>());
assign->set_text(p);
return;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 71a855b22c..c28c7db2cb 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -502,6 +502,16 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d
}
void EditorPropertyArray::_notification(int p_what) {
+ if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
+ change_type->clear();
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ String type = Variant::get_type_name(Variant::Type(i));
+ change_type->add_icon_item(get_theme_icon(type, SNAME("EditorIcons")), type, i);
+ }
+ change_type->add_separator();
+ change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX);
+ }
+
if (p_what == NOTIFICATION_DRAG_BEGIN) {
if (is_visible_in_tree()) {
if (_is_drop_valid(get_viewport()->gui_get_drag_data())) {
@@ -648,7 +658,7 @@ void EditorPropertyArray::_reorder_button_up() {
Variant array = object->get_array();
Variant value_to_move = array.get(reorder_from_index);
- array.call("remove", reorder_from_index);
+ array.call("remove_at", reorder_from_index);
array.call("insert", reorder_to_index, value_to_move);
emit_changed(get_edited_property(), array, "", false);
@@ -691,13 +701,6 @@ EditorPropertyArray::EditorPropertyArray() {
change_type = memnew(PopupMenu);
add_child(change_type);
change_type->connect("id_pressed", callable_mp(this, &EditorPropertyArray::_change_type_menu));
-
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- String type = Variant::get_type_name(Variant::Type(i));
- change_type->add_item(type, i);
- }
- change_type->add_separator();
- change_type->add_item(TTR("Remove Item"), Variant::VARIANT_MAX);
changing_type_index = -1;
subtype = Variant::NIL;
@@ -1119,10 +1122,11 @@ void EditorPropertyDictionary::update_property() {
prop->update_property();
if (i == amount + 1) {
- Button *butt_add_item = memnew(Button);
- butt_add_item->set_text(TTR("Add Key/Value Pair"));
- butt_add_item->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_add_key_value));
- add_vbox->add_child(butt_add_item);
+ button_add_item = memnew(Button);
+ button_add_item->set_text(TTR("Add Key/Value Pair"));
+ button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ button_add_item->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_add_key_value));
+ add_vbox->add_child(button_add_item);
}
}
@@ -1142,6 +1146,19 @@ void EditorPropertyDictionary::_object_id_selected(const StringName &p_property,
}
void EditorPropertyDictionary::_notification(int p_what) {
+ if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
+ change_type->clear();
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ String type = Variant::get_type_name(Variant::Type(i));
+ change_type->add_icon_item(get_theme_icon(type, SNAME("EditorIcons")), type, i);
+ }
+ change_type->add_separator();
+ change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX);
+
+ if (Object::cast_to<Button>(button_add_item)) {
+ button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ }
+ }
}
void EditorPropertyDictionary::_edit_pressed() {
@@ -1179,16 +1196,10 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
add_focusable(edit);
vbox = nullptr;
page_slider = nullptr;
+ button_add_item = nullptr;
updating = false;
change_type = memnew(PopupMenu);
add_child(change_type);
change_type->connect("id_pressed", callable_mp(this, &EditorPropertyDictionary::_change_type_menu));
-
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- String type = Variant::get_type_name(Variant::Type(i));
- change_type->add_item(type, i);
- }
- change_type->add_separator();
- change_type->add_item(TTR("Remove Item"), Variant::VARIANT_MAX);
changing_type_index = -1;
}
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 4c31ba0b49..bb10cdf97e 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -147,6 +147,7 @@ class EditorPropertyDictionary : public EditorProperty {
EditorSpinSlider *size_slider;
EditorSpinSlider *page_slider;
HBoxContainer *page_hbox;
+ Button *button_add_item;
void _page_changed(double p_page);
void _edit_pressed();
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 716643f812..398a096550 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -77,16 +77,16 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const
return;
}
- String type = edited_resource->get_class_name();
- if (ClassDB::is_parent_class(type, "Script")) {
- assign_button->set_text(edited_resource->get_path().get_file());
+ Ref<Script> script = edited_resource;
+ if (script.is_valid()) {
+ assign_button->set_text(script->get_path().get_file());
return;
}
if (p_preview.is_valid()) {
preview_rect->set_offset(SIDE_LEFT, assign_button->get_icon()->get_width() + assign_button->get_theme_stylebox(SNAME("normal"))->get_default_margin(SIDE_LEFT) + get_theme_constant(SNAME("hseparation"), SNAME("Button")));
- if (type == "GradientTexture1D") {
+ if (Ref<GradientTexture1D>(edited_resource).is_valid()) {
preview_rect->set_stretch_mode(TextureRect::STRETCH_SCALE);
assign_button->set_custom_minimum_size(Size2(1, 1));
} else {
@@ -377,6 +377,8 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
Resource *resp = Object::cast_to<Resource>(obj);
ERR_BREAK(!resp);
+ EditorNode::get_editor_data().instantiate_object_properties(obj);
+
edited_resource = RES(resp);
emit_signal(SNAME("resource_changed"), edited_resource);
_update_resource();
@@ -640,7 +642,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
for (Set<String>::Element *E = allowed_types.front(); E; E = E->next()) {
String at = E->get().strip_edges();
- if (at == "BaseMaterial3D" && ClassDB::is_parent_class(dropped_resource->get_class(), "Texture2D")) {
+ if (at == "BaseMaterial3D" && Ref<Texture2D>(dropped_resource).is_valid()) {
// Use existing resource if possible and only replace its data.
Ref<StandardMaterial3D> mat = edited_resource;
if (!mat.is_valid()) {
@@ -651,7 +653,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
break;
}
- if (at == "ShaderMaterial" && ClassDB::is_parent_class(dropped_resource->get_class(), "Shader")) {
+ if (at == "ShaderMaterial" && Ref<Shader>(dropped_resource).is_valid()) {
Ref<ShaderMaterial> mat = edited_resource;
if (!mat.is_valid()) {
mat.instantiate();
@@ -661,7 +663,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
break;
}
- if (at == "Font" && ClassDB::is_parent_class(dropped_resource->get_class(), "FontData")) {
+ if (at == "Font" && Ref<FontData>(dropped_resource).is_valid()) {
Ref<Font> font = edited_resource;
if (!font.is_valid()) {
font.instantiate();
@@ -671,7 +673,7 @@ void EditorResourcePicker::drop_data_fw(const Point2 &p_point, const Variant &p_
break;
}
- if (at == "Texture2D" && ClassDB::is_parent_class(dropped_resource->get_class(), "Image")) {
+ if (at == "Texture2D" && Ref<Image>(dropped_resource).is_valid()) {
Ref<ImageTexture> texture = edited_resource;
if (!texture.is_valid()) {
texture.instantiate();
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index 6853045345..1bab56ac4a 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -89,7 +89,7 @@ class SectionedInspectorFilter : public Object {
if (pi.name.begins_with(section + "/")) {
pi.name = pi.name.replace_first(section + "/", "");
- if (!allow_sub && pi.name.find("/") != -1) {
+ if (!allow_sub && pi.name.contains("/")) {
continue;
}
p_list->push_back(pi);
@@ -227,7 +227,7 @@ void SectionedInspector::update_category_list() {
continue;
}
- if (pi.name.find(":") != -1 || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) {
+ if (pi.name.contains(":") || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) {
continue;
}
diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp
index 2520d662c5..fc37590337 100644
--- a/editor/editor_settings_dialog.cpp
+++ b/editor/editor_settings_dialog.cpp
@@ -272,15 +272,15 @@ void EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, const S
shortcut_item->set_text(1, sc_text);
if (sc_text == "None") {
// Fade out unassigned shortcut labels for easier visual grepping.
- shortcut_item->set_custom_color(1, shortcuts->get_theme_color("font_color", "Label") * Color(1, 1, 1, 0.5));
+ shortcut_item->set_custom_color(1, shortcuts->get_theme_color(SNAME("font_color"), SNAME("Label")) * Color(1, 1, 1, 0.5));
}
if (p_allow_revert) {
- shortcut_item->add_button(1, shortcuts->get_theme_icon("Reload", "EditorIcons"), SHORTCUT_REVERT);
+ shortcut_item->add_button(1, shortcuts->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")), SHORTCUT_REVERT);
}
- shortcut_item->add_button(1, shortcuts->get_theme_icon("Add", "EditorIcons"), SHORTCUT_ADD);
- shortcut_item->add_button(1, shortcuts->get_theme_icon("Close", "EditorIcons"), SHORTCUT_ERASE);
+ shortcut_item->add_button(1, shortcuts->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), SHORTCUT_ADD);
+ shortcut_item->add_button(1, shortcuts->get_theme_icon(SNAME("Close"), SNAME("EditorIcons")), SHORTCUT_ERASE);
shortcut_item->set_meta("is_action", p_is_action);
shortcut_item->set_meta("type", "shortcut");
@@ -299,11 +299,11 @@ void EditorSettingsDialog::_create_shortcut_treeitem(TreeItem *p_parent, const S
event_item->set_text(0, shortcut_item->get_child_count() == 1 ? "Primary" : "");
event_item->set_text(1, ie->as_text());
- event_item->add_button(1, shortcuts->get_theme_icon("Edit", "EditorIcons"), SHORTCUT_EDIT);
- event_item->add_button(1, shortcuts->get_theme_icon("Close", "EditorIcons"), SHORTCUT_ERASE);
+ event_item->add_button(1, shortcuts->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), SHORTCUT_EDIT);
+ event_item->add_button(1, shortcuts->get_theme_icon(SNAME("Close"), SNAME("EditorIcons")), SHORTCUT_ERASE);
- event_item->set_custom_bg_color(0, shortcuts->get_theme_color("dark_color_3", "Editor"));
- event_item->set_custom_bg_color(1, shortcuts->get_theme_color("dark_color_3", "Editor"));
+ event_item->set_custom_bg_color(0, shortcuts->get_theme_color(SNAME("dark_color_3"), SNAME("Editor")));
+ event_item->set_custom_bg_color(1, shortcuts->get_theme_color(SNAME("dark_color_3"), SNAME("Editor")));
event_item->set_meta("is_action", p_is_action);
event_item->set_meta("type", "event");
@@ -513,6 +513,38 @@ void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column
}
}
+void EditorSettingsDialog::_shortcut_cell_double_clicked() {
+ // When a shortcut cell is double clicked:
+ // If the cell has children and is in the bindings column, and if its first child is editable,
+ // then uncollapse the cell, and if the first child is the only child, then edit that child.
+ // If the cell is in the bindings column and can be edited, then edit it.
+ // If the cell is in the name column, then toggle collapse.
+ const ShortcutButton edit_btn_id = EditorSettingsDialog::SHORTCUT_EDIT;
+ const int edit_btn_col = 1;
+ TreeItem *ti = shortcuts->get_selected();
+ String type = ti->get_meta("type");
+ int col = shortcuts->get_selected_column();
+ if (type == "shortcut" && col == 0) {
+ if (ti->get_first_child()) {
+ ti->set_collapsed(!ti->is_collapsed());
+ }
+ } else if (type == "shortcut" && col == 1) {
+ if (ti->get_first_child()) {
+ TreeItem *child_ti = ti->get_first_child();
+ if (child_ti->get_button_by_id(edit_btn_col, edit_btn_id) != -1) {
+ ti->set_collapsed(false);
+ if (ti->get_child_count() == 1) {
+ _shortcut_button_pressed(child_ti, edit_btn_col, edit_btn_id);
+ }
+ }
+ }
+ } else if (type == "event" && col == 1) {
+ if (ti->get_button_by_id(edit_btn_col, edit_btn_id) != -1) {
+ _shortcut_button_pressed(ti, edit_btn_col, edit_btn_id);
+ }
+ }
+}
+
Variant EditorSettingsDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
TreeItem *selected = shortcuts->get_selected();
@@ -692,6 +724,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
shortcuts->set_column_title(0, TTR("Name"));
shortcuts->set_column_title(1, TTR("Binding"));
shortcuts->connect("button_pressed", callable_mp(this, &EditorSettingsDialog::_shortcut_button_pressed));
+ shortcuts->connect("item_activated", callable_mp(this, &EditorSettingsDialog::_shortcut_cell_double_clicked));
tab_shortcuts->add_child(shortcuts);
shortcuts->set_drag_forwarding(this);
diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h
index f1c4ea7770..c8858b4fcb 100644
--- a/editor/editor_settings_dialog.h
+++ b/editor/editor_settings_dialog.h
@@ -104,6 +104,7 @@ class EditorSettingsDialog : public AcceptDialog {
void _update_shortcuts();
void _shortcut_button_pressed(Object *p_item, int p_column, int p_idx);
+ void _shortcut_cell_double_clicked();
void _builtin_action_popup_index_pressed(int p_index);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 308a268e42..1e373239a6 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -280,9 +280,9 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
}
// These ones should be converted even if we are using a dark theme.
- const Color error_color = p_theme->get_color("error_color", "Editor");
- const Color success_color = p_theme->get_color("success_color", "Editor");
- const Color warning_color = p_theme->get_color("warning_color", "Editor");
+ const Color error_color = p_theme->get_color(SNAME("error_color"), SNAME("Editor"));
+ const Color success_color = p_theme->get_color(SNAME("success_color"), SNAME("Editor"));
+ const Color warning_color = p_theme->get_color(SNAME("warning_color"), SNAME("Editor"));
dark_icon_color_dictionary[Color::html("#ff5f5f")] = error_color;
dark_icon_color_dictionary[Color::html("#5fff97")] = success_color;
dark_icon_color_dictionary[Color::html("#ffdd65")] = warning_color;
@@ -299,7 +299,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
const int is_exception = exceptions.has(editor_icons_names[i]);
const Ref<ImageTexture> icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation, dark_icon_color_dictionary);
- p_theme->set_icon(editor_icons_names[i], "EditorIcons", icon);
+ p_theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), icon);
}
}
@@ -313,7 +313,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
const int is_exception = exceptions.has(editor_icons_names[index]);
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
- p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
+ p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon);
}
} else {
const float scale = (float)p_thumb_size / 32.0 * EDSCALE;
@@ -322,7 +322,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme =
const bool is_exception = exceptions.has(editor_icons_names[index]);
const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary);
- p_theme->set_icon(editor_icons_names[index], "EditorIcons", icon);
+ p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon);
}
}
#else
@@ -444,7 +444,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color highlight_color = Color(accent_color.r, accent_color.g, accent_color.b, 0.275);
const Color disabled_highlight_color = highlight_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5);
- float prev_icon_saturation = theme->has_color("icon_saturation", "Editor") ? theme->get_color("icon_saturation", "Editor").r : 1.0;
+ float prev_icon_saturation = theme->has_color(SNAME("icon_saturation"), SNAME("Editor")) ? theme->get_color(SNAME("icon_saturation"), SNAME("Editor")).r : 1.0;
theme->set_color("icon_saturation", "Editor", Color(icon_saturation, icon_saturation, icon_saturation)); // can't save single float in theme, so using color
theme->set_color("accent_color", "Editor", accent_color);
@@ -502,16 +502,16 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Register icons + font
// The resolution and the icon color (dark_theme bool) has not changed, so we do not regenerate the icons.
- if (p_theme != nullptr && fabs(p_theme->get_constant("scale", "Editor") - EDSCALE) < 0.00001 && (bool)p_theme->get_constant("dark_theme", "Editor") == dark_theme && prev_icon_saturation == icon_saturation) {
+ if (p_theme != nullptr && fabs(p_theme->get_constant(SNAME("scale"), SNAME("Editor")) - EDSCALE) < 0.00001 && (bool)p_theme->get_constant(SNAME("dark_theme"), SNAME("Editor")) == dark_theme && prev_icon_saturation == icon_saturation) {
// Register already generated icons.
for (int i = 0; i < editor_icons_count; i++) {
- theme->set_icon(editor_icons_names[i], "EditorIcons", p_theme->get_icon(editor_icons_names[i], "EditorIcons"));
+ theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), p_theme->get_icon(editor_icons_names[i], SNAME("EditorIcons")));
}
} else {
editor_register_and_generate_icons(theme, dark_theme, thumb_size, false, icon_saturation);
}
// Thumbnail size has changed, so we regenerate the medium sizes
- if (p_theme != nullptr && fabs((double)p_theme->get_constant("thumb_size", "Editor") - thumb_size) > 0.00001) {
+ if (p_theme != nullptr && fabs((double)p_theme->get_constant(SNAME("thumb_size"), SNAME("Editor")) - thumb_size) > 0.00001) {
editor_register_and_generate_icons(p_theme, dark_theme, thumb_size, true);
}
@@ -708,7 +708,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_disabled_color", "OptionButton", font_disabled_color);
theme->set_color("icon_hover_color", "OptionButton", icon_hover_color);
theme->set_color("icon_focus_color", "OptionButton", icon_focus_color);
- theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons"));
+ theme->set_icon("arrow", "OptionButton", theme->get_icon(SNAME("GuiOptionArrow"), SNAME("EditorIcons")));
theme->set_constant("arrow_margin", "OptionButton", widget_default_margin.x - 2 * EDSCALE);
theme->set_constant("modulate_arrow", "OptionButton", true);
theme->set_constant("hseparation", "OptionButton", 4 * EDSCALE);
@@ -719,15 +719,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("disabled", "CheckButton", style_menu);
theme->set_stylebox("hover", "CheckButton", style_menu);
- theme->set_icon("on", "CheckButton", theme->get_icon("GuiToggleOn", "EditorIcons"));
- theme->set_icon("on_disabled", "CheckButton", theme->get_icon("GuiToggleOnDisabled", "EditorIcons"));
- theme->set_icon("off", "CheckButton", theme->get_icon("GuiToggleOff", "EditorIcons"));
- theme->set_icon("off_disabled", "CheckButton", theme->get_icon("GuiToggleOffDisabled", "EditorIcons"));
+ theme->set_icon("on", "CheckButton", theme->get_icon(SNAME("GuiToggleOn"), SNAME("EditorIcons")));
+ theme->set_icon("on_disabled", "CheckButton", theme->get_icon(SNAME("GuiToggleOnDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("off", "CheckButton", theme->get_icon(SNAME("GuiToggleOff"), SNAME("EditorIcons")));
+ theme->set_icon("off_disabled", "CheckButton", theme->get_icon(SNAME("GuiToggleOffDisabled"), SNAME("EditorIcons")));
- theme->set_icon("on_mirrored", "CheckButton", theme->get_icon("GuiToggleOnMirrored", "EditorIcons"));
- theme->set_icon("on_disabled_mirrored", "CheckButton", theme->get_icon("GuiToggleOnDisabledMirrored", "EditorIcons"));
- theme->set_icon("off_mirrored", "CheckButton", theme->get_icon("GuiToggleOffMirrored", "EditorIcons"));
- theme->set_icon("off_disabled_mirrored", "CheckButton", theme->get_icon("GuiToggleOffDisabledMirrored", "EditorIcons"));
+ theme->set_icon("on_mirrored", "CheckButton", theme->get_icon(SNAME("GuiToggleOnMirrored"), SNAME("EditorIcons")));
+ theme->set_icon("on_disabled_mirrored", "CheckButton", theme->get_icon(SNAME("GuiToggleOnDisabledMirrored"), SNAME("EditorIcons")));
+ theme->set_icon("off_mirrored", "CheckButton", theme->get_icon(SNAME("GuiToggleOffMirrored"), SNAME("EditorIcons")));
+ theme->set_icon("off_disabled_mirrored", "CheckButton", theme->get_icon(SNAME("GuiToggleOffDisabledMirrored"), SNAME("EditorIcons")));
theme->set_color("font_color", "CheckButton", font_color);
theme->set_color("font_hover_color", "CheckButton", font_hover_color);
@@ -751,14 +751,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("pressed", "CheckBox", sb_checkbox);
theme->set_stylebox("disabled", "CheckBox", sb_checkbox);
theme->set_stylebox("hover", "CheckBox", sb_checkbox);
- theme->set_icon("checked", "CheckBox", theme->get_icon("GuiChecked", "EditorIcons"));
- theme->set_icon("unchecked", "CheckBox", theme->get_icon("GuiUnchecked", "EditorIcons"));
- theme->set_icon("radio_checked", "CheckBox", theme->get_icon("GuiRadioChecked", "EditorIcons"));
- theme->set_icon("radio_unchecked", "CheckBox", theme->get_icon("GuiRadioUnchecked", "EditorIcons"));
- theme->set_icon("checked_disabled", "CheckBox", theme->get_icon("GuiCheckedDisabled", "EditorIcons"));
- theme->set_icon("unchecked_disabled", "CheckBox", theme->get_icon("GuiUncheckedDisabled", "EditorIcons"));
- theme->set_icon("radio_checked_disabled", "CheckBox", theme->get_icon("GuiRadioCheckedDisabled", "EditorIcons"));
- theme->set_icon("radio_unchecked_disabled", "CheckBox", theme->get_icon("GuiRadioUncheckedDisabled", "EditorIcons"));
+ theme->set_icon("checked", "CheckBox", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons")));
+ theme->set_icon("unchecked", "CheckBox", theme->get_icon(SNAME("GuiUnchecked"), SNAME("EditorIcons")));
+ theme->set_icon("radio_checked", "CheckBox", theme->get_icon(SNAME("GuiRadioChecked"), SNAME("EditorIcons")));
+ theme->set_icon("radio_unchecked", "CheckBox", theme->get_icon(SNAME("GuiRadioUnchecked"), SNAME("EditorIcons")));
+ theme->set_icon("checked_disabled", "CheckBox", theme->get_icon(SNAME("GuiCheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("unchecked_disabled", "CheckBox", theme->get_icon(SNAME("GuiUncheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("radio_checked_disabled", "CheckBox", theme->get_icon(SNAME("GuiRadioCheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("radio_unchecked_disabled", "CheckBox", theme->get_icon(SNAME("GuiRadioUncheckedDisabled"), SNAME("EditorIcons")));
theme->set_color("font_color", "CheckBox", font_color);
theme->set_color("font_hover_color", "CheckBox", font_hover_color);
@@ -803,19 +803,19 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_accelerator_color", "PopupMenu", font_disabled_color);
theme->set_color("font_disabled_color", "PopupMenu", font_disabled_color);
theme->set_color("font_separator_color", "PopupMenu", font_disabled_color);
- theme->set_icon("checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons"));
- theme->set_icon("unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons"));
- theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiRadioChecked", "EditorIcons"));
- theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiRadioUnchecked", "EditorIcons"));
- theme->set_icon("checked_disabled", "PopupMenu", theme->get_icon("GuiCheckedDisabled", "EditorIcons"));
- theme->set_icon("unchecked_disabled", "PopupMenu", theme->get_icon("GuiUncheckedDisabled", "EditorIcons"));
- theme->set_icon("radio_checked_disabled", "PopupMenu", theme->get_icon("GuiRadioCheckedDisabled", "EditorIcons"));
- theme->set_icon("radio_unchecked_disabled", "PopupMenu", theme->get_icon("GuiRadioUncheckedDisabled", "EditorIcons"));
- theme->set_icon("submenu", "PopupMenu", theme->get_icon("ArrowRight", "EditorIcons"));
- theme->set_icon("submenu_mirrored", "PopupMenu", theme->get_icon("ArrowLeft", "EditorIcons"));
- theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons"));
- theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons"));
- theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon("GuiVisibilityXray", "EditorIcons"));
+ theme->set_icon("checked", "PopupMenu", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons")));
+ theme->set_icon("unchecked", "PopupMenu", theme->get_icon(SNAME("GuiUnchecked"), SNAME("EditorIcons")));
+ theme->set_icon("radio_checked", "PopupMenu", theme->get_icon(SNAME("GuiRadioChecked"), SNAME("EditorIcons")));
+ theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon(SNAME("GuiRadioUnchecked"), SNAME("EditorIcons")));
+ theme->set_icon("checked_disabled", "PopupMenu", theme->get_icon(SNAME("GuiCheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("unchecked_disabled", "PopupMenu", theme->get_icon(SNAME("GuiUncheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("radio_checked_disabled", "PopupMenu", theme->get_icon(SNAME("GuiRadioCheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("radio_unchecked_disabled", "PopupMenu", theme->get_icon(SNAME("GuiRadioUncheckedDisabled"), SNAME("EditorIcons")));
+ theme->set_icon("submenu", "PopupMenu", theme->get_icon(SNAME("ArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("submenu_mirrored", "PopupMenu", theme->get_icon(SNAME("ArrowLeft"), SNAME("EditorIcons")));
+ theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")));
+ theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")));
+ theme->set_icon("visibility_xray", "PopupMenu", theme->get_icon(SNAME("GuiVisibilityXray"), SNAME("EditorIcons")));
theme->set_constant("vseparation", "PopupMenu", (extra_spacing + default_margin_size + 1) * EDSCALE);
theme->set_constant("item_start_padding", "PopupMenu", popup_menu_margin_size * EDSCALE);
@@ -888,14 +888,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("bg", "Tree", style_tree_bg);
// Tree
- theme->set_icon("checked", "Tree", theme->get_icon("GuiChecked", "EditorIcons"));
- theme->set_icon("indeterminate", "Tree", theme->get_icon("GuiIndeterminate", "EditorIcons"));
- theme->set_icon("unchecked", "Tree", theme->get_icon("GuiUnchecked", "EditorIcons"));
- theme->set_icon("arrow", "Tree", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
- theme->set_icon("arrow_collapsed", "Tree", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
- theme->set_icon("arrow_collapsed_mirrored", "Tree", theme->get_icon("GuiTreeArrowLeft", "EditorIcons"));
- theme->set_icon("updown", "Tree", theme->get_icon("GuiTreeUpdown", "EditorIcons"));
- theme->set_icon("select_arrow", "Tree", theme->get_icon("GuiDropdown", "EditorIcons"));
+ theme->set_icon("checked", "Tree", theme->get_icon(SNAME("GuiChecked"), SNAME("EditorIcons")));
+ theme->set_icon("indeterminate", "Tree", theme->get_icon(SNAME("GuiIndeterminate"), SNAME("EditorIcons")));
+ theme->set_icon("unchecked", "Tree", theme->get_icon(SNAME("GuiUnchecked"), SNAME("EditorIcons")));
+ theme->set_icon("arrow", "Tree", theme->get_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
+ theme->set_icon("arrow_collapsed", "Tree", theme->get_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("arrow_collapsed_mirrored", "Tree", theme->get_icon(SNAME("GuiTreeArrowLeft"), SNAME("EditorIcons")));
+ theme->set_icon("updown", "Tree", theme->get_icon(SNAME("GuiTreeUpdown"), SNAME("EditorIcons")));
+ theme->set_icon("select_arrow", "Tree", theme->get_icon(SNAME("GuiDropdown"), SNAME("EditorIcons")));
theme->set_stylebox("bg_focus", "Tree", style_widget_focus);
theme->set_stylebox("custom_button", "Tree", make_empty_stylebox());
theme->set_stylebox("custom_button_pressed", "Tree", make_empty_stylebox());
@@ -1012,21 +1012,21 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_unselected_color", "TabContainer", font_disabled_color);
theme->set_color("font_selected_color", "TabBar", font_color);
theme->set_color("font_unselected_color", "TabBar", font_disabled_color);
- theme->set_icon("menu", "TabContainer", theme->get_icon("GuiTabMenu", "EditorIcons"));
- theme->set_icon("menu_highlight", "TabContainer", theme->get_icon("GuiTabMenuHl", "EditorIcons"));
+ theme->set_icon("menu", "TabContainer", theme->get_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons")));
+ theme->set_icon("menu_highlight", "TabContainer", theme->get_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
theme->set_stylebox("SceneTabFG", "EditorStyles", style_tab_selected);
theme->set_stylebox("SceneTabBG", "EditorStyles", style_tab_unselected);
- theme->set_icon("close", "TabBar", theme->get_icon("GuiClose", "EditorIcons"));
+ theme->set_icon("close", "TabBar", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
theme->set_stylebox("button_pressed", "TabBar", style_menu);
theme->set_stylebox("button_highlight", "TabBar", style_menu);
- theme->set_icon("increment", "TabContainer", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
- theme->set_icon("decrement", "TabContainer", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
- theme->set_icon("increment", "TabBar", theme->get_icon("GuiScrollArrowRight", "EditorIcons"));
- theme->set_icon("decrement", "TabBar", theme->get_icon("GuiScrollArrowLeft", "EditorIcons"));
- theme->set_icon("increment_highlight", "TabBar", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons"));
- theme->set_icon("decrement_highlight", "TabBar", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons"));
- theme->set_icon("increment_highlight", "TabContainer", theme->get_icon("GuiScrollArrowRightHl", "EditorIcons"));
- theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon("GuiScrollArrowLeftHl", "EditorIcons"));
+ theme->set_icon("increment", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("decrement", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowLeft"), SNAME("EditorIcons")));
+ theme->set_icon("increment", "TabBar", theme->get_icon(SNAME("GuiScrollArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("decrement", "TabBar", theme->get_icon(SNAME("GuiScrollArrowLeft"), SNAME("EditorIcons")));
+ theme->set_icon("increment_highlight", "TabBar", theme->get_icon(SNAME("GuiScrollArrowRightHl"), SNAME("EditorIcons")));
+ theme->set_icon("decrement_highlight", "TabBar", theme->get_icon(SNAME("GuiScrollArrowLeftHl"), SNAME("EditorIcons")));
+ theme->set_icon("increment_highlight", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowRightHl"), SNAME("EditorIcons")));
+ theme->set_icon("decrement_highlight", "TabContainer", theme->get_icon(SNAME("GuiScrollArrowLeftHl"), SNAME("EditorIcons")));
theme->set_constant("hseparation", "TabBar", 4 * EDSCALE);
// Content of each tab
@@ -1079,7 +1079,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("DebuggerPanel", "EditorStyles", style_panel_debugger);
Ref<StyleBoxFlat> style_panel_invisible_top = style_content_panel->duplicate();
- int stylebox_offset = theme->get_font("tab_selected", "TabContainer")->get_height(theme->get_font_size("tab_selected", "TabContainer")) + theme->get_stylebox(SNAME("tab_selected"), SNAME("TabContainer"))->get_minimum_size().height + theme->get_stylebox(SNAME("panel"), SNAME("TabContainer"))->get_default_margin(SIDE_TOP);
+ int stylebox_offset = theme->get_font(SNAME("tab_selected"), SNAME("TabContainer"))->get_height(theme->get_font_size(SNAME("tab_selected"), SNAME("TabContainer"))) + theme->get_stylebox(SNAME("tab_selected"), SNAME("TabContainer"))->get_minimum_size().height + theme->get_stylebox(SNAME("panel"), SNAME("TabContainer"))->get_default_margin(SIDE_TOP);
style_panel_invisible_top->set_expand_margin_size(SIDE_TOP, -stylebox_offset);
style_panel_invisible_top->set_default_margin(SIDE_TOP, 0);
theme->set_stylebox("BottomPanelDebuggerOverride", "EditorStyles", style_panel_invisible_top);
@@ -1105,7 +1105,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("normal", "LineEdit", style_line_edit);
theme->set_stylebox("focus", "LineEdit", style_widget_focus);
theme->set_stylebox("read_only", "LineEdit", style_line_edit_disabled);
- theme->set_icon("clear", "LineEdit", theme->get_icon("GuiClose", "EditorIcons"));
+ theme->set_icon("clear", "LineEdit", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
theme->set_color("read_only", "LineEdit", font_disabled_color);
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
@@ -1121,8 +1121,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("focus", "TextEdit", style_widget_focus);
theme->set_stylebox("read_only", "TextEdit", style_line_edit_disabled);
theme->set_constant("side_margin", "TabContainer", 0);
- theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons"));
- theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons"));
+ theme->set_icon("tab", "TextEdit", theme->get_icon(SNAME("GuiTab"), SNAME("EditorIcons")));
+ theme->set_icon("space", "TextEdit", theme->get_icon(SNAME("GuiSpace"), SNAME("EditorIcons")));
theme->set_color("font_color", "TextEdit", font_color);
theme->set_color("font_readonly_color", "TextEdit", font_readonly_color);
theme->set_color("font_placeholder_color", "TextEdit", font_placeholder_color);
@@ -1131,25 +1131,25 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("line_spacing", "TextEdit", 4 * EDSCALE);
// CodeEdit
- theme->set_font("font", "CodeEdit", theme->get_font("source", "EditorFonts"));
- theme->set_font_size("font_size", "CodeEdit", theme->get_font_size("source_size", "EditorFonts"));
+ theme->set_font("font", "CodeEdit", theme->get_font(SNAME("source"), SNAME("EditorFonts")));
+ theme->set_font_size("font_size", "CodeEdit", theme->get_font_size(SNAME("source_size"), SNAME("EditorFonts")));
theme->set_stylebox("normal", "CodeEdit", style_widget);
theme->set_stylebox("focus", "CodeEdit", style_widget_hover);
theme->set_stylebox("read_only", "CodeEdit", style_widget_disabled);
- theme->set_icon("tab", "CodeEdit", theme->get_icon("GuiTab", "EditorIcons"));
- theme->set_icon("space", "CodeEdit", theme->get_icon("GuiSpace", "EditorIcons"));
- theme->set_icon("folded", "CodeEdit", theme->get_icon("GuiTreeArrowRight", "EditorIcons"));
- theme->set_icon("can_fold", "CodeEdit", theme->get_icon("GuiTreeArrowDown", "EditorIcons"));
- theme->set_icon("executing_line", "CodeEdit", theme->get_icon("MainPlay", "EditorIcons"));
- theme->set_icon("breakpoint", "CodeEdit", theme->get_icon("Breakpoint", "EditorIcons"));
+ theme->set_icon("tab", "CodeEdit", theme->get_icon(SNAME("GuiTab"), SNAME("EditorIcons")));
+ theme->set_icon("space", "CodeEdit", theme->get_icon(SNAME("GuiSpace"), SNAME("EditorIcons")));
+ theme->set_icon("folded", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowRight"), SNAME("EditorIcons")));
+ theme->set_icon("can_fold", "CodeEdit", theme->get_icon(SNAME("GuiTreeArrowDown"), SNAME("EditorIcons")));
+ theme->set_icon("executing_line", "CodeEdit", theme->get_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+ theme->set_icon("breakpoint", "CodeEdit", theme->get_icon(SNAME("Breakpoint"), SNAME("EditorIcons")));
theme->set_constant("line_spacing", "CodeEdit", EDITOR_DEF("text_editor/appearance/whitespace/line_spacing", 6));
// H/VSplitContainer
- theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon("GuiVsplitBg", "EditorIcons"), 1, 1, 1, 1));
- theme->set_stylebox("bg", "HSplitContainer", make_stylebox(theme->get_icon("GuiHsplitBg", "EditorIcons"), 1, 1, 1, 1));
+ theme->set_stylebox("bg", "VSplitContainer", make_stylebox(theme->get_icon(SNAME("GuiVsplitBg"), SNAME("EditorIcons")), 1, 1, 1, 1));
+ theme->set_stylebox("bg", "HSplitContainer", make_stylebox(theme->get_icon(SNAME("GuiHsplitBg"), SNAME("EditorIcons")), 1, 1, 1, 1));
- theme->set_icon("grabber", "VSplitContainer", theme->get_icon("GuiVsplitter", "EditorIcons"));
- theme->set_icon("grabber", "HSplitContainer", theme->get_icon("GuiHsplitter", "EditorIcons"));
+ theme->set_icon("grabber", "VSplitContainer", theme->get_icon(SNAME("GuiVsplitter"), SNAME("EditorIcons")));
+ theme->set_icon("grabber", "HSplitContainer", theme->get_icon(SNAME("GuiHsplitter"), SNAME("EditorIcons")));
theme->set_constant("separation", "HSplitContainer", default_margin_size * 2 * EDSCALE);
theme->set_constant("separation", "VSplitContainer", default_margin_size * 2 * EDSCALE);
@@ -1187,14 +1187,14 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("embedded_border", "Window", style_window);
theme->set_color("title_color", "Window", font_color);
- theme->set_icon("close", "Window", theme->get_icon("GuiClose", "EditorIcons"));
- theme->set_icon("close_pressed", "Window", theme->get_icon("GuiClose", "EditorIcons"));
+ theme->set_icon("close", "Window", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
+ theme->set_icon("close_pressed", "Window", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
theme->set_constant("close_h_ofs", "Window", 22 * EDSCALE);
theme->set_constant("close_v_ofs", "Window", 20 * EDSCALE);
theme->set_constant("title_height", "Window", 24 * EDSCALE);
theme->set_constant("resize_margin", "Window", 4 * EDSCALE);
- theme->set_font("title_font", "Window", theme->get_font("title", "EditorFonts"));
- theme->set_font_size("title_font_size", "Window", theme->get_font_size("title_size", "EditorFonts"));
+ theme->set_font("title_font", "Window", theme->get_font(SNAME("title"), SNAME("EditorFonts")));
+ theme->set_font_size("title_font_size", "Window", theme->get_font_size(SNAME("title_size"), SNAME("EditorFonts")));
// Complex window (currently only Editor Settings and Project Settings)
Ref<StyleBoxFlat> style_complex_window = style_window->duplicate();
@@ -1210,11 +1210,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// HScrollBar
Ref<Texture2D> empty_icon = memnew(ImageTexture);
- theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0));
+ theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0));
+ theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2));
+ theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2));
theme->set_icon("increment", "HScrollBar", empty_icon);
theme->set_icon("increment_highlight", "HScrollBar", empty_icon);
@@ -1224,11 +1224,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("decrement_pressed", "HScrollBar", empty_icon);
// VScrollBar
- theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollBg", "EditorIcons"), 5, 5, 5, 5, 0, 0, 0, 0));
- theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabber", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2));
- theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberHl", "EditorIcons"), 5, 5, 5, 5, 2, 2, 2, 2));
- theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon("GuiScrollGrabberPressed", "EditorIcons"), 6, 6, 6, 6, 2, 2, 2, 2));
+ theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0));
+ theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0));
+ theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2));
+ theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2));
+ theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2));
theme->set_icon("increment", "VScrollBar", empty_icon);
theme->set_icon("increment_highlight", "VScrollBar", empty_icon);
@@ -1238,15 +1238,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_icon("decrement_pressed", "VScrollBar", empty_icon);
// HSlider
- theme->set_icon("grabber_highlight", "HSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons"));
- theme->set_icon("grabber", "HSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons"));
+ theme->set_icon("grabber_highlight", "HSlider", theme->get_icon(SNAME("GuiSliderGrabberHl"), SNAME("EditorIcons")));
+ theme->set_icon("grabber", "HSlider", theme->get_icon(SNAME("GuiSliderGrabber"), SNAME("EditorIcons")));
theme->set_stylebox("slider", "HSlider", make_flat_stylebox(dark_color_3, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
theme->set_stylebox("grabber_area", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2, corner_width));
theme->set_stylebox("grabber_area_highlight", "HSlider", make_flat_stylebox(contrast_color_1, 0, default_margin_size / 2, 0, default_margin_size / 2));
// VSlider
- theme->set_icon("grabber", "VSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons"));
- theme->set_icon("grabber_highlight", "VSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons"));
+ theme->set_icon("grabber", "VSlider", theme->get_icon(SNAME("GuiSliderGrabber"), SNAME("EditorIcons")));
+ theme->set_icon("grabber_highlight", "VSlider", theme->get_icon(SNAME("GuiSliderGrabberHl"), SNAME("EditorIcons")));
theme->set_stylebox("slider", "VSlider", make_flat_stylebox(dark_color_3, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
theme->set_stylebox("grabber_area", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0, corner_width));
theme->set_stylebox("grabber_area_highlight", "VSlider", make_flat_stylebox(contrast_color_1, default_margin_size / 2, 0, default_margin_size / 2, 0));
@@ -1279,7 +1279,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Panel
theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4, corner_width));
- theme->set_stylebox("panel_fg", "Panel", style_default);
+ theme->set_stylebox("PanelForeground", "EditorStyles", style_default);
// Label
theme->set_stylebox("normal", "Label", style_empty);
@@ -1315,12 +1315,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("panel", "PopupPanel", style_popup);
// SpinBox
- theme->set_icon("updown", "SpinBox", theme->get_icon("GuiSpinboxUpdown", "EditorIcons"));
- theme->set_icon("updown_disabled", "SpinBox", theme->get_icon("GuiSpinboxUpdownDisabled", "EditorIcons"));
+ theme->set_icon("updown", "SpinBox", theme->get_icon(SNAME("GuiSpinboxUpdown"), SNAME("EditorIcons")));
+ theme->set_icon("updown_disabled", "SpinBox", theme->get_icon(SNAME("GuiSpinboxUpdownDisabled"), SNAME("EditorIcons")));
// ProgressBar
- theme->set_stylebox("bg", "ProgressBar", make_stylebox(theme->get_icon("GuiProgressBar", "EditorIcons"), 4, 4, 4, 4, 0, 0, 0, 0));
- theme->set_stylebox("fg", "ProgressBar", make_stylebox(theme->get_icon("GuiProgressFill", "EditorIcons"), 6, 6, 6, 6, 2, 1, 2, 1));
+ theme->set_stylebox("bg", "ProgressBar", make_stylebox(theme->get_icon(SNAME("GuiProgressBar"), SNAME("EditorIcons")), 4, 4, 4, 4, 0, 0, 0, 0));
+ theme->set_stylebox("fg", "ProgressBar", make_stylebox(theme->get_icon(SNAME("GuiProgressFill"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 1, 2, 1));
theme->set_color("font_color", "ProgressBar", font_color);
// GraphEdit
@@ -1332,15 +1332,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("grid_major", "GraphEdit", Color(0.0, 0.0, 0.0, 0.15));
theme->set_color("grid_minor", "GraphEdit", Color(0.0, 0.0, 0.0, 0.07));
}
- theme->set_color("selection_fill", "GraphEdit", theme->get_color("box_selection_fill_color", "Editor"));
- theme->set_color("selection_stroke", "GraphEdit", theme->get_color("box_selection_stroke_color", "Editor"));
+ theme->set_color("selection_fill", "GraphEdit", theme->get_color(SNAME("box_selection_fill_color"), SNAME("Editor")));
+ theme->set_color("selection_stroke", "GraphEdit", theme->get_color(SNAME("box_selection_stroke_color"), SNAME("Editor")));
theme->set_color("activity", "GraphEdit", accent_color);
- theme->set_icon("minus", "GraphEdit", theme->get_icon("ZoomLess", "EditorIcons"));
- theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons"));
- theme->set_icon("reset", "GraphEdit", theme->get_icon("ZoomReset", "EditorIcons"));
- theme->set_icon("snap", "GraphEdit", theme->get_icon("SnapGrid", "EditorIcons"));
- theme->set_icon("minimap", "GraphEdit", theme->get_icon("GridMinimap", "EditorIcons"));
- theme->set_icon("layout", "GraphEdit", theme->get_icon("GridLayout", "EditorIcons"));
+ theme->set_icon("minus", "GraphEdit", theme->get_icon(SNAME("ZoomLess"), SNAME("EditorIcons")));
+ theme->set_icon("more", "GraphEdit", theme->get_icon(SNAME("ZoomMore"), SNAME("EditorIcons")));
+ theme->set_icon("reset", "GraphEdit", theme->get_icon(SNAME("ZoomReset"), SNAME("EditorIcons")));
+ theme->set_icon("snap", "GraphEdit", theme->get_icon(SNAME("SnapGrid"), SNAME("EditorIcons")));
+ theme->set_icon("minimap", "GraphEdit", theme->get_icon(SNAME("GridMinimap"), SNAME("EditorIcons")));
+ theme->set_icon("layout", "GraphEdit", theme->get_icon(SNAME("GridLayout"), SNAME("EditorIcons")));
theme->set_constant("bezier_len_pos", "GraphEdit", 80 * EDSCALE);
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * EDSCALE);
@@ -1366,7 +1366,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("camera", "GraphEditMinimap", style_minimap_camera);
theme->set_stylebox("node", "GraphEditMinimap", style_minimap_node);
- Ref<Texture2D> minimap_resizer_icon = theme->get_icon("GuiResizer", "EditorIcons");
+ Ref<Texture2D> minimap_resizer_icon = theme->get_icon(SNAME("GuiResizer"), SNAME("EditorIcons"));
Color minimap_resizer_color;
if (dark_theme) {
minimap_resizer_color = Color(1, 1, 1, 0.65);
@@ -1435,20 +1435,20 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("close_offset", "GraphNode", 20 * EDSCALE);
theme->set_constant("separation", "GraphNode", 1 * EDSCALE);
- theme->set_icon("close", "GraphNode", theme->get_icon("GuiCloseCustomizable", "EditorIcons"));
- theme->set_icon("resizer", "GraphNode", theme->get_icon("GuiResizer", "EditorIcons"));
- theme->set_icon("port", "GraphNode", theme->get_icon("GuiGraphNodePort", "EditorIcons"));
+ theme->set_icon("close", "GraphNode", theme->get_icon(SNAME("GuiCloseCustomizable"), SNAME("EditorIcons")));
+ theme->set_icon("resizer", "GraphNode", theme->get_icon(SNAME("GuiResizer"), SNAME("EditorIcons")));
+ theme->set_icon("port", "GraphNode", theme->get_icon(SNAME("GuiGraphNodePort"), SNAME("EditorIcons")));
// GridContainer
theme->set_constant("vseparation", "GridContainer", Math::round(widget_default_margin.y - 2 * EDSCALE));
// FileDialog
- theme->set_icon("folder", "FileDialog", theme->get_icon("Folder", "EditorIcons"));
- theme->set_icon("parent_folder", "FileDialog", theme->get_icon("ArrowUp", "EditorIcons"));
- theme->set_icon("back_folder", "FileDialog", theme->get_icon("Back", "EditorIcons"));
- theme->set_icon("forward_folder", "FileDialog", theme->get_icon("Forward", "EditorIcons"));
- theme->set_icon("reload", "FileDialog", theme->get_icon("Reload", "EditorIcons"));
- theme->set_icon("toggle_hidden", "FileDialog", theme->get_icon("GuiVisibilityVisible", "EditorIcons"));
+ theme->set_icon("folder", "FileDialog", theme->get_icon(SNAME("Folder"), SNAME("EditorIcons")));
+ theme->set_icon("parent_folder", "FileDialog", theme->get_icon(SNAME("ArrowUp"), SNAME("EditorIcons")));
+ theme->set_icon("back_folder", "FileDialog", theme->get_icon(SNAME("Back"), SNAME("EditorIcons")));
+ theme->set_icon("forward_folder", "FileDialog", theme->get_icon(SNAME("Forward"), SNAME("EditorIcons")));
+ theme->set_icon("reload", "FileDialog", theme->get_icon(SNAME("Reload"), SNAME("EditorIcons")));
+ theme->set_icon("toggle_hidden", "FileDialog", theme->get_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")));
// Use a different color for folder icons to make them easier to distinguish from files.
// On a light theme, the icon will be dark, so we need to lighten it before blending it with the accent color.
theme->set_color("folder_icon_modulate", "FileDialog", (dark_theme ? Color(1, 1, 1) : Color(4.25, 4.25, 4.25)).lerp(accent_color, 0.7));
@@ -1460,22 +1460,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("sv_height", "ColorPicker", 256 * EDSCALE);
theme->set_constant("h_width", "ColorPicker", 30 * EDSCALE);
theme->set_constant("label_width", "ColorPicker", 10 * EDSCALE);
- theme->set_icon("screen_picker", "ColorPicker", theme->get_icon("ColorPick", "EditorIcons"));
- theme->set_icon("add_preset", "ColorPicker", theme->get_icon("Add", "EditorIcons"));
- theme->set_icon("sample_bg", "ColorPicker", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
- theme->set_icon("overbright_indicator", "ColorPicker", theme->get_icon("OverbrightIndicator", "EditorIcons"));
- theme->set_icon("bar_arrow", "ColorPicker", theme->get_icon("ColorPickerBarArrow", "EditorIcons"));
- theme->set_icon("picker_cursor", "ColorPicker", theme->get_icon("PickerCursor", "EditorIcons"));
+ theme->set_icon("screen_picker", "ColorPicker", theme->get_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
+ theme->set_icon("add_preset", "ColorPicker", theme->get_icon(SNAME("Add"), SNAME("EditorIcons")));
+ theme->set_icon("sample_bg", "ColorPicker", theme->get_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")));
+ theme->set_icon("overbright_indicator", "ColorPicker", theme->get_icon(SNAME("OverbrightIndicator"), SNAME("EditorIcons")));
+ theme->set_icon("bar_arrow", "ColorPicker", theme->get_icon(SNAME("ColorPickerBarArrow"), SNAME("EditorIcons")));
+ theme->set_icon("picker_cursor", "ColorPicker", theme->get_icon(SNAME("PickerCursor"), SNAME("EditorIcons")));
// ColorPickerButton
- theme->set_icon("bg", "ColorPickerButton", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
+ theme->set_icon("bg", "ColorPickerButton", theme->get_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")));
// ColorPresetButton
Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2, 2);
preset_sb->set_anti_aliased(false);
theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
- theme->set_icon("preset_bg", "ColorPresetButton", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
- theme->set_icon("overbright_indicator", "ColorPresetButton", theme->get_icon("OverbrightIndicator", "EditorIcons"));
+ theme->set_icon("preset_bg", "ColorPresetButton", theme->get_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")));
+ theme->set_icon("overbright_indicator", "ColorPresetButton", theme->get_icon(SNAME("OverbrightIndicator"), SNAME("EditorIcons")));
// Information on 3D viewport
Ref<StyleBoxFlat> style_info_3d_viewport = style_default->duplicate();
@@ -1486,7 +1486,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// Asset Library.
theme->set_stylebox("panel", "AssetLib", style_content_panel);
theme->set_color("status_color", "AssetLib", Color(0.5, 0.5, 0.5));
- theme->set_icon("dismiss", "AssetLib", theme->get_icon("Close", "EditorIcons"));
+ theme->set_icon("dismiss", "AssetLib", theme->get_icon(SNAME("Close"), SNAME("EditorIcons")));
// Theme editor.
theme->set_color("preview_picker_overlay_color", "ThemeEditor", Color(0.1, 0.1, 0.1, 0.25));
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index 6c9e4ab0fc..24227b3a6b 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -113,22 +113,22 @@ void EditorToaster::_notification(int p_what) {
disable_notifications_button->set_icon(get_theme_icon(SNAME("NotificationDisabled"), SNAME("EditorIcons")));
// Styleboxes background.
- info_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
+ info_panel_style_background->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")));
- warning_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
- warning_panel_style_background->set_border_color(get_theme_color("warning_color", "Editor"));
+ warning_panel_style_background->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")));
+ warning_panel_style_background->set_border_color(get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- error_panel_style_background->set_bg_color(get_theme_color("base_color", "Editor"));
- error_panel_style_background->set_border_color(get_theme_color("error_color", "Editor"));
+ error_panel_style_background->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")));
+ error_panel_style_background->set_border_color(get_theme_color(SNAME("error_color"), SNAME("Editor")));
// Styleboxes progress.
- info_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
+ info_panel_style_progress->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")).lightened(0.03));
- warning_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
- warning_panel_style_progress->set_border_color(get_theme_color("warning_color", "Editor"));
+ warning_panel_style_progress->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")).lightened(0.03));
+ warning_panel_style_progress->set_border_color(get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- error_panel_style_progress->set_bg_color(get_theme_color("base_color", "Editor").lightened(0.03));
- error_panel_style_progress->set_border_color(get_theme_color("error_color", "Editor"));
+ error_panel_style_progress->set_bg_color(get_theme_color(SNAME("base_color"), SNAME("Editor")).lightened(0.03));
+ error_panel_style_progress->set_border_color(get_theme_color(SNAME("error_color"), SNAME("Editor")));
main_button->update();
disable_notifications_button->update();
@@ -256,13 +256,13 @@ void EditorToaster::_draw_button() {
real_t button_radius = main_button->get_size().x / 8;
switch (highest_severity) {
case SEVERITY_INFO:
- color = get_theme_color("accent_color", "Editor");
+ color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
break;
case SEVERITY_WARNING:
- color = get_theme_color("warning_color", "Editor");
+ color = get_theme_color(SNAME("warning_color"), SNAME("Editor"));
break;
case SEVERITY_ERROR:
- color = get_theme_color("error_color", "Editor");
+ color = get_theme_color(SNAME("error_color"), SNAME("Editor"));
break;
default:
break;
@@ -360,7 +360,7 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_
if (p_time > 0.0) {
Button *close_button = memnew(Button);
close_button->set_flat(true);
- close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
+ close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
close_button->connect("pressed", callable_bind(callable_mp(this, &EditorToaster::close), panel));
close_button->connect("theme_changed", callable_bind(callable_mp(this, &EditorToaster::_close_button_theme_changed), close_button));
hbox_container->add_child(close_button);
@@ -442,7 +442,7 @@ void EditorToaster::close(Control *p_control) {
void EditorToaster::_close_button_theme_changed(Control *p_close_button) {
Button *close_button = Object::cast_to<Button>(p_close_button);
if (close_button) {
- close_button->set_icon(get_theme_icon("Close", "EditorIcons"));
+ close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
}
}
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index bc71072d29..c91351022f 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -338,30 +338,28 @@ void FileSystemDock::_notification(int p_what) {
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &FileSystemDock::_fs_changed));
EditorResourcePreview::get_singleton()->connect("preview_invalidated", callable_mp(this, &FileSystemDock::_preview_invalidated));
- String ei = "EditorIcons";
-
- button_reload->set_icon(get_theme_icon(SNAME("Reload"), ei));
- button_toggle_display_mode->set_icon(get_theme_icon(SNAME("Panels2"), ei));
+ button_reload->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
+ button_toggle_display_mode->set_icon(get_theme_icon(SNAME("Panels2"), SNAME("EditorIcons")));
button_file_list_display_mode->connect("pressed", callable_mp(this, &FileSystemDock::_toggle_file_display));
files->connect("item_activated", callable_mp(this, &FileSystemDock::_file_list_activate_file));
button_hist_next->connect("pressed", callable_mp(this, &FileSystemDock::_fw_history));
button_hist_prev->connect("pressed", callable_mp(this, &FileSystemDock::_bw_history));
- tree_search_box->set_right_icon(get_theme_icon(SNAME("Search"), ei));
+ tree_search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
tree_search_box->set_clear_button_enabled(true);
- tree_button_sort->set_icon(get_theme_icon(SNAME("Sort"), ei));
+ tree_button_sort->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
- file_list_search_box->set_right_icon(get_theme_icon(SNAME("Search"), ei));
+ file_list_search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
file_list_search_box->set_clear_button_enabled(true);
- file_list_button_sort->set_icon(get_theme_icon(SNAME("Sort"), ei));
+ file_list_button_sort->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
if (is_layout_rtl()) {
- button_hist_next->set_icon(get_theme_icon(SNAME("Back"), ei));
- button_hist_prev->set_icon(get_theme_icon(SNAME("Forward"), ei));
+ button_hist_next->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
+ button_hist_prev->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
} else {
- button_hist_next->set_icon(get_theme_icon(SNAME("Forward"), ei));
- button_hist_prev->set_icon(get_theme_icon(SNAME("Back"), ei));
+ button_hist_next->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
+ button_hist_prev->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
}
file_list_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_list_rmb_option));
tree_popup->connect("id_pressed", callable_mp(this, &FileSystemDock::_tree_rmb_option));
@@ -412,15 +410,14 @@ void FileSystemDock::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
// Update icons.
- String ei = "EditorIcons";
- button_reload->set_icon(get_theme_icon(SNAME("Reload"), ei));
- button_toggle_display_mode->set_icon(get_theme_icon(SNAME("Panels2"), ei));
+ button_reload->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
+ button_toggle_display_mode->set_icon(get_theme_icon(SNAME("Panels2"), SNAME("EditorIcons")));
if (is_layout_rtl()) {
- button_hist_next->set_icon(get_theme_icon(SNAME("Back"), ei));
- button_hist_prev->set_icon(get_theme_icon(SNAME("Forward"), ei));
+ button_hist_next->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
+ button_hist_prev->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
} else {
- button_hist_next->set_icon(get_theme_icon(SNAME("Forward"), ei));
- button_hist_prev->set_icon(get_theme_icon(SNAME("Back"), ei));
+ button_hist_next->set_icon(get_theme_icon(SNAME("Forward"), SNAME("EditorIcons")));
+ button_hist_prev->set_icon(get_theme_icon(SNAME("Back"), SNAME("EditorIcons")));
}
if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) {
button_file_list_display_mode->set_icon(get_theme_icon(SNAME("FileThumbnail"), SNAME("EditorIcons")));
@@ -428,13 +425,13 @@ void FileSystemDock::_notification(int p_what) {
button_file_list_display_mode->set_icon(get_theme_icon(SNAME("FileList"), SNAME("EditorIcons")));
}
- tree_search_box->set_right_icon(get_theme_icon(SNAME("Search"), ei));
+ tree_search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
tree_search_box->set_clear_button_enabled(true);
- tree_button_sort->set_icon(get_theme_icon(SNAME("Sort"), ei));
+ tree_button_sort->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
- file_list_search_box->set_right_icon(get_theme_icon(SNAME("Search"), ei));
+ file_list_search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
file_list_search_box->set_clear_button_enabled(true);
- file_list_button_sort->set_icon(get_theme_icon(SNAME("Sort"), ei));
+ file_list_button_sort->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
// Update always show folders.
bool new_always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders"));
@@ -629,7 +626,7 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
for (int i = 0; i < p_path->get_file_count(); i++) {
String file = p_path->get_file(i);
- if (file.to_lower().find(searched_string) != -1) {
+ if (file.to_lower().contains(searched_string)) {
FileInfo fi;
fi.name = file;
fi.type = p_path->get_file_type(i);
@@ -718,7 +715,6 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
String directory = path;
String file = "";
- String ei = "EditorIcons";
int thumbnail_size = EditorSettings::get_singleton()->get("docks/filesystem/thumbnail_size");
thumbnail_size *= EDSCALE;
Ref<Texture2D> folder_thumbnail;
@@ -736,13 +732,13 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
if (thumbnail_size < 64) {
- folder_thumbnail = get_theme_icon(SNAME("FolderMediumThumb"), ei);
- file_thumbnail = get_theme_icon(SNAME("FileMediumThumb"), ei);
- file_thumbnail_broken = get_theme_icon(SNAME("FileDeadMediumThumb"), ei);
+ folder_thumbnail = get_theme_icon(SNAME("FolderMediumThumb"), SNAME("EditorIcons"));
+ file_thumbnail = get_theme_icon(SNAME("FileMediumThumb"), SNAME("EditorIcons"));
+ file_thumbnail_broken = get_theme_icon(SNAME("FileDeadMediumThumb"), SNAME("EditorIcons"));
} else {
- folder_thumbnail = get_theme_icon(SNAME("FolderBigThumb"), ei);
- file_thumbnail = get_theme_icon(SNAME("FileBigThumb"), ei);
- file_thumbnail_broken = get_theme_icon(SNAME("FileDeadBigThumb"), ei);
+ folder_thumbnail = get_theme_icon(SNAME("FolderBigThumb"), SNAME("EditorIcons"));
+ file_thumbnail = get_theme_icon(SNAME("FileBigThumb"), SNAME("EditorIcons"));
+ file_thumbnail_broken = get_theme_icon(SNAME("FileDeadBigThumb"), SNAME("EditorIcons"));
}
} else {
// No thumbnails.
@@ -871,7 +867,6 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Fills the ItemList control node from the FileInfos.
String main_scene = ProjectSettings::get_singleton()->get("application/run/main_scene");
- String oi = "Object";
for (FileInfo &E : file_list) {
FileInfo *finfo = &(E);
String fname = finfo->name;
@@ -885,10 +880,10 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Select the icons.
if (!finfo->import_broken) {
- type_icon = (has_theme_icon(ftype, ei)) ? get_theme_icon(ftype, ei) : get_theme_icon(oi, ei);
+ type_icon = (has_theme_icon(ftype, SNAME("EditorIcons"))) ? get_theme_icon(ftype, SNAME("EditorIcons")) : get_theme_icon(SNAME("Object"), SNAME("EditorIcons"));
big_icon = file_thumbnail;
} else {
- type_icon = get_theme_icon(SNAME("ImportFail"), ei);
+ type_icon = get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"));
big_icon = file_thumbnail_broken;
tooltip += "\n" + TTR("Status: Import of file failed. Please fix file and reimport manually.");
}
@@ -1230,6 +1225,30 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin
if (err != OK) {
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ".import\n");
}
+
+ // Remove uid from .import file to avoid conflict.
+ Ref<ConfigFile> cfg;
+ cfg.instantiate();
+ cfg->load(new_path + ".import");
+ cfg->erase_section_key("remap", "uid");
+ cfg->save(new_path + ".import");
+ } else if (p_item.is_file && (old_path.get_extension() == "tscn" || old_path.get_extension() == "tres")) {
+ // FIXME: Quick hack to fix text resources. This should be fixed properly.
+ FileAccessRef file = FileAccess::open(old_path, FileAccess::READ, &err);
+ if (err == OK) {
+ PackedStringArray lines = file->get_as_utf8_string().split("\n");
+ String line = lines[0];
+
+ if (line.contains("uid")) {
+ line = line.substr(0, line.find(" uid")) + "]";
+ lines.write[0] = line;
+
+ FileAccessRef file2 = FileAccess::open(new_path, FileAccess::WRITE, &err);
+ if (err == OK) {
+ file2->store_string(String("\n").join(lines));
+ }
+ }
+ }
}
} else {
EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + "\n");
@@ -1373,8 +1392,8 @@ void FileSystemDock::_make_dir_confirm() {
if (dir_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
- } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.find("*") != -1 ||
- dir_name.find("|") != -1 || dir_name.find(">") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) {
+ } else if (dir_name.contains("/") || dir_name.contains("\\") || dir_name.contains(":") || dir_name.contains("*") ||
+ dir_name.contains("|") || dir_name.contains(">") || dir_name.ends_with(".") || dir_name.ends_with(" ")) {
EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters."));
return;
}
@@ -1478,7 +1497,7 @@ void FileSystemDock::_rename_operation_confirm() {
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
- } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
+ } else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
} else if (to_rename.is_file && old_name.get_extension() != new_name.get_extension()) {
@@ -1540,7 +1559,7 @@ void FileSystemDock::_duplicate_operation_confirm() {
if (new_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided."));
return;
- } else if (new_name.find("/") != -1 || new_name.find("\\") != -1 || new_name.find(":") != -1) {
+ } else if (new_name.contains("/") || new_name.contains("\\") || new_name.contains(":")) {
EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));
return;
}
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index 131ecc3b12..dd72def6ad 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -53,11 +53,6 @@ inline void pop_back(T &container) {
container.resize(container.size() - 1);
}
-// TODO: Copied from TextEdit private, would be nice to extract it in a single place.
-static bool is_text_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
static bool find_next(const String &line, String pattern, int from, bool match_case, bool whole_words, int &out_begin, int &out_end) {
int end = from;
@@ -73,10 +68,10 @@ static bool find_next(const String &line, String pattern, int from, bool match_c
out_end = end;
if (whole_words) {
- if (begin > 0 && is_text_char(line[begin - 1])) {
+ if (begin > 0 && (is_ascii_identifier_char(line[begin - 1]))) {
continue;
}
- if (end < line.size() && is_text_char(line[end])) {
+ if (end < line.size() && (is_ascii_identifier_char(line[end]))) {
continue;
}
}
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index b40a810763..605f385de2 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -287,7 +287,7 @@ void Collada::_parse_image(XMLParser &parser) {
if (state.version < State::Version(1, 4, 0)) {
/* <1.4 */
String path = parser.get_attribute_value("source").strip_edges();
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path.uri_decode()));
}
@@ -300,7 +300,7 @@ void Collada::_parse_image(XMLParser &parser) {
parser.read();
String path = parser.get_node_data().strip_edges().uri_decode();
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path));
@@ -1832,10 +1832,10 @@ void Collada::_parse_animation(XMLParser &parser) {
}
}
- if (target.find("/") != -1) { //transform component
+ if (target.contains("/")) { //transform component
track.target = target.get_slicec('/', 0);
track.param = target.get_slicec('/', 1);
- if (track.param.find(".") != -1) {
+ if (track.param.contains(".")) {
track.component = track.param.get_slice(".", 1).to_upper();
}
track.param = track.param.get_slice(".", 0);
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
index 81b98c1d45..244352fbb2 100644
--- a/editor/import/dynamicfont_import_settings.cpp
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -495,7 +495,7 @@ void DynamicFontImportSettings::_variation_add() {
vars_item->set_text(0, TTR("New configuration"));
vars_item->set_editable(0, true);
- vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->add_button(1, vars_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
Ref<DynamicFontImportSettingsData> import_variation_data;
@@ -643,8 +643,8 @@ void DynamicFontImportSettings::_glyph_selected() {
TreeItem *item = glyph_table->get_selected();
ERR_FAIL_NULL(item);
- Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
- Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ Color scol = glyph_table->get_theme_color(SNAME("box_selection_fill_color"), SNAME("Editor"));
+ Color fcol = glyph_table->get_theme_color(SNAME("font_selected_color"), SNAME("Editor"));
scol.a = 1.f;
int32_t c = item->get_metadata(glyph_table->get_selected_column());
@@ -705,8 +705,8 @@ void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
TreeItem *root = glyph_table->create_item();
ERR_FAIL_NULL(root);
- Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
- Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ Color scol = glyph_table->get_theme_color(SNAME("box_selection_fill_color"), SNAME("Editor"));
+ Color fcol = glyph_table->get_theme_color(SNAME("font_selected_color"), SNAME("Editor"));
scol.a = 1.f;
TreeItem *item = nullptr;
@@ -719,12 +719,12 @@ void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
item->set_text(0, _pad_zeros(String::num_int64(c, 16)));
item->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT);
item->set_selectable(0, false);
- item->set_custom_bg_color(0, glyph_table->get_theme_color("dark_color_3", "Editor"));
+ item->set_custom_bg_color(0, glyph_table->get_theme_color(SNAME("dark_color_3"), SNAME("Editor")));
}
if (font_main->has_char(c)) {
item->set_text(col + 1, String::chr(c));
item->set_custom_color(col + 1, Color(1, 1, 1));
- if (selected_chars.has(c) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, c)))) {
+ if (selected_chars.has(c) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size(SNAME("font_size")) * 2, c)))) {
item->set_custom_color(col + 1, fcol);
item->set_custom_bg_color(col + 1, scol);
} else {
@@ -732,13 +732,13 @@ void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
item->clear_custom_bg_color(col + 1);
}
} else {
- item->set_custom_bg_color(col + 1, glyph_table->get_theme_color("dark_color_2", "Editor"));
+ item->set_custom_bg_color(col + 1, glyph_table->get_theme_color(SNAME("dark_color_2"), SNAME("Editor")));
}
item->set_metadata(col + 1, c);
item->set_text_alignment(col + 1, HORIZONTAL_ALIGNMENT_CENTER);
item->set_selectable(col + 1, true);
item->set_custom_font(col + 1, font_main);
- item->set_custom_font_size(col + 1, get_theme_font_size("font_size") * 2);
+ item->set_custom_font_size(col + 1, get_theme_font_size(SNAME("font_size")) * 2);
col++;
if (col == 16) {
@@ -752,8 +752,8 @@ bool DynamicFontImportSettings::_char_update(int32_t p_char) {
if (selected_chars.has(p_char)) {
selected_chars.erase(p_char);
return false;
- } else if (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char))) {
- selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char));
+ } else if (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size(SNAME("font_size")) * 2, p_char))) {
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size(SNAME("font_size")) * 2, p_char));
return false;
} else {
selected_chars.insert(p_char);
@@ -767,7 +767,7 @@ void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
for (int32_t i = p_start; i <= p_end; i++) {
if (font_main->has_char(i)) {
if (font_main->get_data(0).is_valid()) {
- all_selected = all_selected && (selected_chars.has(i) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i))));
+ all_selected = all_selected && (selected_chars.has(i) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size(SNAME("font_size")) * 2, i))));
} else {
all_selected = all_selected && selected_chars.has(i);
}
@@ -780,7 +780,7 @@ void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
} else {
selected_chars.erase(i);
if (font_main->get_data(0).is_valid()) {
- selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i));
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size(SNAME("font_size")) * 2, i));
}
}
}
@@ -809,7 +809,7 @@ void DynamicFontImportSettings::_lang_add_item(const String &p_locale) {
lang_item->set_checked(0, false);
lang_item->set_text(1, p_locale);
lang_item->set_editable(1, true);
- lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ lang_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
lang_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
@@ -841,7 +841,7 @@ void DynamicFontImportSettings::_ot_add_item(int p_option) {
ot_item->set_editable(0, false);
ot_item->set_text(1, "1");
ot_item->set_editable(1, true);
- ot_item->add_button(2, ot_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ ot_item->add_button(2, ot_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
ot_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
@@ -868,7 +868,7 @@ void DynamicFontImportSettings::_script_add_item(int p_option) {
script_item->set_checked(0, false);
script_item->set_text(1, script_codes[p_option]);
script_item->set_editable(1, true);
- script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ script_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
script_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
@@ -895,10 +895,10 @@ void DynamicFontImportSettings::_notification(int p_what) {
if (p_what == NOTIFICATION_READY) {
connect("confirmed", callable_mp(this, &DynamicFontImportSettings::_re_import));
} else if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
- add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
- add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
- add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
- add_ot->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_lang->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ add_script->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ add_var->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+ add_ot->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
}
}
@@ -1043,7 +1043,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
inspector_vars->edit(nullptr);
inspector_general->edit(nullptr);
- int gww = get_theme_font("font")->get_string_size("00000", get_theme_font_size("font_size")).x + 50;
+ int gww = get_theme_font(SNAME("font"))->get_string_size("00000", get_theme_font_size(SNAME("font_size"))).x + 50;
glyph_table->set_column_custom_minimum_width(0, gww);
glyph_table->clear();
@@ -1146,7 +1146,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
vars_item->set_text(0, TTR("Configuration") + " " + itos(i));
vars_item->set_editable(0, true);
- vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->add_button(1, vars_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
Ref<DynamicFontImportSettingsData> import_variation_data_custom;
@@ -1184,7 +1184,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
lang_item->set_checked(0, true);
lang_item->set_text(1, _langs[i]);
lang_item->set_editable(1, true);
- lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ lang_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
}
} else if (key == "support_overrides/language_disabled") {
PackedStringArray _langs = config->get_value("params", key);
@@ -1197,7 +1197,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
lang_item->set_checked(0, false);
lang_item->set_text(1, _langs[i]);
lang_item->set_editable(1, true);
- lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ lang_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
}
} else if (key == "support_overrides/script_enabled") {
PackedStringArray _scripts = config->get_value("params", key);
@@ -1210,7 +1210,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
script_item->set_checked(0, true);
script_item->set_text(1, _scripts[i]);
script_item->set_editable(1, true);
- script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ script_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
}
} else if (key == "support_overrides/script_disabled") {
PackedStringArray _scripts = config->get_value("params", key);
@@ -1223,7 +1223,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
script_item->set_checked(0, false);
script_item->set_text(1, _scripts[i]);
script_item->set_editable(1, true);
- script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ script_item->add_button(2, lang_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
}
} else if (key == "opentype_feature_overrides") {
Dictionary features = config->get_value("params", key);
@@ -1239,7 +1239,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
ot_item->set_editable(0, false);
ot_item->set_text(1, itos(value));
ot_item->set_editable(1, true);
- ot_item->add_button(2, ot_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ ot_item->add_button(2, ot_list->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_REMOVE_VAR, false, TTR("Remove"));
ot_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
} else {
@@ -1356,7 +1356,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
menu_ot->add_child(menu_ot_cu);
menu_ot_cu->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add_item));
- Color warn_color = (EditorNode::get_singleton()) ? EditorNode::get_singleton()->get_gui_base()->get_theme_color("warning_color", "Editor") : Color(1, 1, 0);
+ Color warn_color = (EditorNode::get_singleton()) ? EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("warning_color"), SNAME("Editor")) : Color(1, 1, 0);
// Root layout
@@ -1438,7 +1438,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
add_var = memnew(Button);
page2_hb_vars->add_child(add_var);
add_var->set_tooltip(TTR("Add configuration"));
- add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_var->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
add_var->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_variation_add));
vars_list = memnew(Tree);
@@ -1545,8 +1545,8 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
for (int i = 0; i < 16; i++) {
glyph_table->set_column_title(i + 1, String::num_int64(i, 16));
}
- glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox("bg"));
- glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox("bg"));
+ glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox(SNAME("bg")));
+ glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox(SNAME("bg")));
glyph_table->add_theme_constant_override("hseparation", 0);
glyph_table->set_h_size_flags(Control::SIZE_EXPAND_FILL);
glyph_table->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -1590,7 +1590,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
add_lang = memnew(Button);
hb_lang->add_child(add_lang);
add_lang->set_tooltip(TTR("Add language override"));
- add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_lang->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
add_lang->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add));
lang_list = memnew(Tree);
@@ -1618,7 +1618,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
add_script = memnew(Button);
hb_script->add_child(add_script);
add_script->set_tooltip(TTR("Add script override"));
- add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_script->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
add_script->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_script_add));
script_list = memnew(Tree);
@@ -1646,7 +1646,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
add_ot = memnew(Button);
hb_ot->add_child(add_ot);
add_ot->set_tooltip(TTR("Add feature override"));
- add_ot->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_ot->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
add_ot->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add));
ot_list = memnew(Tree);
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index d63366638e..69e3311fe6 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -32,8 +32,10 @@
#include "resource_importer_texture.h"
+#include "core/error/error_macros.h"
#include "core/io/config_file.h"
#include "core/io/image_loader.h"
+#include "core/object/ref_counted.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
#include "resource_importer_texture.h"
@@ -263,12 +265,12 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
f->store_8('L');
f->store_32(StreamTextureLayered::FORMAT_VERSION);
- f->store_32(p_images.size()); //2d layers or 3d depth
+ f->store_32(p_images.size()); // For 2d layers or 3d depth.
f->store_32(mode);
f->store_32(0);
f->store_32(0);
- f->store_32(mipmap_images.size()); // amount of mipmaps
+ f->store_32(mipmap_images.size()); // Adjust the amount of mipmaps.
f->store_32(0);
f->store_32(0);
@@ -289,7 +291,6 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
int hdr_compression = p_options["compress/hdr_compression"];
int bptc_ldr = p_options["compress/bptc_ldr"];
bool mipmaps = p_options["mipmaps/generate"];
- //bool mipmap_limit = p_options["mipmaps/limit"];
int channel_pack = p_options["compress/channel_pack"];
int hslices = (p_options.has("slices/horizontal")) ? int(p_options["slices/horizontal"]) : 0;
@@ -377,87 +378,23 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
slices.push_back(slice);
}
}
-
- String extension = get_save_extension();
Array formats_imported;
-
- if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
- //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
- //Android, GLES 2.x
-
- bool ok_on_pc = false;
- bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
- bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
- bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
- bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
-
- if (can_bptc) {
- formats_imported.push_back("bptc"); // Needs to be added anyway.
- }
- bool can_compress_hdr = hdr_compression > 0;
-
- if (is_hdr && can_compress_hdr) {
- if (used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA) {
- //can compress hdr, but hdr with alpha is not compressible
-
- if (hdr_compression == 2) {
- //but user selected to compress hdr anyway, so force an alpha-less format.
- if (image->get_format() == Image::FORMAT_RGBAF) {
- for (int i = 0; i < slices.size(); i++) {
- slices.write[i]->convert(Image::FORMAT_RGBF);
- }
-
- } else if (image->get_format() == Image::FORMAT_RGBAH) {
- for (int i = 0; i < slices.size(); i++) {
- slices.write[i]->convert(Image::FORMAT_RGBH);
- }
- }
- } else {
- can_compress_hdr = false;
- }
- }
-
- if (can_compress_hdr) {
- if (!can_bptc) {
- //default to rgbe
- if (image->get_format() != Image::FORMAT_RGBE9995) {
- for (int i = 0; i < slices.size(); i++) {
- slices.write[i]->convert(Image::FORMAT_RGBE9995);
- }
- }
- }
- } else {
- can_bptc = false;
- }
- }
-
- if (is_ldr && can_bptc) {
- if (bptc_ldr == 0 || (bptc_ldr == 1 && !(used_channels == Image::USED_CHANNELS_LA || used_channels == Image::USED_CHANNELS_RGBA))) {
- can_bptc = false;
- }
- }
-
- if (can_bptc || can_s3tc) {
- _save_tex(slices, p_save_path + ".s3tc." + extension, compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, csource, used_channels, mipmaps, false);
- r_platform_variants->push_back("s3tc");
- formats_imported.push_back("s3tc");
- ok_on_pc = true;
- }
-
- if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) {
- _save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
- r_platform_variants->push_back("etc2");
- formats_imported.push_back("etc2");
- }
-
- if (!ok_on_pc) {
- EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC.");
- }
- } else {
- //import normally
- _save_tex(slices, p_save_path + "." + extension, compress_mode, lossy, Image::COMPRESS_S3TC /* IGNORED */, csource, used_channels, mipmaps, false);
- }
-
+ Ref<LayeredTextureImport> texture_import;
+ texture_import.instantiate();
+ texture_import->csource = &csource;
+ texture_import->save_path = p_save_path;
+ texture_import->options = p_options;
+ texture_import->platform_variants = r_platform_variants;
+ texture_import->image = image;
+ texture_import->formats_imported = formats_imported;
+ texture_import->slices = &slices;
+ texture_import->compress_mode = compress_mode;
+ texture_import->lossy = lossy;
+ texture_import->hdr_compression = hdr_compression;
+ texture_import->bptc_ldr = bptc_ldr;
+ texture_import->mipmaps = mipmaps;
+ texture_import->used_channels = used_channels;
+ _check_compress_stex(texture_import);
if (r_metadata) {
Dictionary metadata;
metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
@@ -537,3 +474,76 @@ ResourceImporterLayeredTexture::ResourceImporterLayeredTexture() {
ResourceImporterLayeredTexture::~ResourceImporterLayeredTexture() {
}
+
+void ResourceImporterLayeredTexture::_check_compress_stex(Ref<LayeredTextureImport> r_texture_import) {
+ String extension = get_save_extension();
+ ERR_FAIL_NULL(r_texture_import->csource);
+ if (r_texture_import->compress_mode != COMPRESS_VRAM_COMPRESSED) {
+ // Import normally.
+ _save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
+ return;
+ }
+ // Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
+ // Android, GLES 2.x
+
+ bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
+ if (can_bptc) {
+ r_texture_import->formats_imported.push_back("bptc"); // BPTC needs to be added anyway.
+ }
+ bool can_compress_hdr = r_texture_import->hdr_compression > 0;
+ ERR_FAIL_NULL(r_texture_import->image);
+ bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995);
+ bool is_ldr = (r_texture_import->image->get_format() >= Image::FORMAT_L8 && r_texture_import->image->get_format() <= Image::FORMAT_RGB565);
+ bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
+ ERR_FAIL_NULL(r_texture_import->slices);
+ // Can compress hdr, but hdr with alpha is not compressible.
+ if (r_texture_import->hdr_compression == 2) {
+ // The user selected to compress hdr anyway, so force an alpha-less format.
+ if (r_texture_import->image->get_format() == Image::FORMAT_RGBAF) {
+ for (int i = 0; i < r_texture_import->slices->size(); i++) {
+ r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBF);
+ }
+
+ } else if (r_texture_import->image->get_format() == Image::FORMAT_RGBAH) {
+ for (int i = 0; i < r_texture_import->slices->size(); i++) {
+ r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBH);
+ }
+ }
+ } else {
+ can_compress_hdr = false;
+ }
+
+ if (is_hdr && can_compress_hdr) {
+ if (!can_bptc) {
+ //default to rgbe
+ if (r_texture_import->image->get_format() != Image::FORMAT_RGBE9995) {
+ for (int i = 0; i < r_texture_import->slices->size(); i++) {
+ r_texture_import->slices->write[i]->convert(Image::FORMAT_RGBE9995);
+ }
+ }
+ }
+ } else {
+ can_bptc = false;
+ }
+
+ if (is_ldr && can_bptc) {
+ if (r_texture_import->bptc_ldr == 0 || (r_texture_import->bptc_ldr == 1 && !(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA))) {
+ can_bptc = false;
+ }
+ }
+ if (!(r_texture_import->used_channels == Image::USED_CHANNELS_LA || r_texture_import->used_channels == Image::USED_CHANNELS_RGBA)) {
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) {
+ _save_tex(*r_texture_import->slices, r_texture_import->save_path + ".etc2." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_ETC2, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, true);
+ r_texture_import->platform_variants->push_back("etc2");
+ r_texture_import->formats_imported.push_back("etc2");
+ }
+
+ if (can_bptc || can_s3tc) {
+ _save_tex(*r_texture_import->slices, r_texture_import->save_path + ".s3tc." + extension, r_texture_import->compress_mode, r_texture_import->lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
+ r_texture_import->platform_variants->push_back("s3tc");
+ r_texture_import->formats_imported.push_back("s3tc");
+ }
+ return;
+ }
+ EditorNode::add_io_error("Warning, no suitable PC VRAM compression enabled in Project Settings. This texture will not display correctly on PC.");
+}
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index ee8e7dc615..edd981c63d 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -33,9 +33,30 @@
#include "core/io/image.h"
#include "core/io/resource_importer.h"
+#include "core/object/ref_counted.h"
class StreamTexture2D;
+class LayeredTextureImport : public RefCounted {
+ GDCLASS(LayeredTextureImport, RefCounted);
+
+public:
+ Image::CompressSource *csource = nullptr;
+ String save_path;
+ Map<StringName, Variant> options;
+ List<String> *platform_variants = nullptr;
+ Ref<Image> image = nullptr;
+ Array formats_imported;
+ Vector<Ref<Image>> *slices = nullptr;
+ int compress_mode = 0;
+ float lossy = 1.0;
+ int hdr_compression = 0;
+ int bptc_ldr = 0;
+ bool mipmaps = true;
+ Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA;
+ virtual ~LayeredTextureImport() {}
+};
+
class ResourceImporterLayeredTexture : public ResourceImporter {
GDCLASS(ResourceImporterLayeredTexture, ResourceImporter);
@@ -66,6 +87,8 @@ protected:
static ResourceImporterLayeredTexture *singleton;
public:
+ void _check_compress_stex(Ref<LayeredTextureImport> r_texture_import);
+
static ResourceImporterLayeredTexture *get_singleton() { return singleton; }
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 0fefa0f3c4..3eb12353b5 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -39,6 +39,7 @@
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation_region_3d.h"
+#include "scene/3d/occluder_instance_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/3d/vehicle_body_3d.h"
#include "scene/animation/animation_player.h"
@@ -258,8 +259,8 @@ String ResourceImporterScene::get_visible_name() const {
}
void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {
- for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
- E->get()->get_extensions(p_extensions);
+ for (Ref<EditorSceneFormatImporter> importer_elem : importers) {
+ importer_elem->get_extensions(p_extensions);
}
}
@@ -316,7 +317,7 @@ static bool _teststr(const String &p_what, const String &p_str) {
String what = p_what;
//remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this
- while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
+ while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
what = what.substr(0, what.length() - 1);
}
@@ -336,7 +337,7 @@ static String _fixstr(const String &p_what, const String &p_str) {
String what = p_what;
//remove trailing spaces and numbers, some apps like blender add ".number" to duplicates so also compensate for this
- while (what.length() && ((what[what.length() - 1] >= '0' && what[what.length() - 1] <= '9') || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
+ while (what.length() && (is_digit(what[what.length() - 1]) || what[what.length() - 1] <= 32 || what[what.length() - 1] == '.')) {
what = what.substr(0, what.length() - 1);
}
@@ -371,10 +372,10 @@ static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r
}
}
-Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames) {
+Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames) {
// Children first.
for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map, r_node_renames);
+ Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames);
if (!r) {
i--; // Was erased.
}
@@ -498,14 +499,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
if (mesh.is_valid()) {
Vector<Ref<Shape3D>> shapes;
- if (collision_map.has(mesh)) {
- shapes = collision_map[mesh];
+ if (r_collision_map.has(mesh)) {
+ shapes = r_collision_map[mesh];
} else if (_teststr(name, "colonly")) {
_pre_gen_shape_list(mesh, shapes, false);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
} else if (_teststr(name, "convcolonly")) {
_pre_gen_shape_list(mesh, shapes, true);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
}
if (shapes.size()) {
@@ -560,8 +561,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
if (mesh.is_valid()) {
Vector<Ref<Shape3D>> shapes;
- if (collision_map.has(mesh)) {
- shapes = collision_map[mesh];
+ if (r_collision_map.has(mesh)) {
+ shapes = r_collision_map[mesh];
} else {
_pre_gen_shape_list(mesh, shapes, true);
}
@@ -586,14 +587,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
if (mesh.is_valid()) {
Vector<Ref<Shape3D>> shapes;
String fixed_name;
- if (collision_map.has(mesh)) {
- shapes = collision_map[mesh];
+ if (r_collision_map.has(mesh)) {
+ shapes = r_collision_map[mesh];
} else if (_teststr(name, "col")) {
_pre_gen_shape_list(mesh, shapes, false);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
} else if (_teststr(name, "convcol")) {
_pre_gen_shape_list(mesh, shapes, true);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
}
if (_teststr(name, "col")) {
@@ -635,7 +636,31 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
p_node->replace_by(nmi);
memdelete(p_node);
p_node = nmi;
+ } else if (_teststr(name, "occ") || _teststr(name, "occonly")) {
+ if (isroot) {
+ return p_node;
+ }
+ ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
+ if (mi) {
+ Ref<ImporterMesh> mesh = mi->get_mesh();
+ if (mesh.is_valid()) {
+ if (r_occluder_arrays) {
+ OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
+ }
+ if (_teststr(name, "occ")) {
+ String fixed_name = _fixstr(name, "occ");
+ if (!fixed_name.is_empty()) {
+ if (mi->get_parent() && !mi->get_parent()->has_node(fixed_name)) {
+ mi->set_name(fixed_name);
+ }
+ }
+ } else {
+ memdelete(p_node);
+ p_node = nullptr;
+ }
+ }
+ }
} else if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
//last attempt, maybe collision inside the mesh data
@@ -644,16 +669,21 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
Ref<ImporterMesh> mesh = mi->get_mesh();
if (!mesh.is_null()) {
Vector<Ref<Shape3D>> shapes;
- if (collision_map.has(mesh)) {
- shapes = collision_map[mesh];
+ if (r_collision_map.has(mesh)) {
+ shapes = r_collision_map[mesh];
} else if (_teststr(mesh->get_name(), "col")) {
_pre_gen_shape_list(mesh, shapes, false);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
mesh->set_name(_fixstr(mesh->get_name(), "col"));
} else if (_teststr(mesh->get_name(), "convcol")) {
_pre_gen_shape_list(mesh, shapes, true);
- collision_map[mesh] = shapes;
+ r_collision_map[mesh] = shapes;
mesh->set_name(_fixstr(mesh->get_name(), "convcol"));
+ } else if (_teststr(mesh->get_name(), "occ")) {
+ if (r_occluder_arrays) {
+ OccluderInstance3D::bake_single_node(mi, 0.0f, r_occluder_arrays->first, r_occluder_arrays->second);
+ }
+ mesh->set_name(_fixstr(mesh->get_name(), "occ"));
}
if (shapes.size()) {
@@ -677,10 +707,10 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
return p_node;
}
-Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
+Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
// children first
for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
+ Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
if (!r) {
i--; //was erased
}
@@ -883,6 +913,32 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
+ if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {
+ ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
+
+ Ref<ImporterMesh> m = mi->get_mesh();
+
+ if (m.is_valid()) {
+ if (node_settings.has("generate/occluder")) {
+ int occluder_mode = node_settings["generate/occluder"];
+
+ if (occluder_mode != OCCLUDER_DISABLED) {
+ float simplification_dist = 0.0f;
+ if (node_settings.has("occluder/simplification_distance")) {
+ simplification_dist = node_settings["occluder/simplification_distance"];
+ }
+
+ OccluderInstance3D::bake_single_node(mi, simplification_dist, r_occluder_arrays.first, r_occluder_arrays.second);
+
+ if (occluder_mode == OCCLUDER_OCCLUDER_ONLY) {
+ memdelete(p_node);
+ p_node = nullptr;
+ }
+ }
+ }
+ }
+ }
+
if (Object::cast_to<AnimationPlayer>(p_node)) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
@@ -1255,6 +1311,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/occluder", PROPERTY_HINT_ENUM, "Disabled,Mesh + Occluder,Occluder Only", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0.1f));
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
@@ -1376,6 +1435,11 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
(physics_shape == SHAPE_TYPE_CYLINDER ||
physics_shape == SHAPE_TYPE_CAPSULE);
}
+
+ if (p_option == "occluder/simplification_distance") {
+ // Show only if occluder generation is enabled
+ return p_options.has("generate/occluder") && p_options["generate/occluder"].operator signed int() != OCCLUDER_DISABLED;
+ }
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
@@ -1490,8 +1554,8 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
post_importer_plugins.write[i]->get_import_options(p_path, r_options);
}
- for (Ref<EditorSceneFormatImporter> importer : importers) {
- importer->get_import_options(p_path, r_options);
+ for (Ref<EditorSceneFormatImporter> importer_elem : importers) {
+ importer_elem->get_import_options(p_path, r_options);
}
}
@@ -1843,13 +1907,13 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);
progress.step(TTR("Importing Scene..."), 0);
- for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Ref<EditorSceneFormatImporter> importer_elem : importers) {
List<String> extensions;
- E->get()->get_extensions(&extensions);
+ importer_elem->get_extensions(&extensions);
for (const String &F : extensions) {
if (F.to_lower() == ext) {
- importer = E->get();
+ importer = importer_elem;
break;
}
}
@@ -1869,7 +1933,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
List<Pair<NodePath, Node *>> node_renames;
- _pre_fix_node(scene, scene, collision_map, node_renames);
+ _pre_fix_node(scene, scene, collision_map, nullptr, node_renames);
return scene;
}
@@ -1883,13 +1947,13 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
EditorProgress progress("import", TTR("Import Scene"), 104);
progress.step(TTR("Importing Scene..."), 0);
- for (Set<Ref<EditorSceneFormatImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ for (Ref<EditorSceneFormatImporter> importer_elem : importers) {
List<String> extensions;
- E->get()->get_extensions(&extensions);
+ importer_elem->get_extensions(&extensions);
for (const String &F : extensions) {
if (F.to_lower() == ext) {
- importer = E->get();
+ importer = importer_elem;
break;
}
}
@@ -1944,15 +2008,16 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
Set<Ref<ImporterMesh>> scanned_meshes;
Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
+ Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;
List<Pair<NodePath, Node *>> node_renames;
- _pre_fix_node(scene, scene, collision_map, node_renames);
+ _pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames);
for (int i = 0; i < post_importer_plugins.size(); i++) {
post_importer_plugins.write[i]->pre_process(scene, p_options);
}
- _post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
+ _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps);
String root_type = p_options["nodes/root_type"];
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
@@ -1989,6 +2054,15 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
scene->set_name(p_save_path.get_file().get_basename());
}
+ if (!occluder_arrays.first.is_empty() && !occluder_arrays.second.is_empty()) {
+ Ref<ArrayOccluder3D> occ = memnew(ArrayOccluder3D);
+ occ->set_arrays(occluder_arrays.first, occluder_arrays.second);
+ OccluderInstance3D *occluder_instance = memnew(OccluderInstance3D);
+ occluder_instance->set_occluder(occ);
+ scene->add_child(occluder_instance, true);
+ occluder_instance->set_owner(scene);
+ }
+
bool gen_lods = bool(p_options["meshes/generate_lods"]);
bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);
int light_bake_mode = p_options["meshes/light_baking"];
@@ -2088,6 +2162,32 @@ ResourceImporterScene::ResourceImporterScene() {
singleton = this;
}
+void ResourceImporterScene::add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority) {
+ ERR_FAIL_COND(p_importer.is_null());
+ if (p_first_priority) {
+ importers.insert(0, p_importer);
+ } else {
+ importers.push_back(p_importer);
+ }
+}
+
+void ResourceImporterScene::remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) {
+ post_importer_plugins.erase(p_plugin);
+}
+
+void ResourceImporterScene::add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority) {
+ ERR_FAIL_COND(p_plugin.is_null());
+ if (p_first_priority) {
+ post_importer_plugins.insert(0, p_plugin);
+ } else {
+ post_importer_plugins.push_back(p_plugin);
+ }
+}
+
+void ResourceImporterScene::remove_importer(Ref<EditorSceneFormatImporter> p_importer) {
+ importers.erase(p_importer);
+}
+
///////////////////////////////////////
uint32_t EditorSceneFormatImporterESCN::get_import_flags() const {
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 13b55b5754..a819be682d 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -153,7 +153,7 @@ VARIANT_ENUM_CAST(EditorScenePostImportPlugin::InternalImportCategory)
class ResourceImporterScene : public ResourceImporter {
GDCLASS(ResourceImporterScene, ResourceImporter);
- Set<Ref<EditorSceneFormatImporter>> importers;
+ Vector<Ref<EditorSceneFormatImporter>> importers;
static ResourceImporterScene *singleton;
@@ -178,6 +178,12 @@ class ResourceImporterScene : public ResourceImporter {
NAVMESH_NAVMESH_ONLY,
};
+ enum OccluderMode {
+ OCCLUDER_DISABLED,
+ OCCLUDER_MESH_AND_OCCLUDER,
+ OCCLUDER_OCCLUDER_ONLY,
+ };
+
enum MeshOverride {
MESH_OVERRIDE_DEFAULT,
MESH_OVERRIDE_ENABLE,
@@ -224,13 +230,13 @@ class ResourceImporterScene : public ResourceImporter {
public:
static ResourceImporterScene *get_singleton() { return singleton; }
- void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) { post_importer_plugins.push_back(p_plugin); }
- void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin) { post_importer_plugins.erase(p_plugin); }
+ void add_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin, bool p_first_priority = false);
+ void remove_post_importer_plugin(const Ref<EditorScenePostImportPlugin> &p_plugin);
- const Set<Ref<EditorSceneFormatImporter>> &get_importers() const { return importers; }
+ const Vector<Ref<EditorSceneFormatImporter>> &get_importers() const { return importers; }
- void add_importer(Ref<EditorSceneFormatImporter> p_importer) { importers.insert(p_importer); }
- void remove_importer(Ref<EditorSceneFormatImporter> p_importer) { importers.erase(p_importer); }
+ void add_importer(Ref<EditorSceneFormatImporter> p_importer, bool p_first_priority = false);
+ void remove_importer(Ref<EditorSceneFormatImporter> p_importer);
virtual String get_importer_name() const override;
virtual String get_visible_name() const override;
@@ -261,8 +267,8 @@ public:
// Import scenes *after* everything else (such as textures).
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
- Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, List<Pair<NodePath, Node *>> &r_node_renames);
- Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
+ Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames);
+ Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, Set<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 69c705ed5a..127cd4511e 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -496,11 +496,10 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
//must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
//Android, GLES 2.x
- bool ok_on_pc = false;
- bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
+ const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
- bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
- bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
+ const bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
+ const bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
if (can_bptc) {
//add to the list anyway
@@ -525,29 +524,24 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
}
}
- if (can_compress_hdr) {
- if (!can_bptc) {
- //fallback to RGBE99995
- if (image->get_format() != Image::FORMAT_RGBE9995) {
- image->convert(Image::FORMAT_RGBE9995);
- }
+ if (!can_compress_hdr) {
+ //fallback to RGBE99995
+ if (image->get_format() != Image::FORMAT_RGBE9995) {
+ image->convert(Image::FORMAT_RGBE9995);
}
- } else {
- can_bptc = false;
- }
- }
-
- if (is_ldr && can_bptc) {
- if (bptc_ldr == 0 || (bptc_ldr == 1 && !has_alpha)) {
- can_bptc = false;
}
}
+ bool ok_on_pc = false;
if (can_bptc || can_s3tc) {
- _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
+ ok_on_pc = true;
+ Image::CompressMode image_compress_mode = Image::COMPRESS_BPTC;
+ if (!bptc_ldr && can_s3tc && is_ldr) {
+ image_compress_mode = Image::COMPRESS_S3TC;
+ }
+ _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back("s3tc");
formats_imported.push_back("s3tc");
- ok_on_pc = true;
}
if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) {
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index e46c81b77e..40d6bc48e7 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -300,7 +300,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
base_name = add_options[p_idx].name;
} else {
ERR_FAIL_COND(add_options[p_idx].script.is_null());
- String base_type = add_options[p_idx].script->get_instance_base_type();
+ StringName base_type = add_options[p_idx].script->get_instance_base_type();
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(base_type));
ERR_FAIL_COND(!an);
anode = Ref<AnimationNode>(an);
@@ -833,7 +833,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima
const String &new_name = p_text;
- ERR_FAIL_COND(new_name.is_empty() || new_name.find(".") != -1 || new_name.find("/") != -1);
+ ERR_FAIL_COND(new_name.is_empty() || new_name.contains(".") || new_name.contains("/"));
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 83c2b53241..320c47e820 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -485,7 +485,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
player->stop();
String new_name = name->get_text();
- if (new_name.is_empty() || new_name.find(":") != -1 || new_name.find("/") != -1) {
+ if (new_name.is_empty() || new_name.contains(":") || new_name.contains("/")) {
error_dialog->set_text(TTR("Invalid animation name!"));
error_dialog->popup_centered();
return;
@@ -746,7 +746,7 @@ void AnimationPlayerEditor::_load_animations(Vector<String> p_files) {
file = file.substr(file.rfind("\\") + 1, file.length());
}
- if (file.find(".") != -1) {
+ if (file.contains(".")) {
file = file.substr(0, file.find("."));
}
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 649fc53b3a..f750c92fb3 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -1059,7 +1059,7 @@ void AnimationNodeStateMachineEditor::_removed_from_graph() {
void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
const String &new_name = p_text;
- ERR_FAIL_COND(new_name.is_empty() || new_name.find(".") != -1 || new_name.find("/") != -1);
+ ERR_FAIL_COND(new_name.is_empty() || new_name.contains(".") || new_name.contains("/"));
if (new_name == prev_name) {
return; // Nothing to do.
diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
new file mode 100644
index 0000000000..fea3c29967
--- /dev/null
+++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* audio_stream_randomizer_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "audio_stream_randomizer_editor_plugin.h"
+
+void AudioStreamRandomizerEditorPlugin::edit(Object *p_object) {
+}
+
+bool AudioStreamRandomizerEditorPlugin::handles(Object *p_object) const {
+ return false;
+}
+
+void AudioStreamRandomizerEditorPlugin::make_visible(bool p_visible) {
+}
+
+void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+ AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited);
+ if (!randomizer) {
+ return;
+ }
+
+ // Compute the array indices to save.
+ int begin = 0;
+ int end;
+ if (p_array_prefix == "stream_") {
+ end = randomizer->get_streams_count();
+ } else {
+ ERR_FAIL_MSG("Invalid array prefix for AudioStreamRandomizer.");
+ }
+ if (p_from_index < 0) {
+ // Adding new.
+ if (p_to_pos >= 0) {
+ begin = p_to_pos;
+ } else {
+ end = 0; // Nothing to save when adding at the end.
+ }
+ } else if (p_to_pos < 0) {
+ // Removing.
+ begin = p_from_index;
+ } else {
+ // Moving.
+ begin = MIN(p_from_index, p_to_pos);
+ end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
+ }
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
+ // Save layers' properties.
+ if (p_from_index < 0) {
+ undo_redo->add_undo_method(randomizer, "remove_stream", p_to_pos < 0 ? randomizer->get_streams_count() : p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_undo_method(randomizer, "add_stream", p_from_index);
+ }
+
+ List<PropertyInfo> properties;
+ randomizer->get_property_list(&properties);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(p_array_prefix)) {
+ String str = pi.name.trim_prefix(p_array_prefix);
+ int to_char_index = 0;
+ while (to_char_index < str.length()) {
+ if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ break;
+ }
+ to_char_index++;
+ }
+ if (to_char_index > 0) {
+ int array_index = str.left(to_char_index).to_int();
+ if (array_index >= begin && array_index < end) {
+ ADD_UNDO(randomizer, pi.name);
+ }
+ }
+ }
+ }
+#undef ADD_UNDO
+
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(randomizer, "add_stream", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(randomizer, "remove_stream", p_from_index);
+ } else {
+ undo_redo->add_do_method(randomizer, "move_stream", p_from_index, p_to_pos);
+ }
+}
+
+AudioStreamRandomizerEditorPlugin::AudioStreamRandomizerEditorPlugin(EditorNode *p_node) {
+ EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("AudioStreamRandomizer"), callable_mp(this, &AudioStreamRandomizerEditorPlugin::_move_stream_array_element));
+}
+
+AudioStreamRandomizerEditorPlugin::~AudioStreamRandomizerEditorPlugin() {}
diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.h b/editor/plugins/audio_stream_randomizer_editor_plugin.h
new file mode 100644
index 0000000000..490af7e2c2
--- /dev/null
+++ b/editor/plugins/audio_stream_randomizer_editor_plugin.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* audio_stream_randomizer_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
+#define AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamRandomizerEditorPlugin : public EditorPlugin {
+ GDCLASS(AudioStreamRandomizerEditorPlugin, EditorPlugin);
+
+ EditorNode *editor;
+
+private:
+ void _move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
+
+public:
+ virtual String get_name() const override { return "AudioStreamRandomizer"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ AudioStreamRandomizerEditorPlugin(EditorNode *p_node);
+ ~AudioStreamRandomizerEditorPlugin();
+};
+
+#endif // AUDIO_STREAM_RANDOMIZER_EDITOR_PLUGIN_H
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index d496804bf2..aa8ad55ff3 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5797,22 +5797,17 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
}
- String node_class = child->get_class();
- if (node_class == "Polygon2D") {
- editor_data->get_undo_redo().add_do_property(child, "texture/texture", texture);
- } else if (node_class == "TouchScreenButton") {
- editor_data->get_undo_redo().add_do_property(child, "normal", texture);
- } else if (node_class == "TextureButton") {
- editor_data->get_undo_redo().add_do_property(child, "texture_button", texture);
+ if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) {
+ editor_data->get_undo_redo().add_do_property(child, "texture_normal", texture);
} else {
editor_data->get_undo_redo().add_do_property(child, "texture", texture);
}
// make visible for certain node type
- if (ClassDB::is_parent_class(node_class, "Control")) {
+ if (Object::cast_to<Control>(child)) {
Size2 texture_size = texture->get_size();
editor_data->get_undo_redo().add_do_property(child, "rect_size", texture_size);
- } else if (node_class == "Polygon2D") {
+ } else if (Object::cast_to<Polygon2D>(child)) {
Size2 texture_size = texture->get_size();
Vector<Vector2> list = {
Vector2(0, 0),
@@ -5940,29 +5935,32 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
bool can_instantiate = false;
- for (int i = 0; i < files.size(); i++) { // check if dragged files contain resource or scene can be created at least once
- RES res = ResourceLoader::load(files[i]);
- if (res.is_null()) {
- continue;
- }
- String type = res->get_class();
- if (type == "PackedScene") {
- Ref<PackedScene> sdata = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
- Node *instantiated_scene = sdata->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
- if (!instantiated_scene) {
+
+ List<String> scene_extensions;
+ ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
+ List<String> texture_extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Texture2D", &texture_extensions);
+
+ for (int i = 0; i < files.size(); i++) {
+ // Check if dragged files with texture or scene extension can be created at least once.
+ if (texture_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) {
+ RES res = ResourceLoader::load(files[i]);
+ if (res.is_null()) {
continue;
}
- memdelete(instantiated_scene);
- } else if (ClassDB::is_parent_class(type, "Texture2D")) {
- Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(*res));
- if (!texture.is_valid()) {
+ Ref<PackedScene> scn = res;
+ if (scn.is_valid()) {
+ Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
+ if (!instantiated_scene) {
+ continue;
+ }
+ memdelete(instantiated_scene);
+ } else {
continue;
}
- } else {
- continue;
+ can_instantiate = true;
+ break;
}
- can_instantiate = true;
- break;
}
if (can_instantiate) {
if (!preview_node->get_parent()) { // create preview only once
@@ -6069,10 +6067,21 @@ Node *CanvasItemEditorViewport::_make_texture_node_type(String texture_node_type
}
void CanvasItemEditorViewport::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ List<BaseButton *> btn_list;
+ button_group->get_buttons(&btn_list);
+
+ for (int i = 0; i < btn_list.size(); i++) {
+ CheckBox *check = Object::cast_to<CheckBox>(btn_list[i]);
+ check->set_icon(get_theme_icon(check->get_text(), SNAME("EditorIcons")));
+ }
+
+ label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+ }
+
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
connect("mouse_exited", callable_mp(this, &CanvasItemEditorViewport::_on_mouse_exit));
- label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
} break;
case NOTIFICATION_EXIT_TREE: {
disconnect("mouse_exited", callable_mp(this, &CanvasItemEditorViewport::_on_mouse_exit));
@@ -6123,7 +6132,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
btn_group = memnew(VBoxContainer);
vbc->add_child(btn_group);
- btn_group->set_h_size_flags(0);
+ btn_group->set_h_size_flags(SIZE_EXPAND_FILL);
button_group.instantiate();
for (int i = 0; i < texture_node_types.size(); i++) {
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index cef505181a..d23b52014e 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -188,7 +188,7 @@ bool EditorImagePreviewPlugin::generate_small_preview_automatically() const {
}
////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////
+
bool EditorBitmapPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "BitMap");
}
@@ -308,7 +308,7 @@ void EditorMaterialPreviewPlugin::_preview_done() {
}
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
- return ClassDB::is_parent_class(p_type, "Material"); //any material
+ return ClassDB::is_parent_class(p_type, "Material"); // Any material.
}
bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const {
@@ -462,10 +462,6 @@ EditorMaterialPreviewPlugin::~EditorMaterialPreviewPlugin() {
///////////////////////////////////////////////////////////////////////////
-static bool _is_text_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
bool EditorScriptPreviewPlugin::handles(const String &p_type) const {
return ClassDB::is_parent_class(p_type, "Script");
}
@@ -538,15 +534,15 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
if (in_comment) {
color = comment_color;
} else {
- if (c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t')) {
+ if (is_symbol(c)) {
//make symbol a little visible
color = symbol_color;
in_control_flow_keyword = false;
in_keyword = false;
- } else if (!prev_is_text && _is_text_char(c)) {
+ } else if (!prev_is_text && is_ascii_identifier_char(c)) {
int pos = i;
- while (_is_text_char(code[pos])) {
+ while (is_ascii_identifier_char(code[pos])) {
pos++;
}
String word = code.substr(i, pos - i);
@@ -556,7 +552,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
in_keyword = true;
}
- } else if (!_is_text_char(c)) {
+ } else if (!is_ascii_identifier_char(c)) {
in_keyword = false;
}
@@ -571,7 +567,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
img->set_pixel(col, y0 + line * 2, bg_color.blend(ul));
img->set_pixel(col, y0 + line * 2 + 1, color);
- prev_is_text = _is_text_char(c);
+ prev_is_text = is_ascii_identifier_char(c);
}
col++;
} else {
@@ -703,7 +699,7 @@ void EditorMeshPreviewPlugin::_preview_done() {
}
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
- return ClassDB::is_parent_class(p_type, "Mesh"); //any Mesh
+ return ClassDB::is_parent_class(p_type, "Mesh"); // Any mesh.
}
Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 &p_size) const {
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 59ba49232e..b4b1cf05ac 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -1627,7 +1627,7 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
//////
AudioListener3DGizmoPlugin::AudioListener3DGizmoPlugin() {
- create_icon_material("audio_listener_3d_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoAudioListener3D", "EditorIcons"));
+ create_icon_material("audio_listener_3d_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoAudioListener3D"), SNAME("EditorIcons")));
}
bool AudioListener3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
@@ -1889,6 +1889,7 @@ void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() {
create_material("line_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1)));
+ create_handle_material("handles");
}
bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
@@ -1903,6 +1904,189 @@ int OccluderInstance3DGizmoPlugin::get_priority() const {
return -1;
}
+String OccluderInstance3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
+ const OccluderInstance3D *cs = Object::cast_to<OccluderInstance3D>(p_gizmo->get_spatial_node());
+
+ Ref<Occluder3D> o = cs->get_occluder();
+ if (o.is_null()) {
+ return "";
+ }
+
+ if (Object::cast_to<SphereOccluder3D>(*o)) {
+ return "Radius";
+ }
+
+ if (Object::cast_to<BoxOccluder3D>(*o) || Object::cast_to<QuadOccluder3D>(*o)) {
+ return "Size";
+ }
+
+ return "";
+}
+
+Variant OccluderInstance3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
+ OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_spatial_node());
+
+ Ref<Occluder3D> o = oi->get_occluder();
+ if (o.is_null()) {
+ return Variant();
+ }
+
+ if (Object::cast_to<SphereOccluder3D>(*o)) {
+ Ref<SphereOccluder3D> so = o;
+ return so->get_radius();
+ }
+
+ if (Object::cast_to<BoxOccluder3D>(*o)) {
+ Ref<BoxOccluder3D> bo = o;
+ return bo->get_size();
+ }
+
+ if (Object::cast_to<QuadOccluder3D>(*o)) {
+ Ref<QuadOccluder3D> qo = o;
+ return qo->get_size();
+ }
+
+ return Variant();
+}
+
+void OccluderInstance3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
+ OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_spatial_node());
+
+ Ref<Occluder3D> o = oi->get_occluder();
+ if (o.is_null()) {
+ return;
+ }
+
+ Transform3D gt = oi->get_global_transform();
+ Transform3D gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
+
+ bool snap_enabled = Node3DEditor::get_singleton()->is_snap_enabled();
+ float snap = Node3DEditor::get_singleton()->get_translate_snap();
+
+ if (Object::cast_to<SphereOccluder3D>(*o)) {
+ Ref<SphereOccluder3D> so = o;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ float d = ra.x;
+ if (snap_enabled) {
+ d = Math::snapped(d, snap);
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ so->set_radius(d);
+ }
+
+ if (Object::cast_to<BoxOccluder3D>(*o)) {
+ Vector3 axis;
+ axis[p_id] = 1.0;
+ Ref<BoxOccluder3D> bo = o;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_id];
+ if (snap_enabled) {
+ d = Math::snapped(d, snap);
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ Vector3 he = bo->get_size();
+ he[p_id] = d * 2;
+ bo->set_size(he);
+ }
+
+ if (Object::cast_to<QuadOccluder3D>(*o)) {
+ Ref<QuadOccluder3D> qo = o;
+ Plane p = Plane(Vector3(0.0f, 0.0f, 1.0f), 0.0f);
+ Vector3 intersection;
+ if (!p.intersects_segment(sg[0], sg[1], &intersection)) {
+ return;
+ }
+
+ if (p_id == 2) {
+ Vector2 s = Vector2(intersection.x, intersection.y) * 2.0f;
+ if (snap_enabled) {
+ s = s.snapped(Vector2(snap, snap));
+ }
+ s = s.max(Vector2(0.001, 0.001));
+ qo->set_size(s);
+ } else {
+ float d = intersection[p_id];
+ if (snap_enabled) {
+ d = Math::snapped(d, snap);
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ Vector2 he = qo->get_size();
+ he[p_id] = d * 2.0f;
+ qo->set_size(he);
+ }
+ }
+}
+
+void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
+ OccluderInstance3D *oi = Object::cast_to<OccluderInstance3D>(p_gizmo->get_spatial_node());
+
+ Ref<Occluder3D> o = oi->get_occluder();
+ if (o.is_null()) {
+ return;
+ }
+
+ if (Object::cast_to<SphereOccluder3D>(*o)) {
+ Ref<SphereOccluder3D> so = o;
+ if (p_cancel) {
+ so->set_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Sphere Shape Radius"));
+ ur->add_do_method(so.ptr(), "set_radius", so->get_radius());
+ ur->add_undo_method(so.ptr(), "set_radius", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<BoxOccluder3D>(*o)) {
+ Ref<BoxOccluder3D> bo = o;
+ if (p_cancel) {
+ bo->set_size(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Box Shape Size"));
+ ur->add_do_method(bo.ptr(), "set_size", bo->get_size());
+ ur->add_undo_method(bo.ptr(), "set_size", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<QuadOccluder3D>(*o)) {
+ Ref<QuadOccluder3D> qo = o;
+ if (p_cancel) {
+ qo->set_size(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Box Shape Size"));
+ ur->add_do_method(qo.ptr(), "set_size", qo->get_size());
+ ur->add_undo_method(qo.ptr(), "set_size", p_restore);
+ ur->commit_action();
+ }
+}
+
void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
OccluderInstance3D *occluder_instance = Object::cast_to<OccluderInstance3D>(p_gizmo->get_spatial_node());
@@ -1920,6 +2104,35 @@ void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
}
+
+ Ref<Material> handles_material = get_material("handles");
+ if (Object::cast_to<SphereOccluder3D>(*o)) {
+ Ref<SphereOccluder3D> so = o;
+ float r = so->get_radius();
+ Vector<Vector3> handles = { Vector3(r, 0, 0) };
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
+ if (Object::cast_to<BoxOccluder3D>(*o)) {
+ Ref<BoxOccluder3D> bo = o;
+
+ Vector<Vector3> handles;
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = bo->get_size()[i] / 2;
+ handles.push_back(ax);
+ }
+
+ p_gizmo->add_handles(handles, handles_material);
+ }
+
+ if (Object::cast_to<QuadOccluder3D>(*o)) {
+ Ref<QuadOccluder3D> qo = o;
+ Vector2 size = qo->get_size();
+ Vector3 s = Vector3(size.x, size.y, 0.0f) / 2.0f;
+ Vector<Vector3> handles = { Vector3(s.x, 0.0f, 0.0f), Vector3(0.0f, s.y, 0.0f), Vector3(s.x, s.y, 0.0f) };
+ p_gizmo->add_handles(handles, handles_material);
+ }
}
/////
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 66b3f02fcf..fa55651d26 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -300,6 +300,11 @@ public:
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
+ Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override;
+ void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override;
+ void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override;
+
OccluderInstance3DGizmoPlugin();
};
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 0c0188e8d1..2e8ae1a286 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -384,6 +384,28 @@ int Node3DEditorViewport::get_selected_count() const {
return count;
}
+void Node3DEditorViewport::cancel_transform() {
+ _edit.mode = TRANSFORM_NONE;
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ Node3D *sp = Object::cast_to<Node3D>(E->get());
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ sp->set_global_transform(se->original);
+ }
+ surface->update();
+ set_message(TTR("Transform Aborted."), 3);
+}
+
float Node3DEditorViewport::get_znear() const {
return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z);
}
@@ -865,6 +887,7 @@ void Node3DEditorViewport::_update_name() {
}
void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
+ _edit.original_local = spatial_editor->are_local_coords_enabled();
_edit.click_ray = _get_ray(p_point);
_edit.click_ray_pos = _get_ray_pos(p_point);
_edit.plane = TRANSFORM_VIEW;
@@ -1018,24 +1041,40 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
int col_axis = -1;
- float col_d = 1e20;
- for (int i = 0; i < 3; i++) {
- Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
- Vector3 r;
- if (!plane.intersects_ray(ray_pos, ray, &r)) {
- continue;
+ Vector3 hit_position;
+ Vector3 hit_normal;
+ real_t ray_length = gt.origin.distance_to(ray_pos) + (GIZMO_CIRCLE_SIZE * gizmo_scale) * 4.0f;
+ if (Geometry3D::segment_intersects_sphere(ray_pos, ray_pos + ray * ray_length, gt.origin, gizmo_scale * (GIZMO_CIRCLE_SIZE), &hit_position, &hit_normal)) {
+ if (hit_normal.dot(_get_camera_normal()) < 0.05) {
+ hit_position = gt.xform_inv(hit_position).abs();
+ int min_axis = hit_position.min_axis_index();
+ if (hit_position[min_axis] < gizmo_scale * GIZMO_RING_HALF_WIDTH) {
+ col_axis = min_axis;
+ }
}
+ }
+
+ if (col_axis == -1) {
+ float col_d = 1e20;
- const real_t dist = r.distance_to(gt.origin);
- const Vector3 r_dir = (r - gt.origin).normalized();
+ for (int i = 0; i < 3; i++) {
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
+ Vector3 r;
+ if (!plane.intersects_ray(ray_pos, ray, &r)) {
+ continue;
+ }
+
+ const real_t dist = r.distance_to(gt.origin);
+ const Vector3 r_dir = (r - gt.origin).normalized();
- if (_get_camera_normal().dot(r_dir) <= 0.005) {
- if (dist > gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gizmo_scale * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) {
- const real_t d = ray_pos.distance_to(r);
- if (d < col_d) {
- col_d = d;
- col_axis = i;
+ if (_get_camera_normal().dot(r_dir) <= 0.005) {
+ if (dist > gizmo_scale * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gizmo_scale * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) {
+ const real_t d = ray_pos.distance_to(r);
+ if (d < col_d) {
+ col_d = d;
+ col_axis = i;
+ }
}
}
}
@@ -1375,39 +1414,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) {
- //cancel motion
- _edit.mode = TRANSFORM_NONE;
-
- List<Node *> &selection = editor_selection->get_selected_node_list();
-
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (se->gizmo.is_valid()) {
- Vector<int> ids;
- Vector<Transform3D> restore;
-
- for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
- ids.push_back(GE.key);
- restore.push_back(GE.value);
- }
-
- se->gizmo->commit_subgizmos(ids, restore, true);
- spatial_editor->update_transform_gizmo();
- } else {
- sp->set_global_transform(se->original);
- }
- }
- surface->update();
- set_message(TTR("Transform Aborted."), 3);
+ cancel_transform();
}
if (b->is_pressed()) {
@@ -1461,6 +1468,10 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} break;
case MouseButton::LEFT: {
if (b->is_pressed()) {
+ if (_edit.mode != TRANSFORM_NONE && _edit.instant) {
+ commit_transform();
+ break; // just commit the edit, stop processing the event so we don't deselect the object
+ }
NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->is_alt_pressed()) {
break;
@@ -1567,33 +1578,17 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
clicked = ObjectID();
if ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
- /* HANDLE ROTATION */
- if (get_selected_count() == 0) {
- break; //bye
- }
- //handle rotate
- _edit.mode = TRANSFORM_ROTATE;
- _compute_edit(b->get_position());
+ begin_transform(TRANSFORM_ROTATE, false);
break;
}
if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
- if (get_selected_count() == 0) {
- break; //bye
- }
- //handle translate
- _edit.mode = TRANSFORM_TRANSLATE;
- _compute_edit(b->get_position());
+ begin_transform(TRANSFORM_TRANSLATE, false);
break;
}
if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) {
- if (get_selected_count() == 0) {
- break; //bye
- }
- //handle scale
- _edit.mode = TRANSFORM_SCALE;
- _compute_edit(b->get_position());
+ begin_transform(TRANSFORM_SCALE, false);
break;
}
@@ -1648,32 +1643,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
se->gizmo->commit_subgizmos(ids, restore, false);
spatial_editor->update_transform_gizmo();
} else {
- static const char *_transform_name[4] = {
- TTRC("None"),
- TTRC("Rotate"),
- // TRANSLATORS: This refers to the movement that changes the position of an object.
- TTRC("Translate"),
- TTRC("Scale"),
- };
- undo_redo->create_action(TTRGET(_transform_name[_edit.mode]));
-
- List<Node *> &selection = editor_selection->get_selected_node_list();
-
- for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
- Node3D *sp = Object::cast_to<Node3D>(E->get());
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!sel_item) {
- continue;
- }
-
- undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
- undo_redo->add_undo_method(sp, "set_global_transform", sel_item->original);
- }
- undo_redo->commit_action();
+ commit_transform();
}
_edit.mode = TRANSFORM_NONE;
set_message("");
@@ -1739,7 +1709,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle, _edit.gizmo_handle_secondary);
set_message(n + ": " + String(v));
- } else if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
+ } else if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE || _edit.instant) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) {
@@ -1767,324 +1737,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
- Vector3 ray_pos = _get_ray_pos(m->get_position());
- Vector3 ray = _get_ray(m->get_position());
- double snap = EDITOR_GET("interface/inspector/default_float_step");
- int snap_step_decimals = Math::range_step_decimals(snap);
-
- switch (_edit.mode) {
- case TRANSFORM_SCALE: {
- Vector3 motion_mask;
- Plane plane;
- bool plane_mv = false;
-
- switch (_edit.plane) {
- case TRANSFORM_VIEW:
- motion_mask = Vector3(0, 0, 0);
- plane = Plane(_get_camera_normal(), _edit.center);
- break;
- case TRANSFORM_X_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_Y_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_Z_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_YZ:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
- plane_mv = true;
- break;
- case TRANSFORM_XZ:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
- plane_mv = true;
- break;
- case TRANSFORM_XY:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
- plane_mv = true;
- break;
- }
-
- Vector3 intersection;
- if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
- break;
- }
-
- Vector3 click;
- if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
- break;
- }
-
- Vector3 motion = intersection - click;
- if (_edit.plane != TRANSFORM_VIEW) {
- if (!plane_mv) {
- motion = motion_mask.dot(motion) * motion_mask;
-
- } else {
- // Alternative planar scaling mode
- if (_get_key_modifier(m) != Key::SHIFT) {
- motion = motion_mask.dot(motion) * motion_mask;
- }
- }
-
- } else {
- const real_t center_click_dist = click.distance_to(_edit.center);
- const real_t center_inters_dist = intersection.distance_to(_edit.center);
- if (center_click_dist == 0) {
- break;
- }
-
- const real_t scale = center_inters_dist - center_click_dist;
- motion = Vector3(scale, scale, scale);
- }
-
- motion /= click.distance_to(_edit.center);
-
- // Disable local transformation for TRANSFORM_VIEW
- bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
-
- if (_edit.snap || spatial_editor->is_snap_enabled()) {
- snap = spatial_editor->get_scale_snap() / 100;
- }
- Vector3 motion_snapped = motion;
- motion_snapped.snap(Vector3(snap, snap, snap));
- // This might not be necessary anymore after issue #288 is solved (in 4.0?).
- set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
- String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
- motion = _edit.original.basis.inverse().xform(motion);
-
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
- Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
- if (!local_coords) {
- new_xform = se->original.affine_inverse() * new_xform;
- }
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
- _transform_gizmo_apply(se->sp, new_xform, local_coords);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->update();
-
- } break;
-
- case TRANSFORM_TRANSLATE: {
- Vector3 motion_mask;
- Plane plane;
- bool plane_mv = false;
-
- switch (_edit.plane) {
- case TRANSFORM_VIEW:
- plane = Plane(_get_camera_normal(), _edit.center);
- break;
- case TRANSFORM_X_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_Y_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_Z_AXIS:
- motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
- plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
- break;
- case TRANSFORM_YZ:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
- plane_mv = true;
- break;
- case TRANSFORM_XZ:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
- plane_mv = true;
- break;
- case TRANSFORM_XY:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
- plane_mv = true;
- break;
- }
-
- Vector3 intersection;
- if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
- break;
- }
-
- Vector3 click;
- if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
- break;
- }
-
- Vector3 motion = intersection - click;
- if (_edit.plane != TRANSFORM_VIEW) {
- if (!plane_mv) {
- motion = motion_mask.dot(motion) * motion_mask;
- }
- }
-
- // Disable local transformation for TRANSFORM_VIEW
- bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
-
- if (_edit.snap || spatial_editor->is_snap_enabled()) {
- snap = spatial_editor->get_translate_snap();
- }
- Vector3 motion_snapped = motion;
- motion_snapped.snap(Vector3(snap, snap, snap));
- set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
- String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
- motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion);
-
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
- Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
- new_xform = se->original.affine_inverse() * new_xform;
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
- _transform_gizmo_apply(se->sp, new_xform, false);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->update();
-
- } break;
-
- case TRANSFORM_ROTATE: {
- Plane plane;
- Vector3 axis;
-
- switch (_edit.plane) {
- case TRANSFORM_VIEW:
- plane = Plane(_get_camera_normal(), _edit.center);
- break;
- case TRANSFORM_X_AXIS:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
- axis = Vector3(1, 0, 0);
- break;
- case TRANSFORM_Y_AXIS:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
- axis = Vector3(0, 1, 0);
- break;
- case TRANSFORM_Z_AXIS:
- plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
- axis = Vector3(0, 0, 1);
- break;
- case TRANSFORM_YZ:
- case TRANSFORM_XZ:
- case TRANSFORM_XY:
- break;
- }
-
- Vector3 intersection;
- if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
- break;
- }
-
- Vector3 click;
- if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
- break;
- }
-
- Vector3 y_axis = (click - _edit.center).normalized();
- Vector3 x_axis = plane.normal.cross(y_axis).normalized();
-
- double angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center));
-
- if (_edit.snap || spatial_editor->is_snap_enabled()) {
- snap = spatial_editor->get_rotate_snap();
- }
- angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
- angle -= Math::fmod(angle, snap);
- set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));
- angle = Math::deg2rad(angle);
-
- bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
-
- List<Node *> &selection = editor_selection->get_selected_node_list();
- for (Node *E : selection) {
- Node3D *sp = Object::cast_to<Node3D>(E);
- if (!sp) {
- continue;
- }
-
- Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
- if (!se) {
- continue;
- }
-
- if (sp->has_meta("_edit_lock_")) {
- continue;
- }
-
- Vector3 compute_axis = local_coords ? axis : plane.normal;
- if (se->gizmo.is_valid()) {
- for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
- Transform3D xform = GE.value;
-
- Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo.
- if (!local_coords) {
- new_xform = se->original.affine_inverse() * new_xform;
- }
- se->gizmo->set_subgizmo_transform(GE.key, new_xform);
- }
- } else {
- Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
- _transform_gizmo_apply(se->sp, new_xform, local_coords);
- }
- }
-
- spatial_editor->update_transform_gizmo();
- surface->update();
-
- } break;
- default: {
- }
- }
+ update_transform(m->get_position(), _get_key_modifier(m) == Key::SHIFT);
}
} else if ((m->get_button_mask() & MouseButton::MASK_RIGHT) != MouseButton::NONE || freelook_active) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
@@ -2225,6 +1878,51 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
+ if (_edit.mode != TRANSFORM_NONE) {
+ // We're actively transforming, handle keys specially
+ TransformPlane new_plane = TRANSFORM_VIEW;
+ String new_message;
+ if (ED_IS_SHORTCUT("spatial_editor/lock_transform_x", p_event)) {
+ new_plane = TRANSFORM_X_AXIS;
+ new_message = TTR("X-Axis Transform.");
+ } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_y", p_event)) {
+ new_plane = TRANSFORM_Y_AXIS;
+ new_message = TTR("Y-Axis Transform.");
+ } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_z", p_event)) {
+ new_plane = TRANSFORM_Z_AXIS;
+ new_message = TTR("Z-Axis Transform.");
+ } else if (_edit.mode != TRANSFORM_ROTATE) { // rotating on a plane doesn't make sense
+ if (ED_IS_SHORTCUT("spatial_editor/lock_transform_yz", p_event)) {
+ new_plane = TRANSFORM_YZ;
+ new_message = TTR("YZ-Plane Transform.");
+ } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xz", p_event)) {
+ new_plane = TRANSFORM_XZ;
+ new_message = TTR("XZ-Plane Transform.");
+ } else if (ED_IS_SHORTCUT("spatial_editor/lock_transform_xy", p_event)) {
+ new_plane = TRANSFORM_XY;
+ new_message = TTR("XY-Plane Transform.");
+ }
+ }
+
+ if (new_plane != TRANSFORM_VIEW) {
+ if (new_plane != _edit.plane) {
+ // lock me once and get a global constraint
+ _edit.plane = new_plane;
+ spatial_editor->set_local_coords_enabled(false);
+ } else if (!spatial_editor->are_local_coords_enabled()) {
+ // lock me twice and get a local constraint
+ spatial_editor->set_local_coords_enabled(true);
+ } else {
+ // lock me thrice and we're back where we started
+ _edit.plane = TRANSFORM_VIEW;
+ spatial_editor->set_local_coords_enabled(false);
+ }
+ update_transform(_edit.mouse_pos, Input::get_singleton()->is_key_pressed(Key::SHIFT));
+ set_message(new_message, 2);
+ accept_event();
+ return;
+ }
+ }
if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) {
if (_edit.mode != TRANSFORM_NONE) {
_edit.snap = !_edit.snap;
@@ -2315,6 +2013,18 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
set_message(TTR("Animation Key Inserted."));
}
+ if (ED_IS_SHORTCUT("spatial_editor/cancel_transform", p_event) && _edit.mode != TRANSFORM_NONE) {
+ cancel_transform();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/instant_translate", p_event)) {
+ begin_transform(TRANSFORM_TRANSLATE, true);
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/instant_rotate", p_event)) {
+ begin_transform(TRANSFORM_ROTATE, true);
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/instant_scale", p_event)) {
+ begin_transform(TRANSFORM_SCALE, true);
+ }
// Freelook doesn't work in orthogonal mode.
if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) {
@@ -3036,7 +2746,7 @@ void Node3DEditorViewport::_draw() {
font->draw_string(ci, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
}
- if (_edit.mode == TRANSFORM_ROTATE) {
+ if (_edit.mode == TRANSFORM_ROTATE && _edit.show_rotation_line) {
Point2 center = _point_to_screen(_edit.center);
Color handle_color;
@@ -3544,6 +3254,13 @@ void Node3DEditorViewport::_init_gizmo_instance(int p_idx) {
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
RS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer);
RS::get_singleton()->instance_geometry_set_flag(scale_plane_gizmo_instance[i], RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+
+ axis_gizmo_instance[i] = RS::get_singleton()->instance_create();
+ RS::get_singleton()->instance_set_base(axis_gizmo_instance[i], spatial_editor->get_axis_gizmo(i)->get_rid());
+ RS::get_singleton()->instance_set_scenario(axis_gizmo_instance[i], get_tree()->get_root()->get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_visible(axis_gizmo_instance[i], true);
+ RS::get_singleton()->instance_geometry_set_cast_shadows_setting(axis_gizmo_instance[i], RS::SHADOW_CASTING_SETTING_OFF);
+ RS::get_singleton()->instance_set_layer_mask(axis_gizmo_instance[i], layer);
}
// Rotation white outline
@@ -3563,6 +3280,7 @@ void Node3DEditorViewport::_finish_gizmo_instances() {
RS::get_singleton()->free(rotate_gizmo_instance[i]);
RS::get_singleton()->free(scale_gizmo_instance[i]);
RS::get_singleton()->free(scale_plane_gizmo_instance[i]);
+ RS::get_singleton()->free(axis_gizmo_instance[i]);
}
// Rotation white outline
RS::get_singleton()->free(rotate_gizmo_instance[3]);
@@ -3655,6 +3373,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false);
RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false);
RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false);
+ RenderingServer::get_singleton()->instance_set_visible(axis_gizmo_instance[i], false);
}
// Rotation white outline
RenderingServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[3], false);
@@ -3711,7 +3430,15 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
RenderingServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
RenderingServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], axis_angle);
RenderingServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE));
+ RenderingServer::get_singleton()->instance_set_transform(axis_gizmo_instance[i], xform);
}
+
+ bool show_axes = spatial_editor->is_gizmo_visible() && _edit.mode != TRANSFORM_NONE;
+ RenderingServer *rs = RenderingServer::get_singleton();
+ rs->instance_set_visible(axis_gizmo_instance[0], show_axes && (_edit.plane == TRANSFORM_X_AXIS || _edit.plane == TRANSFORM_XY || _edit.plane == TRANSFORM_XZ));
+ rs->instance_set_visible(axis_gizmo_instance[1], show_axes && (_edit.plane == TRANSFORM_Y_AXIS || _edit.plane == TRANSFORM_XY || _edit.plane == TRANSFORM_YZ));
+ rs->instance_set_visible(axis_gizmo_instance[2], show_axes && (_edit.plane == TRANSFORM_Z_AXIS || _edit.plane == TRANSFORM_XZ || _edit.plane == TRANSFORM_YZ));
+
// Rotation white outline
xform.orthonormalize();
xform.basis.scale(scale);
@@ -3978,7 +3705,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo
if (child) {
AABB child_bounds = _calculate_spatial_bounds(child, false);
- if (bounds.size == Vector3() && p_parent->get_class_name() == StringName("Node3D")) {
+ if (bounds.size == Vector3() && Object::cast_to<Node3D>(p_parent)) {
bounds = child_bounds;
} else {
bounds.merge_with(child_bounds);
@@ -3986,7 +3713,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo
}
}
- if (bounds.size == Vector3() && p_parent->get_class_name() != StringName("Node3D")) {
+ if (bounds.size == Vector3() && !Object::cast_to<Node3D>(p_parent)) {
bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
@@ -4213,25 +3940,19 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant
ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions);
for (int i = 0; i < files.size(); i++) {
+ // Check if dragged files with mesh or scene extension can be created at least once.
if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) {
RES res = ResourceLoader::load(files[i]);
if (res.is_null()) {
continue;
}
-
- String type = res->get_class();
- if (type == "PackedScene") {
- Ref<PackedScene> sdata = ResourceLoader::load(files[i]);
- Node *instantiated_scene = sdata->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
+ Ref<PackedScene> scn = res;
+ if (scn.is_valid()) {
+ Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
if (!instantiated_scene) {
continue;
}
memdelete(instantiated_scene);
- } else if (ClassDB::is_parent_class(type, "Mesh")) {
- Ref<Mesh> mesh = ResourceLoader::load(files[i]);
- if (!mesh.is_valid()) {
- continue;
- }
} else {
continue;
}
@@ -4299,6 +4020,387 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
_perform_drop_data();
}
+void Node3DEditorViewport::begin_transform(TransformMode p_mode, bool instant) {
+ if (get_selected_count() > 0) {
+ _edit.mode = p_mode;
+ _compute_edit(_edit.mouse_pos);
+ _edit.instant = instant;
+ _edit.snap = spatial_editor->is_snap_enabled();
+ }
+}
+
+void Node3DEditorViewport::commit_transform() {
+ ERR_FAIL_COND(_edit.mode == TRANSFORM_NONE);
+ static const char *_transform_name[4] = {
+ TTRC("None"),
+ TTRC("Rotate"),
+ // TRANSLATORS: This refers to the movement that changes the position of an object.
+ TTRC("Translate"),
+ TTRC("Scale"),
+ };
+ undo_redo->create_action(_transform_name[_edit.mode]);
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+
+ for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
+ Node3D *sp = Object::cast_to<Node3D>(E->get());
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
+ undo_redo->add_undo_method(sp, "set_global_transform", se->original);
+ }
+ undo_redo->commit_action();
+ _edit.mode = TRANSFORM_NONE;
+ _edit.instant = false;
+ spatial_editor->set_local_coords_enabled(_edit.original_local);
+ set_message("");
+ spatial_editor->update_transform_gizmo();
+ surface->update();
+}
+
+void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) {
+ Vector3 ray_pos = _get_ray_pos(p_mousepos);
+ Vector3 ray = _get_ray(p_mousepos);
+ double snap = EDITOR_GET("interface/inspector/default_float_step");
+ int snap_step_decimals = Math::range_step_decimals(snap);
+
+ switch (_edit.mode) {
+ case TRANSFORM_SCALE: {
+ Vector3 motion_mask;
+ Plane plane;
+ bool plane_mv = false;
+
+ switch (_edit.plane) {
+ case TRANSFORM_VIEW:
+ motion_mask = Vector3(0, 0, 0);
+ plane = Plane(_get_camera_normal(), _edit.center);
+ break;
+ case TRANSFORM_X_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_Y_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_Z_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_YZ:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ case TRANSFORM_XZ:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ case TRANSFORM_XY:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized() + spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
+ break;
+ }
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
+ break;
+ }
+
+ Vector3 motion = intersection - click;
+ if (_edit.plane != TRANSFORM_VIEW) {
+ if (!plane_mv) {
+ motion = motion_mask.dot(motion) * motion_mask;
+
+ } else {
+ // Alternative planar scaling mode
+ if (p_shift) {
+ motion = motion_mask.dot(motion) * motion_mask;
+ }
+ }
+
+ } else {
+ const real_t center_click_dist = click.distance_to(_edit.center);
+ const real_t center_inters_dist = intersection.distance_to(_edit.center);
+ if (center_click_dist == 0) {
+ break;
+ }
+
+ const real_t scale = center_inters_dist - center_click_dist;
+ motion = Vector3(scale, scale, scale);
+ }
+
+ motion /= click.distance_to(_edit.center);
+
+ // Disable local transformation for TRANSFORM_VIEW
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
+
+ if (_edit.snap || spatial_editor->is_snap_enabled()) {
+ snap = spatial_editor->get_scale_snap() / 100;
+ }
+ Vector3 motion_snapped = motion;
+ motion_snapped.snap(Vector3(snap, snap, snap));
+ // This might not be necessary anymore after issue #288 is solved (in 4.0?).
+ set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+ motion = _edit.original.basis.inverse().xform(motion);
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ for (Node *E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E);
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
+ if (se->gizmo.is_valid()) {
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
+ Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
+ if (!local_coords) {
+ new_xform = se->original.affine_inverse() * new_xform;
+ }
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
+ }
+ } else {
+ Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
+ _transform_gizmo_apply(se->sp, new_xform, local_coords);
+ }
+ }
+
+ spatial_editor->update_transform_gizmo();
+ surface->update();
+
+ } break;
+
+ case TRANSFORM_TRANSLATE: {
+ Vector3 motion_mask;
+ Plane plane;
+ bool plane_mv = false;
+
+ switch (_edit.plane) {
+ case TRANSFORM_VIEW:
+ plane = Plane(_get_camera_normal(), _edit.center);
+ break;
+ case TRANSFORM_X_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_Y_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_Z_AXIS:
+ motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized();
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
+ break;
+ case TRANSFORM_YZ:
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ case TRANSFORM_XZ:
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ case TRANSFORM_XY:
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2).normalized(), _edit.center);
+ plane_mv = true;
+ break;
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
+ break;
+ }
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
+ break;
+ }
+
+ Vector3 motion = intersection - click;
+ if (_edit.plane != TRANSFORM_VIEW) {
+ if (!plane_mv) {
+ motion = motion_mask.dot(motion) * motion_mask;
+ }
+ }
+
+ // Disable local transformation for TRANSFORM_VIEW
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
+
+ if (_edit.snap || spatial_editor->is_snap_enabled()) {
+ snap = spatial_editor->get_translate_snap();
+ }
+ Vector3 motion_snapped = motion;
+ motion_snapped.snap(Vector3(snap, snap, snap));
+ set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+ motion = spatial_editor->get_gizmo_transform().basis.inverse().xform(motion);
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ for (Node *E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E);
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
+ if (se->gizmo.is_valid()) {
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
+ Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords, true); // Force orthogonal with subgizmo.
+ new_xform = se->original.affine_inverse() * new_xform;
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
+ }
+ } else {
+ Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
+ _transform_gizmo_apply(se->sp, new_xform, false);
+ }
+ }
+
+ spatial_editor->update_transform_gizmo();
+ surface->update();
+
+ } break;
+
+ case TRANSFORM_ROTATE: {
+ Plane plane = Plane(_get_camera_normal(), _edit.center);
+
+ Vector3 local_axis;
+ Vector3 global_axis;
+ switch (_edit.plane) {
+ case TRANSFORM_VIEW:
+ // local_axis unused
+ global_axis = _get_camera_normal();
+ break;
+ case TRANSFORM_X_AXIS:
+ local_axis = Vector3(1, 0, 0);
+ break;
+ case TRANSFORM_Y_AXIS:
+ local_axis = Vector3(0, 1, 0);
+ break;
+ case TRANSFORM_Z_AXIS:
+ local_axis = Vector3(0, 0, 1);
+ break;
+ case TRANSFORM_YZ:
+ case TRANSFORM_XZ:
+ case TRANSFORM_XY:
+ break;
+ }
+
+ if (_edit.plane != TRANSFORM_VIEW) {
+ global_axis = spatial_editor->get_gizmo_transform().basis.xform(local_axis).normalized();
+ }
+
+ Vector3 intersection;
+ if (!plane.intersects_ray(ray_pos, ray, &intersection)) {
+ break;
+ }
+
+ Vector3 click;
+ if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click)) {
+ break;
+ }
+
+ static const float orthogonal_threshold = Math::cos(Math::deg2rad(87.0f));
+ bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold;
+
+ double angle = 0.0f;
+ if (axis_is_orthogonal) {
+ _edit.show_rotation_line = false;
+ Vector3 projection_axis = plane.normal.cross(global_axis);
+ Vector3 delta = intersection - click;
+ float projection = delta.dot(projection_axis);
+ angle = (projection * (Math_PI / 2.0f)) / (gizmo_scale * GIZMO_CIRCLE_SIZE);
+ } else {
+ _edit.show_rotation_line = true;
+ Vector3 click_axis = (click - _edit.center).normalized();
+ Vector3 current_axis = (intersection - _edit.center).normalized();
+ angle = click_axis.signed_angle_to(current_axis, global_axis);
+ }
+
+ if (_edit.snap || spatial_editor->is_snap_enabled()) {
+ snap = spatial_editor->get_rotate_snap();
+ }
+ angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
+ angle -= Math::fmod(angle, snap);
+ set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));
+ angle = Math::deg2rad(angle);
+
+ bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
+
+ List<Node *> &selection = editor_selection->get_selected_node_list();
+ for (Node *E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E);
+ if (!sp) {
+ continue;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (!se) {
+ continue;
+ }
+
+ if (sp->has_meta("_edit_lock_")) {
+ continue;
+ }
+
+ Vector3 compute_axis = local_coords ? local_axis : global_axis;
+ if (se->gizmo.is_valid()) {
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
+
+ Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords, true); // Force orthogonal with subgizmo.
+ if (!local_coords) {
+ new_xform = se->original.affine_inverse() * new_xform;
+ }
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
+ }
+ } else {
+ Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords, sp->get_rotation_edit_mode() != Node3D::ROTATION_EDIT_MODE_BASIS);
+ _transform_gizmo_apply(se->sp, new_xform, local_coords);
+ }
+ }
+
+ spatial_editor->update_transform_gizmo();
+ surface->update();
+
+ } break;
+ default: {
+ }
+ }
+}
+
Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
cpu_time_history_index = 0;
gpu_time_history_index = 0;
@@ -4306,6 +4408,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
_edit.mode = TRANSFORM_NONE;
_edit.plane = TRANSFORM_VIEW;
_edit.snap = true;
+ _edit.show_rotation_line = true;
+ _edit.instant = false;
_edit.gizmo_handle = -1;
_edit.gizmo_handle_secondary = false;
@@ -4383,7 +4487,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
display_submenu->add_radio_check_item(TTR("Normal Buffer"), VIEW_DISPLAY_NORMAL_BUFFER);
display_submenu->add_separator();
display_submenu->add_radio_check_item(TTR("Shadow Atlas"), VIEW_DISPLAY_DEBUG_SHADOW_ATLAS);
- display_submenu->add_radio_check_item(TTR("Directional Shadow"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS);
+ display_submenu->add_radio_check_item(TTR("Directional Shadow Map"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS);
display_submenu->add_separator();
display_submenu->add_radio_check_item(TTR("Decal Atlas"), VIEW_DISPLAY_DEBUG_DECAL_ATLAS);
display_submenu->add_separator();
@@ -4399,14 +4503,14 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO);
display_submenu->add_radio_check_item(TTR("SSIL"), VIEW_DISPLAY_DEBUG_SSIL);
display_submenu->add_separator();
- display_submenu->add_radio_check_item(TTR("GI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER);
+ display_submenu->add_radio_check_item(TTR("VoxelGI/SDFGI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER);
display_submenu->add_separator();
- display_submenu->add_radio_check_item(TTR("Disable LOD"), VIEW_DISPLAY_DEBUG_DISABLE_LOD);
+ display_submenu->add_radio_check_item(TTR("Disable Mesh LOD"), VIEW_DISPLAY_DEBUG_DISABLE_LOD);
display_submenu->add_separator();
- display_submenu->add_radio_check_item(TTR("Omni Light Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS);
- display_submenu->add_radio_check_item(TTR("Spot Light Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS);
+ display_submenu->add_radio_check_item(TTR("OmniLight3D Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_OMNI_LIGHTS);
+ display_submenu->add_radio_check_item(TTR("SpotLight3D Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_SPOT_LIGHTS);
display_submenu->add_radio_check_item(TTR("Decal Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_DECALS);
- display_submenu->add_radio_check_item(TTR("Reflection Probe Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES);
+ display_submenu->add_radio_check_item(TTR("ReflectionProbe Cluster"), VIEW_DISPLAY_DEBUG_CLUSTER_REFLECTION_PROBES);
display_submenu->add_radio_check_item(TTR("Occlusion Culling Buffer"), VIEW_DISPLAY_DEBUG_OCCLUDERS);
display_submenu->set_name("display_advanced");
@@ -4465,6 +4569,16 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), Key::Q);
ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), Key::SHIFT);
ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), Key::ALT);
+ ED_SHORTCUT("spatial_editor/lock_transform_x", TTR("Lock Transformation to X axis"), Key::X);
+ ED_SHORTCUT("spatial_editor/lock_transform_y", TTR("Lock Transformation to Y axis"), Key::Y);
+ ED_SHORTCUT("spatial_editor/lock_transform_z", TTR("Lock Transformation to Z axis"), Key::Z);
+ ED_SHORTCUT("spatial_editor/lock_transform_yz", TTR("Lock Transformation to YZ plane"), KeyModifierMask::SHIFT | Key::X);
+ ED_SHORTCUT("spatial_editor/lock_transform_xz", TTR("Lock Transformation to XZ plane"), KeyModifierMask::SHIFT | Key::Y);
+ ED_SHORTCUT("spatial_editor/lock_transform_xy", TTR("Lock Transformation to XY plane"), KeyModifierMask::SHIFT | Key::Z);
+ ED_SHORTCUT("spatial_editor/cancel_transform", TTR("Cancel Transformation"), Key::ESCAPE);
+ ED_SHORTCUT("spatial_editor/instant_translate", TTR("Begin Translate Transformation"));
+ ED_SHORTCUT("spatial_editor/instant_rotate", TTR("Begin Rotate Transformation"));
+ ED_SHORTCUT("spatial_editor/instant_scale", TTR("Begin Scale Transformation"));
preview_camera = memnew(CheckBox);
preview_camera->set_text(TTR("Preview"));
@@ -5854,6 +5968,7 @@ void fragment() {
rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
+ axis_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
@@ -6175,6 +6290,21 @@ void fragment() {
plane_mat_hl->set_albedo(col.from_hsv(col.get_h(), 0.25, 1.0, 1));
plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides
}
+
+ // Lines to visualize transforms locked to an axis/plane
+ {
+ Ref<SurfaceTool> surftool = memnew(SurfaceTool);
+ surftool->begin(Mesh::PRIMITIVE_LINES);
+
+ Vector3 vec;
+ vec[i] = 1;
+
+ // line extending through infinity(ish)
+ surftool->add_vertex(vec * -99999);
+ surftool->add_vertex(vec * 99999);
+ surftool->set_material(mat_hl);
+ surftool->commit(axis_gizmo[i]);
+ }
}
}
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 20a782c8a8..bbe5615570 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -247,6 +247,7 @@ private:
Point2 _point_to_screen(const Vector3 &p_point);
Transform3D _get_camera_transform() const;
int get_selected_count() const;
+ void cancel_transform();
Vector3 _get_camera_position() const;
Vector3 _get_camera_normal() const;
@@ -310,10 +311,13 @@ private:
Point2 mouse_pos;
Point2 original_mouse_pos;
bool snap = false;
+ bool show_rotation_line = false;
Ref<EditorNode3DGizmo> gizmo;
int gizmo_handle = 0;
bool gizmo_handle_secondary = false;
Variant gizmo_initial_value;
+ bool original_local;
+ bool instant;
} _edit;
struct Cursor {
@@ -347,7 +351,7 @@ private:
real_t zoom_indicator_delay;
int zoom_failed_attempts_count = 0;
- RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[4], scale_gizmo_instance[3], scale_plane_gizmo_instance[3];
+ RID move_gizmo_instance[3], move_plane_gizmo_instance[3], rotate_gizmo_instance[4], scale_gizmo_instance[3], scale_plane_gizmo_instance[3], axis_gizmo_instance[3];
String last_message;
String message;
@@ -402,6 +406,10 @@ private:
Transform3D _compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local, bool p_orthogonal);
+ void begin_transform(TransformMode p_mode, bool instant);
+ void commit_transform();
+ void update_transform(Point2 p_mousepos, bool p_shift);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -546,7 +554,7 @@ private:
Camera3D::Projection grid_camera_last_update_perspective = Camera3D::PROJECTION_PERSPECTIVE;
Vector3 grid_camera_last_update_position = Vector3();
- Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[4], scale_gizmo[3], scale_plane_gizmo[3];
+ Ref<ArrayMesh> move_gizmo[3], move_plane_gizmo[3], rotate_gizmo[4], scale_gizmo[3], scale_plane_gizmo[3], axis_gizmo[3];
Ref<StandardMaterial3D> gizmo_color[3];
Ref<StandardMaterial3D> plane_gizmo_color[3];
Ref<ShaderMaterial> rotate_gizmo_color[3];
@@ -773,12 +781,14 @@ public:
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
+ void set_local_coords_enabled(bool on) const { tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->set_pressed(on); }
bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; }
double get_translate_snap() const;
double get_rotate_snap() const;
double get_scale_snap() const;
Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; }
+ Ref<ArrayMesh> get_axis_gizmo(int idx) const { return axis_gizmo[idx]; }
Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; }
Ref<ArrayMesh> get_rotate_gizmo(int idx) const { return rotate_gizmo[idx]; }
Ref<ArrayMesh> get_scale_gizmo(int idx) const { return scale_gizmo[idx]; }
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
index 2dd760275e..e7fe8da716 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
@@ -34,9 +34,9 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
if (occluder_instance) {
OccluderInstance3D::BakeError err;
if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == occluder_instance) {
- err = occluder_instance->bake(occluder_instance, p_file);
+ err = occluder_instance->bake_scene(occluder_instance, p_file);
} else {
- err = occluder_instance->bake(occluder_instance->get_parent(), p_file);
+ err = occluder_instance->bake_scene(occluder_instance->get_parent(), p_file);
}
switch (err) {
@@ -59,6 +59,10 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
EditorNode::get_singleton()->show_warning(TTR("No meshes to bake.\nMake sure there is at least one MeshInstance3D node in the scene whose visual layers are part of the OccluderInstance3D's Bake Mask property."));
break;
}
+ case OccluderInstance3D::BAKE_ERROR_CANT_SAVE: {
+ EditorNode::get_singleton()->show_warning(TTR("Could not save the new occluder at the specified path: ") + p_file);
+ break;
+ }
default: {
}
}
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index b492c27f41..9a8584f4a2 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -132,10 +132,7 @@ PackedSceneEditorTranslationParserPlugin::PackedSceneEditorTranslationParserPlug
lookup_properties.insert("script");
// Exception list (to prevent false positives).
- exception_list.insert("LineEdit", Vector<StringName>());
- exception_list["LineEdit"].append("text");
- exception_list.insert("TextEdit", Vector<StringName>());
- exception_list["TextEdit"].append("text");
- exception_list.insert("CodeEdit", Vector<StringName>());
- exception_list["CodeEdit"].append("text");
+ exception_list.insert("LineEdit", { "text" });
+ exception_list.insert("TextEdit", { "text" });
+ exception_list.insert("CodeEdit", { "text" });
}
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h
index fc19496eb6..ecd090b31b 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.h
+++ b/editor/plugins/packed_scene_translation_parser_plugin.h
@@ -37,9 +37,9 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP
GDCLASS(PackedSceneEditorTranslationParserPlugin, EditorTranslationParserPlugin);
// Scene Node's properties that contain translation strings.
- Set<StringName> lookup_properties;
+ Set<String> lookup_properties;
// Properties from specific Nodes that should be ignored.
- Map<StringName, Vector<StringName>> exception_list;
+ Map<String, Vector<String>> exception_list;
public:
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index 52651ae380..4014da2441 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* collision_polygon_3d_editor_plugin.cpp */
+/* polygon_3d_editor_plugin.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,9 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "collision_polygon_3d_editor_plugin.h"
+#include "polygon_3d_editor_plugin.h"
#include "canvas_item_editor_plugin.h"
+#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/io/file_access.h"
#include "core/math/geometry_2d.h"
@@ -39,13 +40,13 @@
#include "node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
-void CollisionPolygon3DEditor::_notification(int p_what) {
+void Polygon3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
button_create->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
button_edit->set_icon(get_theme_icon(SNAME("MovePoint"), SNAME("EditorIcons")));
button_edit->set_pressed(true);
- get_tree()->connect("node_removed", callable_mp(this, &CollisionPolygon3DEditor::_node_removed));
+ get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed));
} break;
case NOTIFICATION_PROCESS: {
@@ -62,7 +63,7 @@ void CollisionPolygon3DEditor::_notification(int p_what) {
}
}
-void CollisionPolygon3DEditor::_node_removed(Node *p_node) {
+void Polygon3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = nullptr;
if (imgeom->get_parent() == p_node) {
@@ -73,7 +74,7 @@ void CollisionPolygon3DEditor::_node_removed(Node *p_node) {
}
}
-void CollisionPolygon3DEditor::_menu_option(int p_option) {
+void Polygon3DEditor::_menu_option(int p_option) {
switch (p_option) {
case MODE_CREATE: {
mode = MODE_CREATE;
@@ -88,10 +89,12 @@ void CollisionPolygon3DEditor::_menu_option(int p_option) {
}
}
-void CollisionPolygon3DEditor::_wip_close() {
+void Polygon3DEditor::_wip_close() {
+ Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
+ ERR_FAIL_COND_MSG(!obj, "Edited object is not valid.");
undo_redo->create_action(TTR("Create Polygon3D"));
- undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon"));
- undo_redo->add_do_method(node, "set_polygon", wip);
+ undo_redo->add_undo_method(obj, "set_polygon", obj->call("get_polygon"));
+ undo_redo->add_do_method(obj, "set_polygon", wip);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
wip.clear();
@@ -103,11 +106,12 @@ void CollisionPolygon3DEditor::_wip_close() {
undo_redo->commit_action();
}
-EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput Polygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
+ Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
Transform3D gt = node->get_global_transform();
Transform3D gi = gt.affine_inverse();
float depth = _get_depth() * 0.5;
@@ -135,7 +139,7 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
//Let the snap happen when the point is being moved, instead.
//cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
- Vector<Vector2> poly = node->call("get_polygon");
+ PackedVector2Array poly = _get_polygon();
//first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
@@ -178,9 +182,9 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
if (mb->is_ctrl_pressed()) {
if (poly.size() < 3) {
undo_redo->create_action(TTR("Edit Poly"));
- undo_redo->add_undo_method(node, "set_polygon", poly);
+ undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.push_back(cpoint);
- undo_redo->add_do_method(node, "set_polygon", poly);
+ undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
@@ -215,7 +219,7 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
- node->call("set_polygon", poly);
+ _set_polygon(poly);
_polygon_draw();
snap_ignore = true;
@@ -256,8 +260,8 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
undo_redo->create_action(TTR("Edit Poly"));
- undo_redo->add_do_method(node, "set_polygon", poly);
- undo_redo->add_undo_method(node, "set_polygon", pre_move_edit);
+ undo_redo->add_do_method(obj, "set_polygon", poly);
+ undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
@@ -284,9 +288,9 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
if (closest_idx >= 0) {
undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
- undo_redo->add_undo_method(node, "set_polygon", poly);
+ undo_redo->add_undo_method(obj, "set_polygon", poly);
poly.remove_at(closest_idx);
- undo_redo->add_do_method(node, "set_polygon", poly);
+ undo_redo->add_do_method(obj, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
@@ -335,25 +339,40 @@ EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
-float CollisionPolygon3DEditor::_get_depth() {
- if (bool(node->call("_has_editable_3d_polygon_no_depth"))) {
- return 0;
+float Polygon3DEditor::_get_depth() {
+ Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
+ ERR_FAIL_COND_V_MSG(!obj, 0.0f, "Edited object is not valid.");
+
+ if (bool(obj->call("_has_editable_3d_polygon_no_depth"))) {
+ return 0.0f;
}
- return float(node->call("get_depth"));
+ return float(obj->call("get_depth"));
+}
+
+PackedVector2Array Polygon3DEditor::_get_polygon() {
+ Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
+ ERR_FAIL_COND_V_MSG(!obj, PackedVector2Array(), "Edited object is not valid.");
+ return PackedVector2Array(obj->call("get_polygon"));
+}
+
+void Polygon3DEditor::_set_polygon(PackedVector2Array p_poly) {
+ Object *obj = node_resource.is_valid() ? (Object *)node_resource.ptr() : node;
+ ERR_FAIL_COND_MSG(!obj, "Edited object is not valid.");
+ obj->call("set_polygon", p_poly);
}
-void CollisionPolygon3DEditor::_polygon_draw() {
+void Polygon3DEditor::_polygon_draw() {
if (!node) {
return;
}
- Vector<Vector2> poly;
+ PackedVector2Array poly;
if (wip_active) {
poly = wip;
} else {
- poly = node->call("get_polygon");
+ poly = _get_polygon();
}
float depth = _get_depth() * 0.5;
@@ -464,23 +483,32 @@ void CollisionPolygon3DEditor::_polygon_draw() {
m->surface_set_material(0, handle_material);
}
-void CollisionPolygon3DEditor::edit(Node *p_collision_polygon) {
- if (p_collision_polygon) {
- node = Object::cast_to<Node3D>(p_collision_polygon);
+void Polygon3DEditor::edit(Node *p_node) {
+ if (p_node) {
+ node = Object::cast_to<Node3D>(p_node);
+ node_resource = node->call("_get_editable_3d_polygon_resource");
+
+ if (node_resource.is_valid()) {
+ node_resource->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw));
+ }
//Enable the pencil tool if the polygon is empty
- if (Vector<Vector2>(node->call("get_polygon")).size() == 0) {
+ if (_get_polygon().is_empty()) {
_menu_option(MODE_CREATE);
}
wip.clear();
wip_active = false;
edited_point = -1;
- p_collision_polygon->add_child(imgeom);
+ p_node->add_child(imgeom);
_polygon_draw();
set_process(true);
prev_depth = -1;
} else {
node = nullptr;
+ if (node_resource.is_valid()) {
+ node_resource->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Polygon3DEditor::_polygon_draw));
+ }
+ node_resource.unref();
if (imgeom->get_parent()) {
imgeom->get_parent()->remove_child(imgeom);
@@ -490,11 +518,11 @@ void CollisionPolygon3DEditor::edit(Node *p_collision_polygon) {
}
}
-void CollisionPolygon3DEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygon3DEditor::_polygon_draw);
+void Polygon3DEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
}
-CollisionPolygon3DEditor::CollisionPolygon3DEditor(EditorNode *p_editor) {
+Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) {
node = nullptr;
editor = p_editor;
undo_redo = EditorNode::get_undo_redo();
@@ -503,13 +531,13 @@ CollisionPolygon3DEditor::CollisionPolygon3DEditor(EditorNode *p_editor) {
button_create = memnew(Button);
button_create->set_flat(true);
add_child(button_create);
- button_create->connect("pressed", callable_mp(this, &CollisionPolygon3DEditor::_menu_option), varray(MODE_CREATE));
+ button_create->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option), varray(MODE_CREATE));
button_create->set_toggle_mode(true);
button_edit = memnew(Button);
button_edit->set_flat(true);
add_child(button_edit);
- button_edit->connect("pressed", callable_mp(this, &CollisionPolygon3DEditor::_menu_option), varray(MODE_EDIT));
+ button_edit->connect("pressed", callable_mp(this, &Polygon3DEditor::_menu_option), varray(MODE_EDIT));
button_edit->set_toggle_mode(true);
mode = MODE_EDIT;
@@ -545,12 +573,12 @@ CollisionPolygon3DEditor::CollisionPolygon3DEditor(EditorNode *p_editor) {
snap_ignore = false;
}
-CollisionPolygon3DEditor::~CollisionPolygon3DEditor() {
+Polygon3DEditor::~Polygon3DEditor() {
memdelete(imgeom);
}
void Polygon3DEditorPlugin::edit(Object *p_object) {
- collision_polygon_editor->edit(Object::cast_to<Node>(p_object));
+ polygon_editor->edit(Object::cast_to<Node>(p_object));
}
bool Polygon3DEditorPlugin::handles(Object *p_object) const {
@@ -559,19 +587,19 @@ bool Polygon3DEditorPlugin::handles(Object *p_object) const {
void Polygon3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- collision_polygon_editor->show();
+ polygon_editor->show();
} else {
- collision_polygon_editor->hide();
- collision_polygon_editor->edit(nullptr);
+ polygon_editor->hide();
+ polygon_editor->edit(nullptr);
}
}
Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- collision_polygon_editor = memnew(CollisionPolygon3DEditor(p_node));
- Node3DEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
+ polygon_editor = memnew(Polygon3DEditor(p_node));
+ Node3DEditor::get_singleton()->add_control_to_menu_panel(polygon_editor);
- collision_polygon_editor->hide();
+ polygon_editor->hide();
}
Polygon3DEditorPlugin::~Polygon3DEditorPlugin() {
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.h b/editor/plugins/polygon_3d_editor_plugin.h
index cd8c857398..6b0370541e 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.h
+++ b/editor/plugins/polygon_3d_editor_plugin.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* collision_polygon_3d_editor_plugin.h */
+/* polygon_3d_editor_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef COLLISION_POLYGON_EDITOR_PLUGIN_H
-#define COLLISION_POLYGON_EDITOR_PLUGIN_H
+#ifndef POLYGON_3D_EDITOR_PLUGIN_H
+#define POLYGON_3D_EDITOR_PLUGIN_H
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
@@ -39,8 +39,8 @@
class CanvasItemEditor;
-class CollisionPolygon3DEditor : public HBoxContainer {
- GDCLASS(CollisionPolygon3DEditor, HBoxContainer);
+class Polygon3DEditor : public HBoxContainer {
+ GDCLASS(Polygon3DEditor, HBoxContainer);
UndoRedo *undo_redo;
enum Mode {
@@ -60,6 +60,7 @@ class CollisionPolygon3DEditor : public HBoxContainer {
EditorNode *editor;
Panel *panel;
Node3D *node;
+ Ref<Resource> node_resource;
Ref<ImmediateMesh> imesh;
MeshInstance3D *imgeom;
MeshInstance3D *pointsm;
@@ -69,8 +70,8 @@ class CollisionPolygon3DEditor : public HBoxContainer {
int edited_point;
Vector2 edited_point_pos;
- Vector<Vector2> pre_move_edit;
- Vector<Vector2> wip;
+ PackedVector2Array pre_move_edit;
+ PackedVector2Array wip;
bool wip_active;
bool snap_ignore;
@@ -81,6 +82,8 @@ class CollisionPolygon3DEditor : public HBoxContainer {
void _menu_option(int p_option);
float _get_depth();
+ PackedVector2Array _get_polygon();
+ void _set_polygon(PackedVector2Array p_poly);
protected:
void _notification(int p_what);
@@ -89,19 +92,19 @@ protected:
public:
virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
- void edit(Node *p_collision_polygon);
- CollisionPolygon3DEditor(EditorNode *p_editor);
- ~CollisionPolygon3DEditor();
+ void edit(Node *p_node);
+ Polygon3DEditor(EditorNode *p_editor);
+ ~Polygon3DEditor();
};
class Polygon3DEditorPlugin : public EditorPlugin {
GDCLASS(Polygon3DEditorPlugin, EditorPlugin);
- CollisionPolygon3DEditor *collision_polygon_editor;
+ Polygon3DEditor *polygon_editor;
EditorNode *editor;
public:
- virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
virtual String get_name() const override { return "Polygon3DEditor"; }
bool has_main_screen() const override { return false; }
@@ -113,4 +116,4 @@ public:
~Polygon3DEditorPlugin();
};
-#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H
+#endif // POLYGON_3D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/replication_editor_plugin.cpp b/editor/plugins/replication_editor_plugin.cpp
new file mode 100644
index 0000000000..fd4fc8f59c
--- /dev/null
+++ b/editor/plugins/replication_editor_plugin.cpp
@@ -0,0 +1,390 @@
+/*************************************************************************/
+/* replication_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "replication_editor_plugin.h"
+
+#include "editor/editor_scale.h"
+#include "editor/inspector_dock.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/tree.h"
+#include "scene/multiplayer/multiplayer_synchronizer.h"
+
+/// ReplicationEditor
+ReplicationEditor::ReplicationEditor(EditorNode *p_editor) {
+ editor = p_editor;
+ set_v_size_flags(SIZE_EXPAND_FILL);
+ set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+
+ delete_dialog = memnew(ConfirmationDialog);
+ delete_dialog->connect("cancelled", callable_mp(this, &ReplicationEditor::_dialog_closed), varray(false));
+ delete_dialog->connect("confirmed", callable_mp(this, &ReplicationEditor::_dialog_closed), varray(true));
+ add_child(delete_dialog);
+
+ error_dialog = memnew(AcceptDialog);
+ error_dialog->get_ok_button()->set_text(TTR("Close"));
+ error_dialog->set_title(TTR("Error!"));
+ add_child(error_dialog);
+
+ VBoxContainer *vb = memnew(VBoxContainer);
+ vb->set_v_size_flags(SIZE_EXPAND_FILL);
+ add_child(vb);
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ vb->add_child(hb);
+ np_line_edit = memnew(LineEdit);
+ np_line_edit->set_placeholder(":property");
+ np_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(np_line_edit);
+ add_button = memnew(Button);
+ add_button->connect("pressed", callable_mp(this, &ReplicationEditor::_add_pressed));
+ add_button->set_text(TTR("Add"));
+ hb->add_child(add_button);
+
+ tree = memnew(Tree);
+ tree->set_hide_root(true);
+ tree->set_columns(4);
+ tree->set_column_titles_visible(true);
+ tree->set_column_title(0, TTR("Properties"));
+ tree->set_column_expand(0, true);
+ tree->set_column_title(1, TTR("Spawn"));
+ tree->set_column_expand(1, false);
+ tree->set_column_custom_minimum_width(1, 100);
+ tree->set_column_title(2, TTR("Sync"));
+ tree->set_column_custom_minimum_width(2, 100);
+ tree->set_column_expand(2, false);
+ tree->set_column_expand(3, false);
+ tree->create_item();
+ tree->connect("button_pressed", callable_mp(this, &ReplicationEditor::_tree_button_pressed));
+ tree->connect("item_edited", callable_mp(this, &ReplicationEditor::_tree_item_edited));
+ tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ vb->add_child(tree);
+}
+
+void ReplicationEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_update_config"), &ReplicationEditor::_update_config);
+ ClassDB::bind_method(D_METHOD("_update_checked", "property", "column", "checked"), &ReplicationEditor::_update_checked);
+ ADD_SIGNAL(MethodInfo("keying_changed"));
+}
+
+void ReplicationEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ add_theme_style_override("panel", editor->get_gui_base()->get_theme_stylebox(SNAME("panel"), SNAME("Panel")));
+ } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ update_keying();
+ }
+}
+
+void ReplicationEditor::_add_pressed() {
+ if (!current) {
+ error_dialog->set_text(TTR("Please select a MultiplayerSynchronizer first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ if (current->get_root_path().is_empty()) {
+ error_dialog->set_text(TTR("The MultiplayerSynchronizer needs a root path."));
+ error_dialog->popup_centered();
+ return;
+ }
+ String np_text = np_line_edit->get_text();
+ if (np_text.find(":") == -1) {
+ np_text = ":" + np_text;
+ }
+ NodePath prop = NodePath(np_text);
+ if (prop.is_empty()) {
+ return;
+ }
+ UndoRedo *undo_redo = editor->get_undo_redo();
+ undo_redo->create_action(TTR("Add property"));
+ config = current->get_replication_config();
+ if (config.is_null()) {
+ config.instantiate();
+ current->set_replication_config(config);
+ undo_redo->add_do_method(current, "set_replication_config", config);
+ undo_redo->add_undo_method(current, "set_replication_config", Ref<SceneReplicationConfig>());
+ _update_config();
+ }
+ undo_redo->add_do_method(config.ptr(), "add_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+}
+
+void ReplicationEditor::_tree_item_edited() {
+ TreeItem *ti = tree->get_edited();
+ if (!ti || config.is_null()) {
+ return;
+ }
+ int column = tree->get_edited_column();
+ ERR_FAIL_COND(column < 1 || column > 2);
+ const NodePath prop = ti->get_metadata(0);
+ UndoRedo *undo_redo = editor->get_undo_redo();
+ bool value = ti->is_checked(column);
+ String method;
+ if (column == 1) {
+ undo_redo->create_action(TTR("Set spawn property"));
+ method = "property_set_spawn";
+ } else {
+ undo_redo->create_action(TTR("Set sync property"));
+ method = "property_set_sync";
+ }
+ undo_redo->add_do_method(config.ptr(), method, prop, value);
+ undo_redo->add_undo_method(config.ptr(), method, prop, !value);
+ undo_redo->add_do_method(this, "_update_checked", prop, column, value);
+ undo_redo->add_undo_method(this, "_update_checked", prop, column, !value);
+ undo_redo->commit_action();
+}
+
+void ReplicationEditor::_tree_button_pressed(Object *p_item, int p_column, int p_id) {
+ TreeItem *ti = Object::cast_to<TreeItem>(p_item);
+ if (!ti) {
+ return;
+ }
+ deleting = ti->get_metadata(0);
+ delete_dialog->set_text(TTR("Delete Property?") + "\n\"" + ti->get_text(0) + "\"");
+ delete_dialog->popup_centered();
+}
+
+void ReplicationEditor::_dialog_closed(bool p_confirmed) {
+ if (deleting.is_empty() || config.is_null()) {
+ return;
+ }
+ if (p_confirmed) {
+ const NodePath prop = deleting;
+ int idx = config->property_get_index(prop);
+ bool spawn = config->property_get_spawn(prop);
+ bool sync = config->property_get_sync(prop);
+ UndoRedo *undo_redo = editor->get_undo_redo();
+ undo_redo->create_action(TTR("Remove Property"));
+ undo_redo->add_do_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "add_property", prop, idx);
+ undo_redo->add_undo_method(config.ptr(), "property_set_spawn", prop, spawn);
+ undo_redo->add_undo_method(config.ptr(), "property_set_sync", prop, sync);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+ }
+ deleting = NodePath();
+}
+
+void ReplicationEditor::_update_checked(const NodePath &p_prop, int p_column, bool p_checked) {
+ if (!tree->get_root()) {
+ return;
+ }
+ TreeItem *ti = tree->get_root()->get_first_child();
+ while (ti) {
+ if (ti->get_metadata(0).operator NodePath() == p_prop) {
+ ti->set_checked(p_column, p_checked);
+ return;
+ }
+ ti = ti->get_next();
+ }
+}
+
+void ReplicationEditor::update_keying() {
+ /// TODO make keying usable.
+#if 0
+ bool keying_enabled = false;
+ EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history();
+ if (is_visible_in_tree() && config.is_valid() && editor_history->get_path_size() > 0) {
+ Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0));
+ keying_enabled = Object::cast_to<Node>(obj) != nullptr;
+ }
+
+ if (keying_enabled == keying) {
+ return;
+ }
+
+ keying = keying_enabled;
+ emit_signal(SNAME("keying_changed"));
+#endif
+}
+
+void ReplicationEditor::_update_config() {
+ deleting = NodePath();
+ tree->clear();
+ tree->create_item();
+ if (!config.is_valid()) {
+ update_keying();
+ return;
+ }
+ TypedArray<NodePath> props = config->get_properties();
+ for (int i = 0; i < props.size(); i++) {
+ const NodePath path = props[i];
+ _add_property(path, config->property_get_spawn(path), config->property_get_sync(path));
+ }
+ update_keying();
+}
+
+void ReplicationEditor::edit(MultiplayerSynchronizer *p_sync) {
+ if (current == p_sync) {
+ return;
+ }
+ current = p_sync;
+ if (current) {
+ config = current->get_replication_config();
+ } else {
+ config.unref();
+ }
+ _update_config();
+}
+
+Ref<Texture2D> ReplicationEditor::_get_class_icon(const Node *p_node) {
+ if (!p_node || !has_theme_icon(p_node->get_class(), "EditorIcons")) {
+ return get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"));
+ }
+ return get_theme_icon(p_node->get_class(), "EditorIcons");
+}
+
+void ReplicationEditor::_add_property(const NodePath &p_property, bool p_spawn, bool p_sync) {
+ String prop = String(p_property);
+ TreeItem *item = tree->create_item();
+ item->set_selectable(0, false);
+ item->set_selectable(1, false);
+ item->set_selectable(2, false);
+ item->set_selectable(3, false);
+ item->set_text(0, prop);
+ item->set_metadata(0, prop);
+ Node *root_node = current && !current->get_root_path().is_empty() ? current->get_node(current->get_root_path()) : nullptr;
+ Ref<Texture2D> icon = _get_class_icon(root_node);
+ if (root_node) {
+ String path = prop.substr(0, prop.find(":"));
+ String subpath = prop.substr(path.size());
+ Node *node = root_node->get_node_or_null(path);
+ if (!node) {
+ node = root_node;
+ }
+ item->set_text(0, String(node->get_name()) + ":" + subpath);
+ icon = _get_class_icon(node);
+ }
+ item->set_icon(0, icon);
+ item->add_button(3, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ item->set_text_alignment(1, HORIZONTAL_ALIGNMENT_CENTER);
+ item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK);
+ item->set_checked(1, p_spawn);
+ item->set_editable(1, true);
+ item->set_text_alignment(2, HORIZONTAL_ALIGNMENT_CENTER);
+ item->set_cell_mode(2, TreeItem::CELL_MODE_CHECK);
+ item->set_checked(2, p_sync);
+ item->set_editable(2, true);
+}
+
+void ReplicationEditor::property_keyed(const String &p_property) {
+ ERR_FAIL_COND(!current || config.is_null());
+ Node *root = current->get_node(current->get_root_path());
+ ERR_FAIL_COND(!root);
+ EditorHistory *history = editor->get_editor_history();
+ ERR_FAIL_COND(history->get_path_size() == 0);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(history->get_path_object(0)));
+ ERR_FAIL_COND(!node);
+ if (node->is_class("MultiplayerSynchronizer")) {
+ error_dialog->set_text(TTR("Properties of 'MultiplayerSynchronizer' cannot be configured for replication."));
+ error_dialog->popup_centered();
+ return;
+ }
+ if (history->get_path_size() > 1 || p_property.get_slice_count(":") > 1) {
+ error_dialog->set_text(TTR("Subresources cannot yet be configured for replication."));
+ error_dialog->popup_centered();
+ return;
+ }
+
+ String path = root->get_path_to(node);
+ for (int i = 1; i < history->get_path_size(); i++) {
+ String prop = history->get_path_property(i);
+ ERR_FAIL_COND(prop == "");
+ path += ":" + prop;
+ }
+ path += ":" + p_property;
+
+ NodePath prop = path;
+ UndoRedo *undo_redo = editor->get_undo_redo();
+ undo_redo->create_action(TTR("Add property"));
+ undo_redo->add_do_method(config.ptr(), "add_property", prop);
+ undo_redo->add_undo_method(config.ptr(), "remove_property", prop);
+ undo_redo->add_do_method(this, "_update_config");
+ undo_redo->add_undo_method(this, "_update_config");
+ undo_redo->commit_action();
+}
+
+/// ReplicationEditorPlugin
+ReplicationEditorPlugin::ReplicationEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ repl_editor = memnew(ReplicationEditor(editor));
+ editor->add_bottom_panel_item(TTR("Replication"), repl_editor);
+}
+
+ReplicationEditorPlugin::~ReplicationEditorPlugin() {
+}
+
+void ReplicationEditorPlugin::_keying_changed() {
+ // TODO make lock usable.
+ //InspectorDock::get_inspector_singleton()->set_keying(repl_editor->has_keying(), this);
+}
+
+void ReplicationEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) {
+ if (!repl_editor->has_keying()) {
+ return;
+ }
+ repl_editor->property_keyed(p_keyed);
+}
+
+void ReplicationEditorPlugin::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ //Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request));
+ InspectorDock::get_inspector_singleton()->connect("property_keyed", callable_mp(this, &ReplicationEditorPlugin::_property_keyed));
+ repl_editor->connect("keying_changed", callable_mp(this, &ReplicationEditorPlugin::_keying_changed));
+ // TODO make lock usable.
+ //InspectorDock::get_inspector_singleton()->connect("object_inspected", callable_mp(repl_editor, &ReplicationEditor::update_keying));
+ get_tree()->connect("node_removed", callable_mp(this, &ReplicationEditorPlugin::_node_removed));
+ }
+}
+
+void ReplicationEditorPlugin::_node_removed(Node *p_node) {
+ if (p_node && p_node == repl_editor->get_current()) {
+ repl_editor->edit(nullptr);
+ if (repl_editor->is_visible_in_tree()) {
+ editor->hide_bottom_panel();
+ }
+ }
+}
+
+void ReplicationEditorPlugin::edit(Object *p_object) {
+ repl_editor->edit(Object::cast_to<MultiplayerSynchronizer>(p_object));
+}
+
+bool ReplicationEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("MultiplayerSynchronizer");
+}
+
+void ReplicationEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ editor->make_bottom_panel_item_visible(repl_editor);
+ }
+}
diff --git a/editor/plugins/replication_editor_plugin.h b/editor/plugins/replication_editor_plugin.h
new file mode 100644
index 0000000000..049eda99cc
--- /dev/null
+++ b/editor/plugins/replication_editor_plugin.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* replication_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REPLICATION_EDITOR_PLUGIN_H
+#define REPLICATION_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/resources/scene_replication_config.h"
+
+class ConfirmationDialog;
+class MultiplayerSynchronizer;
+class Tree;
+
+class ReplicationEditor : public VBoxContainer {
+ GDCLASS(ReplicationEditor, VBoxContainer);
+
+private:
+ EditorNode *editor;
+ MultiplayerSynchronizer *current = nullptr;
+
+ AcceptDialog *error_dialog = nullptr;
+ ConfirmationDialog *delete_dialog = nullptr;
+ Button *add_button = nullptr;
+ LineEdit *np_line_edit = nullptr;
+
+ Ref<SceneReplicationConfig> config;
+ NodePath deleting;
+ Tree *tree;
+ bool keying = false;
+
+ Ref<Texture2D> _get_class_icon(const Node *p_node);
+
+ void _add_pressed();
+ void _tree_item_edited();
+ void _tree_button_pressed(Object *p_item, int p_column, int p_id);
+ void _update_checked(const NodePath &p_prop, int p_column, bool p_checked);
+ void _update_config();
+ void _dialog_closed(bool p_confirmed);
+ void _add_property(const NodePath &p_property, bool p_spawn = true, bool p_sync = true);
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+public:
+ void update_keying();
+ void edit(MultiplayerSynchronizer *p_object);
+ bool has_keying() const { return keying; }
+ MultiplayerSynchronizer *get_current() const { return current; }
+ void property_keyed(const String &p_property);
+
+ ReplicationEditor(EditorNode *p_node);
+ ~ReplicationEditor() {}
+};
+
+class ReplicationEditorPlugin : public EditorPlugin {
+ GDCLASS(ReplicationEditorPlugin, EditorPlugin);
+
+private:
+ EditorNode *editor;
+ ReplicationEditor *repl_editor;
+
+ void _node_removed(Node *p_node);
+ void _keying_changed();
+ void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance);
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ ReplicationEditorPlugin(EditorNode *p_node);
+ ~ReplicationEditorPlugin();
+};
+
+#endif // REPLICATION_EDITOR_PLUGIN_H
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index d5287bc2fb..786217a5c2 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -110,7 +110,7 @@ void ResourcePreloaderEditor::_item_edited() {
return;
}
- if (new_name.is_empty() || new_name.find("\\") != -1 || new_name.find("/") != -1 || preloader->has_resource(new_name)) {
+ if (new_name.is_empty() || new_name.contains("\\") || new_name.contains("/") || preloader->has_resource(new_name)) {
s->set_text(0, old_name);
return;
}
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index f1f67eeea0..17de3ba026 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -159,7 +159,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
- if (name.find("/") != -1) {
+ if (name.contains("/")) {
continue;
}
highlighter->add_member_keyword_color(name, member_variable_color);
@@ -714,7 +714,7 @@ void ScriptEditor::_open_recent_script(int p_idx) {
return;
}
// if it's a path then it's most likely a deleted file not help
- } else if (path.find("::") != -1) {
+ } else if (path.contains("::")) {
// built-in script
String res_path = path.get_slice("::", 0);
if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
@@ -2265,7 +2265,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
if (use_external_editor &&
(EditorDebuggerNode::get_singleton()->get_dump_stack_script() != p_resource || EditorDebuggerNode::get_singleton()->get_debug_with_external_editor()) &&
p_resource->get_path().is_resource_file() &&
- p_resource->get_class_name() != StringName("VisualScript")) {
+ !Ref<VisualScript>(p_resource).is_valid()) {
String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
@@ -2293,7 +2293,7 @@ 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) {
+ if (arg.contains("{file}")) {
has_file_flag = true;
}
@@ -2364,7 +2364,7 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
se->set_edited_resource(p_resource);
- if (p_resource->get_class_name() != StringName("VisualScript")) {
+ if (!Ref<VisualScript>(p_resource).is_valid()) {
bool highlighter_set = false;
for (int i = 0; i < syntax_highlighters.size(); i++) {
Ref<EditorSyntaxHighlighter> highlighter = syntax_highlighters[i]->_create();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 09af73520b..c3d61dfd58 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -398,10 +398,10 @@ Ref<Texture2D> ScriptTextEditor::get_theme_icon() {
icon_name += "Internal";
}
- if (get_parent_control()->has_theme_icon(icon_name, "EditorIcons")) {
- return get_parent_control()->get_theme_icon(icon_name, "EditorIcons");
- } else if (get_parent_control()->has_theme_icon(script->get_class(), "EditorIcons")) {
- return get_parent_control()->get_theme_icon(script->get_class(), "EditorIcons");
+ if (get_parent_control()->has_theme_icon(icon_name, SNAME("EditorIcons"))) {
+ return get_parent_control()->get_theme_icon(icon_name, SNAME("EditorIcons"));
+ } else if (get_parent_control()->has_theme_icon(script->get_class(), SNAME("EditorIcons"))) {
+ return get_parent_control()->get_theme_icon(script->get_class(), SNAME("EditorIcons"));
}
}
@@ -930,21 +930,22 @@ void ScriptTextEditor::_update_connected_methods() {
continue;
}
- if (methods_found.has(connection.callable.get_method())) {
+ const StringName method = connection.callable.get_method();
+ if (methods_found.has(method)) {
continue;
}
- if (!ClassDB::has_method(script->get_instance_base_type(), connection.callable.get_method())) {
+ if (!ClassDB::has_method(script->get_instance_base_type(), method)) {
int line = -1;
for (int j = 0; j < functions.size(); j++) {
String name = functions[j].get_slice(":", 0);
- if (name == connection.callable.get_method()) {
+ if (name == method) {
line = functions[j].get_slice(":", 1).to_int() - 1;
- text_edit->set_line_gutter_metadata(line, connection_gutter, connection.callable.get_method());
+ text_edit->set_line_gutter_metadata(line, connection_gutter, method);
text_edit->set_line_gutter_icon(line, connection_gutter, get_parent_control()->get_theme_icon(SNAME("Slot"), SNAME("EditorIcons")));
text_edit->set_line_gutter_clickable(line, connection_gutter, true);
- methods_found.insert(connection.callable.get_method());
+ methods_found.insert(method);
break;
}
}
@@ -957,7 +958,7 @@ void ScriptTextEditor::_update_connected_methods() {
bool found_inherited_function = false;
Ref<Script> inherited_script = script->get_base_script();
while (!inherited_script.is_null()) {
- if (inherited_script->has_method(connection.callable.get_method())) {
+ if (inherited_script->has_method(method)) {
found_inherited_function = true;
break;
}
@@ -1295,7 +1296,7 @@ void ScriptTextEditor::_edit_option_toggle_inline_comment() {
script->get_language()->get_comment_delimiters(&comment_delimiters);
for (const String &script_delimiter : comment_delimiters) {
- if (script_delimiter.find(" ") == -1) {
+ if (!script_delimiter.contains(" ")) {
delimiter = script_delimiter;
break;
}
@@ -1373,7 +1374,7 @@ void ScriptTextEditor::reload(bool p_soft) {
return;
}
scr->set_source_code(te->get_text());
- bool soft = p_soft || scr->get_instance_base_type() == "EditorPlugin"; //always soft-reload editor plugins
+ bool soft = p_soft || scr->get_instance_base_type() == "EditorPlugin"; // Always soft-reload editor plugins.
scr->get_language()->reload_tool_script(scr, soft);
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 5dd24983ff..ac77f51812 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -821,7 +821,7 @@ void fragment() {
}
)");
handle_material->set_shader(handle_shader);
- Ref<Texture2D> handle = editor->get_gui_base()->get_theme_icon("EditorBoneHandle", "EditorIcons");
+ Ref<Texture2D> handle = editor->get_gui_base()->get_theme_icon(SNAME("EditorBoneHandle"), SNAME("EditorIcons"));
handle_material->set_shader_param("point_size", handle->get_width());
handle_material->set_shader_param("texture_albedo", handle);
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 014fa0e7a5..419076a3f6 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -285,7 +285,7 @@ void SpriteFramesEditor::_sheet_spin_changed(double) {
}
void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) {
- Ref<Resource> texture = ResourceLoader::load(p_file);
+ Ref<Texture2D> texture = ResourceLoader::load(p_file);
if (!texture.is_valid()) {
EditorNode::get_singleton()->show_warning(TTR("Unable to load images"));
ERR_FAIL_COND(!texture.is_valid());
diff --git a/editor/plugins/text_control_editor_plugin.cpp b/editor/plugins/text_control_editor_plugin.cpp
index a51b5d3e03..4ce94176e5 100644
--- a/editor/plugins/text_control_editor_plugin.cpp
+++ b/editor/plugins/text_control_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "text_control_editor_plugin.h"
#include "editor/editor_scale.h"
+#include "editor/multi_node_edit.h"
void TextControlEditor::_notification(int p_notification) {
switch (p_notification) {
@@ -109,7 +110,7 @@ void TextControlEditor::_update_styles_menu() {
for (Map<String, String>::Element *E = fonts[name].front(); E; E = E->next()) {
font_style_list->add_item(E->key());
}
- } else {
+ } else if (font_list->get_selected() >= 0) {
font_style_list->add_item("Default");
}
@@ -121,82 +122,157 @@ void TextControlEditor::_update_styles_menu() {
}
void TextControlEditor::_update_control() {
- if (edited_control) {
- // Get override names.
- if (edited_control->is_class("RichTextLabel")) {
- edited_color = "default_color";
- edited_font = "normal_font";
- edited_font_size = "normal_font_size";
- } else {
- edited_color = "font_color";
- edited_font = "font";
- edited_font_size = "font_size";
- }
-
- // Get font override.
- Ref<Font> font;
- if (edited_control->has_theme_font_override(edited_font)) {
- font = edited_control->get_theme_font(edited_font);
- }
- if (font.is_valid()) {
- if (font->get_data_count() != 1) {
- // Composite font, save it to "custom_font" to allow undoing font change.
- custom_font = font;
- _update_fonts_menu();
- font_list->select(FONT_INFO_USER_CUSTOM);
- _update_styles_menu();
- font_style_list->select(0);
+ if (!edited_controls.is_empty()) {
+ String font_selected;
+ bool same_font = true;
+ String style_selected;
+ bool same_style = true;
+ int font_size = 0;
+ bool same_font_size = true;
+ int outline_size = 0;
+ bool same_outline_size = true;
+ Color font_color = Color{ 1.0f, 1.0f, 1.0f };
+ bool same_font_color = true;
+ Color outline_color = Color{ 1.0f, 1.0f, 1.0f };
+ bool same_outline_color = true;
+
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ StringName edited_color;
+ StringName edited_font;
+ StringName edited_font_size;
+
+ // Get override names.
+ if (Object::cast_to<RichTextLabel>(edited_control)) {
+ edited_color = SNAME("default_color");
+ edited_font = SNAME("normal_font");
+ edited_font_size = SNAME("normal_font_size");
} else {
- // Single face font, search for the font with matching name and style.
- String name = font->get_data(0)->get_font_name();
- String style = font->get_data(0)->get_font_style_name();
- if (fonts.has(name) && fonts[name].has(style)) {
- _update_fonts_menu();
- for (int i = 0; i < font_list->get_item_count(); i++) {
- if (font_list->get_item_text(i) == name) {
- font_list->select(i);
- break;
- }
+ edited_color = SNAME("font_color");
+ edited_font = SNAME("font");
+ edited_font_size = SNAME("font_size");
+ }
+
+ // Get font override.
+ Ref<Font> font;
+ if (edited_control->has_theme_font_override(edited_font)) {
+ font = edited_control->get_theme_font(edited_font);
+ }
+
+ if (font.is_valid()) {
+ if (font->get_data_count() != 1) {
+ if (i > 0) {
+ same_font = same_font && (custom_font == font);
}
- _update_styles_menu();
- for (int i = 0; i < font_style_list->get_item_count(); i++) {
- if (font_style_list->get_item_text(i) == style) {
- font_style_list->select(i);
- break;
+ custom_font = font;
+
+ font_selected = TTR("[Custom Font]");
+ same_style = false;
+ } else {
+ String name = font->get_data(0)->get_font_name();
+ String style = font->get_data(0)->get_font_style_name();
+ if (fonts.has(name) && fonts[name].has(style)) {
+ if (i > 0) {
+ same_font = same_font && (name == font_selected);
+ same_style = same_style && (style == style_selected);
+ }
+ font_selected = name;
+ style_selected = style;
+ } else {
+ if (i > 0) {
+ same_font = same_font && (custom_font == font);
}
+ custom_font = font;
+
+ font_selected = TTR("[Custom Font]");
+ same_style = false;
}
- } else {
- // Unknown font, save it to "custom_font" to allow undoing font change.
- custom_font = font;
- _update_fonts_menu();
- font_list->select(FONT_INFO_USER_CUSTOM);
- _update_styles_menu();
- font_style_list->select(0);
+ }
+ } else {
+ if (i > 0) {
+ same_font = same_font && (font_selected == TTR("[Theme Default]"));
+ }
+
+ font_selected = TTR("[Theme Default]");
+ same_style = false;
+ }
+
+ int current_font_size = edited_control->get_theme_font_size(edited_font_size);
+ int current_outline_size = edited_control->get_theme_constant(SNAME("outline_size"));
+ Color current_font_color = edited_control->get_theme_color(edited_color);
+ Color current_outline_color = edited_control->get_theme_color(SNAME("font_outline_color"));
+ if (i > 0) {
+ same_font_size = same_font_size && (font_size == current_font_size);
+ same_outline_size = same_outline_size && (outline_size == current_outline_size);
+ same_font_color = same_font_color && (font_color == current_font_color);
+ same_outline_color = same_outline_color && (outline_color == current_outline_color);
+ }
+
+ font_size = current_font_size;
+ outline_size = current_outline_size;
+ font_color = current_font_color;
+ outline_color = current_outline_color;
+ }
+ _update_fonts_menu();
+ if (same_font) {
+ for (int j = 0; j < font_list->get_item_count(); j++) {
+ if (font_list->get_item_text(j) == font_selected) {
+ font_list->select(j);
+ break;
}
}
} else {
- // No font override, select "Theme Default".
- _update_fonts_menu();
- font_list->select(FONT_INFO_THEME_DEFAULT);
- _update_styles_menu();
- font_style_list->select(0);
+ custom_font = Ref<Font>();
+ font_list->select(-1);
+ }
+
+ _update_styles_menu();
+ if (same_style) {
+ for (int j = 0; j < font_style_list->get_item_count(); j++) {
+ if (font_style_list->get_item_text(j) == style_selected) {
+ font_style_list->select(j);
+ break;
+ }
+ }
+ } else {
+ font_style_list->select(-1);
}
// Get other theme overrides.
font_size_list->set_block_signals(true);
- font_size_list->set_value(edited_control->get_theme_font_size(edited_font_size));
+ if (same_font_size) {
+ font_size_list->get_line_edit()->set_text(String::num_uint64(font_size));
+ font_size_list->set_value(font_size);
+ } else {
+ font_size_list->get_line_edit()->set_text("");
+ }
font_size_list->set_block_signals(false);
outline_size_list->set_block_signals(true);
- outline_size_list->set_value(edited_control->get_theme_constant("outline_size"));
+ if (same_outline_size) {
+ outline_size_list->get_line_edit()->set_text(String::num_uint64(outline_size));
+ outline_size_list->set_value(outline_size);
+ } else {
+ outline_size_list->get_line_edit()->set_text("");
+ }
outline_size_list->set_block_signals(false);
- font_color_picker->set_pick_color(edited_control->get_theme_color(edited_color));
- outline_color_picker->set_pick_color(edited_control->get_theme_color("font_outline_color"));
+ if (!same_font_color) {
+ font_color = Color{ 1.0f, 1.0f, 1.0f };
+ }
+ font_color_picker->set_pick_color(font_color);
+
+ if (!same_outline_color) {
+ outline_color = Color{ 1.0f, 1.0f, 1.0f };
+ }
+ outline_color_picker->set_pick_color(outline_color);
}
}
void TextControlEditor::_font_selected(int p_id) {
+ _update_styles_menu();
_set_font();
}
@@ -205,42 +281,53 @@ void TextControlEditor::_font_style_selected(int p_id) {
}
void TextControlEditor::_set_font() {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Font"));
- if (font_list->get_selected_id() == FONT_INFO_THEME_DEFAULT) {
- // Remove font override.
- ur->add_do_method(edited_control, "remove_theme_font_override", edited_font);
- } else if (font_list->get_selected_id() == FONT_INFO_USER_CUSTOM) {
- // Restore "custom_font".
- ur->add_do_method(edited_control, "add_theme_font_override", edited_font, custom_font);
- } else {
- // Load new font resource using selected name and style.
- String name = font_list->get_item_text(font_list->get_selected());
- String style = font_style_list->get_item_text(font_style_list->get_selected());
- if (style.is_empty()) {
- style = "Default";
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ StringName edited_font;
+ if (Object::cast_to<RichTextLabel>(edited_control)) {
+ edited_font = SNAME("normal_font");
+ } else {
+ edited_font = SNAME("font");
}
- if (fonts.has(name)) {
- Ref<FontData> fd = ResourceLoader::load(fonts[name][style]);
- if (fd.is_valid()) {
- Ref<Font> font;
- font.instantiate();
- font->add_data(fd);
- ur->add_do_method(edited_control, "add_theme_font_override", edited_font, font);
+ if (font_list->get_selected_id() == FONT_INFO_THEME_DEFAULT) {
+ // Remove font override.
+ ur->add_do_method(edited_control, "remove_theme_font_override", edited_font);
+ } else if (font_list->get_selected_id() == FONT_INFO_USER_CUSTOM) {
+ // Restore "custom_font".
+ ur->add_do_method(edited_control, "add_theme_font_override", edited_font, custom_font);
+ } else if (font_list->get_selected() >= 0) {
+ // Load new font resource using selected name and style.
+ String name = font_list->get_item_text(font_list->get_selected());
+ String style = font_style_list->get_item_text(font_style_list->get_selected());
+ if (style.is_empty()) {
+ style = "Default";
+ }
+ if (fonts.has(name)) {
+ Ref<FontData> fd = ResourceLoader::load(fonts[name][style]);
+ if (fd.is_valid()) {
+ Ref<Font> font;
+ font.instantiate();
+ font->add_data(fd);
+ ur->add_do_method(edited_control, "add_theme_font_override", edited_font, font);
+ }
}
}
- }
- if (edited_control->has_theme_font_override(edited_font)) {
- ur->add_undo_method(edited_control, "add_theme_font_override", edited_font, edited_control->get_theme_font(edited_font));
- } else {
- ur->add_undo_method(edited_control, "remove_theme_font_override", edited_font);
+ if (edited_control->has_theme_font_override(edited_font)) {
+ ur->add_undo_method(edited_control, "add_theme_font_override", edited_font, edited_control->get_theme_font(edited_font));
+ } else {
+ ur->add_undo_method(edited_control, "remove_theme_font_override", edited_font);
+ }
}
ur->add_do_method(this, "_update_control");
@@ -250,18 +337,30 @@ void TextControlEditor::_set_font() {
}
void TextControlEditor::_font_size_selected(double p_size) {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Font Size"));
- ur->add_do_method(edited_control, "add_theme_font_size_override", edited_font_size, p_size);
- if (edited_control->has_theme_font_size_override(edited_font_size)) {
- ur->add_undo_method(edited_control, "add_theme_font_size_override", edited_font_size, edited_control->get_theme_font_size(edited_font_size));
- } else {
- ur->add_undo_method(edited_control, "remove_theme_font_size_override", edited_font_size);
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ StringName edited_font_size;
+ if (Object::cast_to<RichTextLabel>(edited_control)) {
+ edited_font_size = SNAME("normal_font_size");
+ } else {
+ edited_font_size = SNAME("font_size");
+ }
+
+ ur->add_do_method(edited_control, "add_theme_font_size_override", edited_font_size, p_size);
+ if (edited_control->has_theme_font_size_override(edited_font_size)) {
+ ur->add_undo_method(edited_control, "add_theme_font_size_override", edited_font_size, edited_control->get_theme_font_size(edited_font_size));
+ } else {
+ ur->add_undo_method(edited_control, "remove_theme_font_size_override", edited_font_size);
+ }
}
ur->add_do_method(this, "_update_control");
@@ -271,18 +370,23 @@ void TextControlEditor::_font_size_selected(double p_size) {
}
void TextControlEditor::_outline_size_selected(double p_size) {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Font Outline Size"));
- ur->add_do_method(edited_control, "add_theme_constant_override", "outline_size", p_size);
- if (edited_control->has_theme_constant_override("outline_size")) {
- ur->add_undo_method(edited_control, "add_theme_constant_override", "outline_size", edited_control->get_theme_constant("outline_size"));
- } else {
- ur->add_undo_method(edited_control, "remove_theme_constant_override", "outline_size");
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ ur->add_do_method(edited_control, "add_theme_constant_override", "outline_size", p_size);
+ if (edited_control->has_theme_constant_override("outline_size")) {
+ ur->add_undo_method(edited_control, "add_theme_constant_override", "outline_size", edited_control->get_theme_constant(SNAME("outline_size")));
+ } else {
+ ur->add_undo_method(edited_control, "remove_theme_constant_override", "outline_size");
+ }
}
ur->add_do_method(this, "_update_control");
@@ -292,18 +396,30 @@ void TextControlEditor::_outline_size_selected(double p_size) {
}
void TextControlEditor::_font_color_changed(const Color &p_color) {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Font Color"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(edited_control, "add_theme_color_override", edited_color, p_color);
- if (edited_control->has_theme_color_override(edited_color)) {
- ur->add_undo_method(edited_control, "add_theme_color_override", edited_color, edited_control->get_theme_color(edited_color));
- } else {
- ur->add_undo_method(edited_control, "remove_theme_color_override", edited_color);
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ StringName edited_color;
+ if (Object::cast_to<RichTextLabel>(edited_control)) {
+ edited_color = SNAME("default_color");
+ } else {
+ edited_color = SNAME("font_color");
+ }
+
+ ur->add_do_method(edited_control, "add_theme_color_override", edited_color, p_color);
+ if (edited_control->has_theme_color_override(edited_color)) {
+ ur->add_undo_method(edited_control, "add_theme_color_override", edited_color, edited_control->get_theme_color(edited_color));
+ } else {
+ ur->add_undo_method(edited_control, "remove_theme_color_override", edited_color);
+ }
}
ur->add_do_method(this, "_update_control");
@@ -313,18 +429,23 @@ void TextControlEditor::_font_color_changed(const Color &p_color) {
}
void TextControlEditor::_outline_color_changed(const Color &p_color) {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Font Outline Color"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(edited_control, "add_theme_color_override", "font_outline_color", p_color);
- if (edited_control->has_theme_color_override("font_outline_color")) {
- ur->add_undo_method(edited_control, "add_theme_color_override", "font_outline_color", edited_control->get_theme_color("font_outline_color"));
- } else {
- ur->add_undo_method(edited_control, "remove_theme_color_override", "font_outline_color");
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
+
+ ur->add_do_method(edited_control, "add_theme_color_override", "font_outline_color", p_color);
+ if (edited_control->has_theme_color_override("font_outline_color")) {
+ ur->add_undo_method(edited_control, "add_theme_color_override", "font_outline_color", edited_control->get_theme_color(SNAME("font_outline_color")));
+ } else {
+ ur->add_undo_method(edited_control, "remove_theme_color_override", "font_outline_color");
+ }
}
ur->add_do_method(this, "_update_control");
@@ -334,43 +455,63 @@ void TextControlEditor::_outline_color_changed(const Color &p_color) {
}
void TextControlEditor::_clear_formatting() {
- if (!edited_control) {
+ if (edited_controls.is_empty()) {
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Clear Control Formatting"));
- ur->add_do_method(edited_control, "begin_bulk_theme_override");
- ur->add_undo_method(edited_control, "begin_bulk_theme_override");
+ int count = edited_controls.size();
+ for (int i = 0; i < count; ++i) {
+ Control *edited_control = edited_controls[i];
- ur->add_do_method(edited_control, "remove_theme_font_override", edited_font);
- if (edited_control->has_theme_font_override(edited_font)) {
- ur->add_undo_method(edited_control, "add_theme_font_override", edited_font, edited_control->get_theme_font(edited_font));
- }
+ StringName edited_color;
+ StringName edited_font;
+ StringName edited_font_size;
- ur->add_do_method(edited_control, "remove_theme_font_size_override", edited_font_size);
- if (edited_control->has_theme_font_size_override(edited_font_size)) {
- ur->add_undo_method(edited_control, "add_theme_font_size_override", edited_font_size, edited_control->get_theme_font_size(edited_font_size));
- }
+ // Get override names.
+ if (Object::cast_to<RichTextLabel>(edited_control)) {
+ edited_color = SNAME("default_color");
+ edited_font = SNAME("normal_font");
+ edited_font_size = SNAME("normal_font_size");
+ } else {
+ edited_color = SNAME("font_color");
+ edited_font = SNAME("font");
+ edited_font_size = SNAME("font_size");
+ }
- ur->add_do_method(edited_control, "remove_theme_color_override", edited_color);
- if (edited_control->has_theme_color_override(edited_color)) {
- ur->add_undo_method(edited_control, "add_theme_color_override", edited_color, edited_control->get_theme_color(edited_color));
- }
+ ur->add_do_method(edited_control, "begin_bulk_theme_override");
+ ur->add_undo_method(edited_control, "begin_bulk_theme_override");
- ur->add_do_method(edited_control, "remove_theme_color_override", "font_outline_color");
- if (edited_control->has_theme_color_override("font_outline_color")) {
- ur->add_undo_method(edited_control, "add_theme_color_override", "font_outline_color", edited_control->get_theme_color("font_outline_color"));
- }
+ ur->add_do_method(edited_control, "remove_theme_font_override", edited_font);
+ if (edited_control->has_theme_font_override(edited_font)) {
+ ur->add_undo_method(edited_control, "add_theme_font_override", edited_font, edited_control->get_theme_font(edited_font));
+ }
- ur->add_do_method(edited_control, "remove_theme_constant_override", "outline_size");
- if (edited_control->has_theme_constant_override("outline_size")) {
- ur->add_undo_method(edited_control, "add_theme_constant_override", "outline_size", edited_control->get_theme_constant("outline_size"));
- }
+ ur->add_do_method(edited_control, "remove_theme_font_size_override", edited_font_size);
+ if (edited_control->has_theme_font_size_override(edited_font_size)) {
+ ur->add_undo_method(edited_control, "add_theme_font_size_override", edited_font_size, edited_control->get_theme_font_size(edited_font_size));
+ }
- ur->add_do_method(edited_control, "end_bulk_theme_override");
- ur->add_undo_method(edited_control, "end_bulk_theme_override");
+ ur->add_do_method(edited_control, "remove_theme_color_override", edited_color);
+ if (edited_control->has_theme_color_override(edited_color)) {
+ ur->add_undo_method(edited_control, "add_theme_color_override", edited_color, edited_control->get_theme_color(edited_color));
+ }
+
+ ur->add_do_method(edited_control, "remove_theme_color_override", "font_outline_color");
+ if (edited_control->has_theme_color_override("font_outline_color")) {
+ ur->add_undo_method(edited_control, "add_theme_color_override", "font_outline_color", edited_control->get_theme_color(SNAME("font_outline_color")));
+ }
+
+ ur->add_do_method(edited_control, "remove_theme_constant_override", "outline_size");
+ if (edited_control->has_theme_constant_override("outline_size")) {
+ ur->add_undo_method(edited_control, "add_theme_constant_override", "outline_size", edited_control->get_theme_constant(SNAME("outline_size")));
+ }
+
+ ur->add_do_method(edited_control, "end_bulk_theme_override");
+ ur->add_undo_method(edited_control, "end_bulk_theme_override");
+ }
ur->add_do_method(this, "_update_control");
ur->add_undo_method(this, "_update_control");
@@ -380,24 +521,54 @@ void TextControlEditor::_clear_formatting() {
void TextControlEditor::edit(Object *p_object) {
Control *ctrl = Object::cast_to<Control>(p_object);
- if (!ctrl) {
- edited_control = nullptr;
- custom_font = Ref<Font>();
- } else {
- edited_control = ctrl;
- custom_font = Ref<Font>();
+ MultiNodeEdit *multi_node = Object::cast_to<MultiNodeEdit>(p_object);
+
+ edited_controls.clear();
+ custom_font = Ref<Font>();
+ if (ctrl) {
+ edited_controls.append(ctrl);
+ _update_control();
+ } else if (multi_node && handles(multi_node)) {
+ int count = multi_node->get_node_count();
+ Node *scene = EditorNode::get_singleton()->get_edited_scene();
+
+ for (int i = 0; i < count; ++i) {
+ Control *child = Object::cast_to<Control>(scene->get_node(multi_node->get_node(i)));
+ edited_controls.append(child);
+ }
_update_control();
}
}
bool TextControlEditor::handles(Object *p_object) const {
Control *ctrl = Object::cast_to<Control>(p_object);
- if (!ctrl) {
+ MultiNodeEdit *multi_node = Object::cast_to<MultiNodeEdit>(p_object);
+
+ if (!ctrl && !multi_node) {
return false;
- } else {
+ } else if (ctrl) {
bool valid = false;
ctrl->get("text", &valid);
return valid;
+ } else {
+ bool valid = true;
+ int count = multi_node->get_node_count();
+ Node *scene = EditorNode::get_singleton()->get_edited_scene();
+
+ for (int i = 0; i < count; ++i) {
+ bool temp_valid = false;
+ Control *child = Object::cast_to<Control>(scene->get_node(multi_node->get_node(i)));
+ if (child) {
+ child->get("text", &temp_valid);
+ }
+ valid = valid && temp_valid;
+
+ if (!valid) {
+ break;
+ }
+ }
+
+ return valid;
}
}
diff --git a/editor/plugins/text_control_editor_plugin.h b/editor/plugins/text_control_editor_plugin.h
index d284a30f16..5941c100ec 100644
--- a/editor/plugins/text_control_editor_plugin.h
+++ b/editor/plugins/text_control_editor_plugin.h
@@ -62,10 +62,7 @@ class TextControlEditor : public HBoxContainer {
ColorPickerButton *outline_color_picker = nullptr;
Button *clear_formatting = nullptr;
- Control *edited_control = nullptr;
- String edited_color;
- String edited_font;
- String edited_font_size;
+ Vector<Control *> edited_controls;
Ref<Font> custom_font;
protected:
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 12003738e7..ae921871c3 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -40,7 +40,7 @@
void TileDataEditor::_tile_set_changed_plan_update() {
_tile_set_changed_update_needed = true;
- call_deferred("_tile_set_changed_deferred_update");
+ call_deferred(SNAME("_tile_set_changed_deferred_update"));
}
void TileDataEditor::_tile_set_changed_deferred_update() {
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 89027b174d..6e3724ead9 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -2337,7 +2337,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
bool to_replace_modified = true;
while (to_replace_modified) {
// Get the constraints from the removed cells.
- removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set);
+ removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set, false);
// Filter the sources to make sure they are in the potential_to_replace.
Map<TileMap::TerrainConstraint, Set<Vector2i>> per_constraint_tiles;
@@ -3735,7 +3735,7 @@ void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_
String str = pi.name.trim_prefix(p_array_prefix);
int to_char_index = 0;
while (to_char_index < str.length()) {
- if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ if (!is_digit(str[to_char_index])) {
break;
}
to_char_index++;
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index e708b83440..1bd1cd0219 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2593,7 +2593,7 @@ void EditorPropertyTilePolygon::_polygons_changed() {
changed_properties.push_back(vformat(element_pattern, i));
values.push_back(generic_tile_polygon_editor->get_polygon(i));
}
- emit_signal("multiple_properties_changed", changed_properties, values, false);
+ emit_signal(SNAME("multiple_properties_changed"), changed_properties, values, false);
}
}
}
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index be261927ee..ab355d4658 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -458,7 +458,7 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_
String str = pi.name.trim_prefix(p_array_prefix);
int to_char_index = 0;
while (to_char_index < str.length()) {
- if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ if (!is_digit(str[to_char_index])) {
break;
}
to_char_index++;
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 9ab53bf5c3..dd19f3d781 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -585,14 +585,14 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
Button *add_input_btn = memnew(Button);
add_input_btn->set_text(TTR("Add Input"));
- add_input_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_add_input_port), varray(p_id, group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, input_port_name), CONNECT_DEFERRED);
+ add_input_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_add_input_port), varray(p_id, group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR_3D, input_port_name), CONNECT_DEFERRED);
hb2->add_child(add_input_btn);
hb2->add_spacer();
Button *add_output_btn = memnew(Button);
add_output_btn->set_text(TTR("Add Output"));
- add_output_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_add_output_port), varray(p_id, group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, output_port_name), CONNECT_DEFERRED);
+ add_output_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_add_output_port), varray(p_id, group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR_3D, output_port_name), CONNECT_DEFERRED);
hb2->add_child(add_output_btn);
node->add_child(hb2);
@@ -606,7 +606,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
output_port_count += 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
output_port_count += 3;
} break;
default:
@@ -628,7 +628,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
i -= 2;
}
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
if (expanded_port_counter >= 3) {
expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
expanded_port_counter = 0;
@@ -852,7 +852,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_2D;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
port_offset++;
valid_left = (i + 1) < vsnode->get_input_port_count();
port_left = VisualShaderNode::PORT_TYPE_SCALAR;
@@ -877,7 +877,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
}
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
- expanded_type = VisualShaderNode::PORT_TYPE_VECTOR;
+ expanded_type = VisualShaderNode::PORT_TYPE_VECTOR_3D;
} break;
default:
break;
@@ -1432,7 +1432,7 @@ void VisualShaderEditor::_update_options_menu() {
case VisualShaderNode::PORT_TYPE_VECTOR_2D:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")));
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")));
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
@@ -1789,7 +1789,7 @@ void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expa
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
type_size = 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
type_size = 3;
} break;
default:
@@ -2584,7 +2584,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
vsnode = Ref<VisualShaderNode>(vsn);
} else {
ERR_FAIL_COND(add_options[p_idx].script.is_null());
- String base_type = add_options[p_idx].script->get_instance_base_type();
+ StringName base_type = add_options[p_idx].script->get_instance_base_type();
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(base_type));
ERR_FAIL_COND(!vsn);
vsnode = Ref<VisualShaderNode>(vsn);
@@ -2648,7 +2648,7 @@ void VisualShaderEditor::_add_node(int p_idx, const Vector<Variant> &p_ops, Stri
case VisualShaderNode::PORT_TYPE_VECTOR_2D:
initial_expression_code = "output0 = vec2(1.0, 1.0);";
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
initial_expression_code = "output0 = vec3(1.0, 1.0, 1.0);";
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
@@ -4182,7 +4182,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR_3D);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM);
graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SAMPLER);
//graph->add_valid_left_disconnect_type(0);
@@ -4202,27 +4202,27 @@ VisualShaderEditor::VisualShaderEditor() {
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR_3D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_VECTOR_3D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR_INT, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_VECTOR_3D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_2D, VisualShaderNode::PORT_TYPE_BOOLEAN);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR_INT);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_BOOLEAN);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_SCALAR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_SCALAR_INT);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_VECTOR_2D);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_VECTOR_3D);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR_3D, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR_INT);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_2D);
- graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR);
+ graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR_3D);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM);
graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER);
@@ -4446,23 +4446,23 @@ VisualShaderEditor::VisualShaderEditor() {
// COLOR
- add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeVectorFunc::FUNC_HSV2RGB }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeVectorFunc::FUNC_RGB2HSV }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeVectorFunc::FUNC_HSV2RGB }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeVectorFunc::FUNC_RGB2HSV }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
- add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), { VisualShaderNodeColorOp::OP_DARKEN }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), { VisualShaderNodeColorOp::OP_DIFFERENCE }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), { VisualShaderNodeColorOp::OP_DODGE }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator."), { VisualShaderNodeColorOp::OP_HARD_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), { VisualShaderNodeColorOp::OP_LIGHTEN }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), { VisualShaderNodeColorOp::OP_OVERLAY }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), { VisualShaderNodeColorOp::OP_SCREEN }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), { VisualShaderNodeColorOp::OP_SOFT_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), { VisualShaderNodeColorOp::OP_DARKEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), { VisualShaderNodeColorOp::OP_DIFFERENCE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), { VisualShaderNodeColorOp::OP_DODGE }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator."), { VisualShaderNodeColorOp::OP_HARD_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), { VisualShaderNodeColorOp::OP_LIGHTEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), { VisualShaderNodeColorOp::OP_OVERLAY }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), { VisualShaderNodeColorOp::OP_SCREEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), { VisualShaderNodeColorOp::OP_SOFT_LIGHT }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant.")));
add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform.")));
@@ -4478,13 +4478,13 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Equal", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("GreaterThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("GreaterThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("IsInf", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_INF }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("IsNaN", "Conditional", "Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between NaN and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_NAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("LessThan", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("LessThanEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("NotEqual", "Conditional", "Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN));
- add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Switch2D", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("SwitchBool", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN));
add_options.push_back(AddOption("SwitchFloat", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -4506,7 +4506,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "camera"), { "camera" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_camera"), { "inv_camera" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "inv_projection"), { "inv_projection" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("OutputIsSRGB", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "output_is_srgb"), { "output_is_srgb" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "projection"), { "projection" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL));
@@ -4518,7 +4518,7 @@ VisualShaderEditor::VisualShaderEditor() {
// CANVASITEM-FOR-ALL
add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "texture_pixel_size"), { "texture_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "uv"), { "uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, -1, Shader::MODE_CANVAS_ITEM));
@@ -4527,9 +4527,9 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Active", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "active"), { "active" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), { "attractor_force" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), { "custom" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("AttractorForce", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "attractor_force"), { "attractor_force" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Custom", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom"), { "custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("CustomAlpha", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "custom_alpha"), { "custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("Delta", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "delta"), { "delta" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("EmissionTransform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "emission_transform"), { "emission_transform" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
@@ -4538,7 +4538,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Restart", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "restart"), { "restart" }, VisualShaderNode::PORT_TYPE_BOOLEAN, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("Transform", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "transform"), { "transform" }, VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), { "velocity" }, VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("Velocity", "Input", "All", "VisualShaderNodeInput", vformat(input_param_shader_modes, "velocity"), { "velocity" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, -1, Shader::MODE_PARTICLES));
/////////////////
@@ -4561,46 +4561,46 @@ VisualShaderEditor::VisualShaderEditor() {
// NODE3D INPUTS
add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InstanceId", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_id"), { "instance_id" }, VisualShaderNode::PORT_TYPE_SCALAR_INT, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "modelview"), { "modelview" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "alpha"), { "alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "binormal"), { "binormal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "color"), { "color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("DepthTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "depth_texture"), { "depth_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("FrontFacing", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "front_facing"), { "front_facing" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "albedo"), { "albedo" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "albedo"), { "albedo" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "attenuation"), { "attenuation" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Backlight", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "backlight"), { "backlight" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "diffuse"), { "diffuse" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Backlight", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "backlight"), { "backlight" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "diffuse"), { "diffuse" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Metallic", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic"), { "metallic" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness"), { "roughness" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular"), { "specular" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
- add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular"), { "specular" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
+ add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL));
// CANVASITEM INPUTS
add_options.push_back(AddOption("AtLightPass", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Canvas", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "canvas"), { "canvas" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("InstanceCustom", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom"), { "instance_custom" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("InstanceCustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "instance_custom_alpha"), { "instance_custom_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "point_size"), { "point_size" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Screen", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "screen"), { "screen" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
@@ -4608,31 +4608,31 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", vformat(input_param_for_vertex_shader_mode, "world"), { "world" }, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("AtLightPass", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "at_light_pass"), { "at_light_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("NormalTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "normal_texture"), { "normal_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_pixel_size"), { "screen_pixel_size" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("ScreenTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_texture"), { "screen_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininess", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("SpecularShininessTexture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "specular_shininess_texture"), { "specular_shininess_texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Texture", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_mode, "vertex"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "fragcoord"), { "fragcoord" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light"), { "light" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_alpha"), { "light_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color"), { "light_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("LightColorAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_color_alpha"), { "light_color_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightPosition", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_position"), { "light_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("LightVertex", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "light_vertex"), { "light_vertex" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightPosition", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_position"), { "light_position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("LightVertex", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "light_vertex"), { "light_vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "normal"), { "normal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "point_coord"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow"), { "shadow" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("Shadow", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow"), { "shadow" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("ShadowAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "shadow_alpha"), { "shadow_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SpecularShininess", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess"), { "specular_shininess" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("SpecularShininessAlpha", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "specular_shininess_alpha"), { "specular_shininess_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("Texture", "Input", "Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "texture"), { "texture" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
@@ -4641,27 +4641,27 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("AtCubeMapPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_cubemap_pass"), { "at_cubemap_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("AtHalfResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_half_res_pass"), { "at_half_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("AtQuarterResPass", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "at_quarter_res_pass"), { "at_quarter_res_pass" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), { "eyedir" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), { "half_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("EyeDir", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "eyedir"), { "eyedir" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("HalfResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_color"), { "half_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("HalfResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "half_res_alpha"), { "half_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), { "light0_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), { "light0_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_color"), { "light0_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light0Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_direction"), { "light0_direction" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light0Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_enabled"), { "light0_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light0Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light0_energy"), { "light0_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), { "light1_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), { "light1_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_color"), { "light1_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light1Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_direction"), { "light1_direction" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light1Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_enabled"), { "light1_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light1Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light1_energy"), { "light1_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), { "light2_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), { "light2_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_color"), { "light2_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light2Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_direction"), { "light2_direction" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light2Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_enabled"), { "light2_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light2Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light2_energy"), { "light2_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), { "light3_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), { "light3_direction" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Color", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_color"), { "light3_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Light3Direction", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_direction"), { "light3_direction" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light3Enabled", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_enabled"), { "light3_enabled" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Light3Energy", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "light3_energy"), { "light3_energy" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), { "position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
- add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), { "quarter_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("Position", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "position"), { "position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ add_options.push_back(AddOption("QuarterResColor", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_color"), { "quarter_res_color" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("QuarterResAlpha", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "quarter_res_alpha"), { "quarter_res_alpha" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Radiance", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "radiance"), { "radiance" }, VisualShaderNode::PORT_TYPE_SAMPLER, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("ScreenUV", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "screen_uv"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_SKY, Shader::MODE_SKY));
@@ -4670,31 +4670,31 @@ VisualShaderEditor::VisualShaderEditor() {
// FOG INPUTS
- add_options.push_back(AddOption("WorldPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position"), { "world_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("ObjectPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "object_position"), { "object_position" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("UVW", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw"), { "uvw" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
- add_options.push_back(AddOption("Extents", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "extents"), { "extents" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("WorldPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position"), { "world_position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("ObjectPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "object_position"), { "object_position" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("UVW", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw"), { "uvw" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Extents", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "extents"), { "extents" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FOG, Shader::MODE_FOG));
add_options.push_back(AddOption("SDF", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "sdf"), { "sdf" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
add_options.push_back(AddOption("Time", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "time"), { "time" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
// PARTICLES INPUTS
add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), { "collision_depth" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), { "collision_normal" }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("CollisionNormal", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_normal"), { "collision_normal" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
// PARTICLES
add_options.push_back(AddOption("EmitParticle", "Particles", "", "VisualShaderNodeParticleEmit", "", {}, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ParticleAccelerator", "Particles", "", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("ParticleRandomness", "Particles", "", "VisualShaderNodeParticleRandomness", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
- add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("ConeVelocity", "Particles", "Velocity", "VisualShaderNodeParticleConeVelocity", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
// SCALAR
@@ -4783,11 +4783,11 @@ VisualShaderEditor::VisualShaderEditor() {
// SDF
{
- add_options.push_back(AddOption("ScreenUVToSDF", "SDF", "", "VisualShaderNodeScreenUVToSDF", TTR("Converts screen UV to a SDF."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("ScreenUVToSDF", "SDF", "", "VisualShaderNodeScreenUVToSDF", TTR("Converts screen UV to a SDF."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("SDFRaymarch", "SDF", "", "VisualShaderNodeSDFRaymarch", TTR("Casts a ray against the screen SDF and returns the distance travelled."), {}, -1, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("SDFToScreenUV", "SDF", "", "VisualShaderNodeSDFToScreenUV", TTR("Converts a SDF to screen UV."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("SDFToScreenUV", "SDF", "", "VisualShaderNodeSDFToScreenUV", TTR("Converts a SDF to screen UV."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
add_options.push_back(AddOption("TextureSDF", "SDF", "", "VisualShaderNodeTextureSDF", TTR("Performs a SDF texture lookup."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
- add_options.push_back(AddOption("TextureSDFNormal", "SDF", "", "VisualShaderNodeTextureSDFNormal", TTR("Performs a SDF normal texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
+ add_options.push_back(AddOption("TextureSDFNormal", "SDF", "", "VisualShaderNodeTextureSDFNormal", TTR("Performs a SDF normal texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, Shader::MODE_CANVAS_ITEM));
}
// TEXTURES
@@ -4834,146 +4834,146 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Multiply", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("MultiplyComp", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("Subtract", "Transform", "Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM));
- add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM));
// VECTOR
- add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("VectorCompose", "Vector", "Common", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars.")));
add_options.push_back(AddOption("VectorDecompose", "Vector", "Common", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars.")));
add_options.push_back(AddOption("Vector2Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 2D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
add_options.push_back(AddOption("Vector2Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 2D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_2D }));
- add_options.push_back(AddOption("Vector3Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 3D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Vector3Compose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes 3D vector from three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Vector3Decompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes 3D vector to three scalars."), { VisualShaderNodeVectorCompose::OP_TYPE_VECTOR_3D }));
add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), { VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), { VisualShaderNodeClamp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), { VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic cosine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), { VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), { VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdX", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_X, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("DFdY", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), { VisualShaderNodeDerivativeFunc::FUNC_Y, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Distance2D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Distance3D", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), { VisualShaderNodeVectorDistance::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), { VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns the vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), { VisualShaderNodeFaceForward::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), { VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), { VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Fresnel", "Vector", "Functions", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), {}, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), { VisualShaderNodeVectorLen::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), { VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), { VisualShaderNodeVectorOp::OP_MAX, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), { VisualShaderNodeVectorOp::OP_MIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("MixS", "Vector", "Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("MultiplyAdd", "Vector", "Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), { VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), { VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns the vector that points in the direction of refraction."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest even integer to the parameter."), { VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), { VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), { VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), { VisualShaderNodeSmoothStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
- add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
+ add_options.push_back(AddOption("Sum", "Vector", "Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true));
add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 2D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D));
add_options.push_back(AddOption("Vector2Constant", "Vector", "Variables", "VisualShaderNodeVec2Constant", TTR("2D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Vector3Constant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("3D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Vector2Uniform", "Vector", "Variables", "VisualShaderNodeVec2Uniform", TTR("2D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D));
- add_options.push_back(AddOption("Vector3Uniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("3D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Vector3Constant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("3D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
+ add_options.push_back(AddOption("Vector3Uniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("3D vector uniform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D));
// SPECIAL
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index f39a494df8..b8775cd3ed 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -1275,8 +1275,6 @@ ProjectExportDialog::ProjectExportDialog() {
set_hide_on_ok(false);
- editor_icons = "EditorIcons";
-
default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", "");
// If no default set, use project name
if (default_filename.is_empty()) {
diff --git a/editor/project_export.h b/editor/project_export.h
index 3d90a0d3ff..09e402c67f 100644
--- a/editor/project_export.h
+++ b/editor/project_export.h
@@ -85,8 +85,6 @@ private:
Label *include_label;
MarginContainer *include_margin;
- StringName editor_icons;
-
Button *export_button;
Button *export_all_button;
AcceptDialog *export_all_dialog;
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 08e0f7ae30..57e47a15fd 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -40,7 +40,6 @@
#include "core/os/os.h"
#include "core/string/translation.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "editor/editor_vcs_interface.h"
#include "editor_scale.h"
#include "editor_settings.h"
@@ -1423,7 +1422,7 @@ void ProjectList::create_project_item_control(int p_index) {
Button *show = memnew(Button);
// Display a folder icon if the project directory can be opened, or a "broken file" icon if it can't.
- show->set_icon(get_theme_icon(!item.missing ? "Load" : "FileBroken", "EditorIcons"));
+ show->set_icon(get_theme_icon(!item.missing ? SNAME("Load") : SNAME("FileBroken"), SNAME("EditorIcons")));
show->set_flat(true);
if (!item.grayed) {
// Don't make the icon less prominent if the parent is already grayed out.
@@ -1475,7 +1474,7 @@ void ProjectList::sort_projects() {
bool visible = true;
if (!_search_term.is_empty()) {
String search_path;
- if (_search_term.find("/") != -1) {
+ if (_search_term.contains("/")) {
// Search path will match the whole path
search_path = item.path;
} else {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index bedae4f8a9..c0ff1d72ee 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -242,7 +242,7 @@ void ProjectSettingsEditor::unhandled_input(const Ref<InputEvent> &p_event) {
String ProjectSettingsEditor::_get_setting_name() const {
String name = property_box->get_text().strip_edges();
- if (name.find("/") == -1) {
+ if (!name.contains("/")) {
name = "global/" + name;
}
return name;
@@ -512,6 +512,16 @@ void ProjectSettingsEditor::_update_theme() {
restart_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
restart_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
restart_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
+
+ type_box->clear();
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ // There's no point in adding Nil types, and Object types
+ // can't be serialized correctly in the project settings.
+ if (i != Variant::NIL && i != Variant::OBJECT) {
+ String type = Variant::get_type_name(Variant::Type(i));
+ type_box->add_icon_item(get_theme_icon(type, SNAME("EditorIcons")), type, i);
+ }
+ }
}
void ProjectSettingsEditor::_notification(int p_what) {
@@ -526,9 +536,9 @@ void ProjectSettingsEditor::_notification(int p_what) {
_update_action_map_editor();
_update_theme();
} break;
- case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_THEME_CHANGED: {
_update_theme();
- break;
+ } break;
}
}
@@ -589,14 +599,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
type_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
header->add_child(type_box);
- for (int i = 0; i < Variant::VARIANT_MAX; i++) {
- // There's no point in adding Nil types, and Object types
- // can't be serialized correctly in the project settings.
- if (i != Variant::NIL && i != Variant::OBJECT) {
- type_box->add_item(Variant::get_type_name(Variant::Type(i)), i);
- }
- }
-
add_button = memnew(Button);
add_button->set_text(TTR("Add"));
add_button->set_disabled(true);
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 9d894afa6f..405e263a62 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -269,7 +269,9 @@ void CustomPropertyEditor::_menu_option(int p_which) {
res->call("set_instance_base_type", owner->get_class());
}
+ EditorNode::get_editor_data().instantiate_object_properties(obj);
v = obj;
+
emit_signal(SNAME("variant_changed"));
} break;
@@ -623,7 +625,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
MAKE_PROPSELECT
Variant::Type type = Variant::NIL;
String tname = hint_text;
- if (tname.find(".") != -1) {
+ if (tname.contains(".")) {
tname = tname.get_slice(".", 0);
}
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@@ -1092,7 +1094,9 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) {
ERR_FAIL_COND(!obj);
ERR_FAIL_COND(!Object::cast_to<Resource>(obj));
+ EditorNode::get_editor_data().instantiate_object_properties(obj);
v = obj;
+
emit_signal(SNAME("variant_changed"));
hide();
}
@@ -1283,7 +1287,9 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
ERR_BREAK(!obj);
ERR_BREAK(!Object::cast_to<Resource>(obj));
+ EditorNode::get_editor_data().instantiate_object_properties(obj);
v = obj;
+
emit_signal(SNAME("variant_changed"));
hide();
}
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 7c0683c37b..0862efb4ee 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -214,10 +214,13 @@ void PropertySelector::_update_search() {
Variant::construct(type, v, nullptr, 0, ce);
v.get_method_list(&methods);
} else {
- Object *obj = ObjectDB::get_instance(script);
- if (Object::cast_to<Script>(obj)) {
+ Ref<Script> script_ref = Object::cast_to<Script>(ObjectDB::get_instance(script));
+ if (script_ref.is_valid()) {
methods.push_back(MethodInfo("*Script Methods"));
- Object::cast_to<Script>(obj)->get_script_method_list(&methods);
+ if (script_ref->is_built_in()) {
+ script_ref->reload(true);
+ }
+ script_ref->get_script_method_list(&methods);
}
StringName base = base_type;
@@ -276,7 +279,7 @@ void PropertySelector::_update_search() {
TreeItem *item = search_options->create_item(category ? category : root);
String desc;
- if (mi.name.find(":") != -1) {
+ if (mi.name.contains(":")) {
desc = mi.name.get_slice(":", 1) + " ";
mi.name = mi.name.get_slice(":", 0);
} else if (mi.return_val.type != Variant::NIL) {
@@ -296,7 +299,7 @@ void PropertySelector::_update_search() {
if (mi.arguments[i].type == Variant::NIL) {
desc += ": Variant";
- } else if (mi.arguments[i].name.find(":") != -1) {
+ } else if (mi.arguments[i].name.contains(":")) {
desc += vformat(": %s", mi.arguments[i].name.get_slice(":", 1));
mi.arguments[i].name = mi.arguments[i].name.get_slice(":", 0);
} else {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 9b18d3a491..0e362b13c6 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1206,8 +1206,16 @@ void SceneTreeDock::_notification(int p_what) {
create_root_dialog->add_child(top_row);
+ ScrollContainer *scroll_container = memnew(ScrollContainer);
+ scroll_container->set_name("NodeShortcutsScrollContainer");
+ create_root_dialog->add_child(scroll_container);
+ scroll_container->set_v_size_flags(SIZE_EXPAND_FILL);
+ scroll_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+
VBoxContainer *node_shortcuts = memnew(VBoxContainer);
node_shortcuts->set_name("NodeShortcuts");
+ scroll_container->add_child(node_shortcuts);
+ node_shortcuts->set_h_size_flags(SIZE_EXPAND_FILL);
VBoxContainer *beginner_node_shortcuts = memnew(VBoxContainer);
beginner_node_shortcuts->set_name("BeginnerNodeShortcuts");
@@ -1247,8 +1255,6 @@ void SceneTreeDock::_notification(int p_what) {
button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")));
button_clipboard->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_PASTE, false));
- node_shortcuts->add_spacer();
- create_root_dialog->add_child(node_shortcuts);
_update_create_root_dialog();
} break;
@@ -3112,7 +3118,7 @@ void SceneTreeDock::_local_tree_selected() {
void SceneTreeDock::_update_create_root_dialog() {
BaseButton *toggle = Object::cast_to<BaseButton>(create_root_dialog->get_node(String("NodeShortcutsTopRow/NodeShortcutsToggle")));
- Node *node_shortcuts = create_root_dialog->get_node(String("NodeShortcuts"));
+ Node *node_shortcuts = create_root_dialog->get_node(String("NodeShortcutsScrollContainer/NodeShortcuts"));
if (!toggle || !node_shortcuts) {
return;
@@ -3142,6 +3148,7 @@ void SceneTreeDock::_update_create_root_dialog() {
Button *button = memnew(Button);
favorite_nodes->add_child(button);
button->set_text(l);
+ button->set_clip_text(true);
String name = l.get_slicec(' ', 0);
if (ScriptServer::is_global_class(name)) {
name = ScriptServer::get_global_class_native_base(name);
@@ -3403,6 +3410,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel
create_root_dialog = memnew(VBoxContainer);
vbc->add_child(create_root_dialog);
+ create_root_dialog->set_v_size_flags(SIZE_EXPAND_FILL);
create_root_dialog->hide();
scene_tree = memnew(SceneTreeEditor(false, true, true));
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index c755bca64f..e9aed53b4e 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -362,6 +362,17 @@ bool SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent, bool p_scroll
}
_update_visibility_color(p_node, item);
+ } else if (p_node->is_class("CanvasLayer")) {
+ bool v = p_node->call("is_visible");
+ if (v) {
+ item->add_button(0, get_theme_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
+ } else {
+ item->add_button(0, get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
+ }
+
+ if (!p_node->is_connected("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed))) {
+ p_node->connect("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed), varray(p_node));
+ }
} else if (p_node->is_class("Node3D")) {
bool is_locked = p_node->has_meta("_edit_lock_");
if (is_locked) {
@@ -471,6 +482,9 @@ void SceneTreeEditor::_node_visibility_changed(Node *p_node) {
if (p_node->is_class("CanvasItem")) {
visible = p_node->call("is_visible");
CanvasItemEditor::get_singleton()->get_viewport_control()->update();
+ } else if (p_node->is_class("CanvasLayer")) {
+ visible = p_node->call("is_visible");
+ CanvasItemEditor::get_singleton()->get_viewport_control()->update();
} else if (p_node->is_class("Node3D")) {
visible = p_node->call("is_visible");
}
@@ -514,7 +528,7 @@ void SceneTreeEditor::_node_removed(Node *p_node) {
p_node->disconnect("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed));
}
- if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem")) {
+ if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer")) {
if (p_node->is_connected("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed))) {
p_node->disconnect("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed));
}
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 360ba5780c..b42d4a1d6d 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -153,7 +153,7 @@ bool ScriptCreateDialog::_validate_class(const String &p_string) {
}
}
- 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 = is_ascii_identifier_char(p_string[i]) || p_string[i] == '.';
if (!valid_char) {
return false;
@@ -378,7 +378,7 @@ void ScriptCreateDialog::_language_changed(int l) {
String path = file_path->get_text();
String extension = "";
if (!path.is_empty()) {
- if (path.find(".") != -1) {
+ if (path.contains(".")) {
extension = path.get_extension();
}
@@ -656,7 +656,7 @@ void ScriptCreateDialog::_update_dialog() {
if (is_new_script_created) {
class_name->set_editable(true);
class_name->set_placeholder(TTR("Allowed: a-z, A-Z, 0-9, _ and ."));
- Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ Color placeholder_color = class_name->get_theme_color(SNAME("font_placeholder_color"));
placeholder_color.a = 0.3;
class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
} else {
@@ -665,7 +665,7 @@ void ScriptCreateDialog::_update_dialog() {
} else {
class_name->set_editable(false);
class_name->set_placeholder(TTR("N/A"));
- Color placeholder_color = class_name->get_theme_color("font_placeholder_color");
+ Color placeholder_color = class_name->get_theme_color(SNAME("font_placeholder_color"));
placeholder_color.a = 1;
class_name->add_theme_color_override("font_placeholder_color", placeholder_color);
class_name->set_text("");
@@ -763,10 +763,10 @@ void ScriptCreateDialog::_update_dialog() {
}
ScriptLanguage::ScriptTemplate ScriptCreateDialog::_get_current_template() const {
- int selected_id = template_menu->get_selected_id();
+ int selected_index = template_menu->get_selected();
for (const ScriptLanguage::ScriptTemplate &t : template_list) {
if (is_using_templates) {
- if (t.id == selected_id) {
+ if (t.id == selected_index) {
return t;
}
} else {
@@ -811,7 +811,7 @@ ScriptLanguage::ScriptTemplate ScriptCreateDialog::_parse_template(const ScriptL
List<String> comment_delimiters;
language->get_comment_delimiters(&comment_delimiters);
for (const String &script_delimiter : comment_delimiters) {
- if (script_delimiter.find(" ") == -1) {
+ if (!script_delimiter.contains(" ")) {
meta_delimiter = script_delimiter;
break;
}
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index 239860c9ab..95c4c5ff0d 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -221,7 +221,7 @@ void ShaderCreateDialog::_language_changed(int p_language) {
String extension = "";
if (!path.is_empty()) {
- if (path.find(".") != -1) {
+ if (path.contains(".")) {
extension = path.get_extension();
}
if (extension.length() == 0) {
diff --git a/main/main.cpp b/main/main.cpp
index a632059849..0bca5bef0c 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -50,7 +50,6 @@
#include "core/register_core_types.h"
#include "core/string/translation.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "drivers/register_driver_types.h"
#include "main/app_icon.gen.h"
#include "main/main_timer_sync.h"
@@ -200,7 +199,7 @@ static String unescape_cmdline(const String &p_str) {
static String get_full_version_string() {
String hash = String(VERSION_HASH);
- if (hash.length() != 0) {
+ if (!hash.is_empty()) {
hash = "." + hash.left(9);
}
return String(VERSION_FULL_BUILD) + hash;
@@ -827,7 +826,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (I->next()) {
String vm = I->next()->get();
- if (vm.find("x") == -1) { // invalid parameter format
+ if (!vm.contains("x")) { // invalid parameter format
OS::get_singleton()->print("Invalid resolution '%s', it should be e.g. '1280x720'.\n",
vm.utf8().get_data());
@@ -858,7 +857,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (I->next()) {
String vm = I->next()->get();
- if (vm.find(",") == -1) { // invalid parameter format
+ if (!vm.contains(",")) { // invalid parameter format
OS::get_singleton()->print("Invalid position '%s', it should be e.g. '80,128'.\n",
vm.utf8().get_data());
@@ -940,7 +939,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--debug-server") {
if (I->next()) {
debug_server_uri = I->next()->get();
- if (debug_server_uri.find("://") == -1) { // wrong address
+ if (!debug_server_uri.contains("://")) { // wrong address
OS::get_singleton()->print("Invalid debug server uri. It should be of the form <protocol>://<bind_address>:<port>.\n");
goto error;
}
@@ -1073,7 +1072,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
} else if (I->get() == "--remote-debug") {
if (I->next()) {
debug_uri = I->next()->get();
- if (debug_uri.find("://") == -1) { // wrong address
+ if (!debug_uri.contains("://")) { // wrong address
OS::get_singleton()->print(
"Invalid debug host address, it should be of the form <protocol>://<host/IP>:<port>.\n");
goto error;
@@ -1130,7 +1129,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
if (!remotefs.is_empty()) {
file_access_network_client = memnew(FileAccessNetworkClient);
int port;
- if (remotefs.find(":") != -1) {
+ if (remotefs.contains(":")) {
port = remotefs.get_slicec(':', 1).to_int();
remotefs = remotefs.get_slicec(':', 0);
} else {
@@ -2250,9 +2249,8 @@ bool Main::start() {
}
}
- if (main_loop->is_class("SceneTree")) {
- SceneTree *sml = Object::cast_to<SceneTree>(main_loop);
-
+ SceneTree *sml = Object::cast_to<SceneTree>(main_loop);
+ if (sml) {
#ifdef DEBUG_ENABLED
if (debug_collisions) {
sml->set_debug_collisions_hint(true);
@@ -2294,20 +2292,18 @@ bool Main::start() {
RES res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
Node *n = nullptr;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instantiate();
- } else if (res->is_class("Script")) {
- Ref<Script> script_res = res;
+ Ref<PackedScene> scn = res;
+ Ref<Script> script_res = res;
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ } else if (script_res.is_valid()) {
StringName ibt = script_res->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
Object *obj = ClassDB::instantiate(ibt);
- ERR_CONTINUE_MSG(obj == nullptr,
- "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
- String(ibt));
+ ERR_CONTINUE_MSG(!obj, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt) + ".");
n = Object::cast_to<Node>(obj);
n->set_script(script_res);
diff --git a/methods.py b/methods.py
index fbd304ddde..fe84641e9d 100644
--- a/methods.py
+++ b/methods.py
@@ -111,10 +111,9 @@ def update_version(module_version_string=""):
f.close()
# NOTE: It is safe to generate this file here, since this is still executed serially
- fhash = open("core/version_hash.gen.h", "w")
+ fhash = open("core/version_hash.gen.cpp", "w")
fhash.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
- fhash.write("#ifndef VERSION_HASH_GEN_H\n")
- fhash.write("#define VERSION_HASH_GEN_H\n")
+ fhash.write('#include "core/version.h"\n')
githash = ""
gitfolder = ".git"
@@ -132,8 +131,7 @@ def update_version(module_version_string=""):
else:
githash = head
- fhash.write('#define VERSION_HASH "' + githash + '"\n')
- fhash.write("#endif // VERSION_HASH_GEN_H\n")
+ fhash.write('const char *const VERSION_HASH = "' + githash + '";\n')
fhash.close()
diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html
index a681a2a1c3..9e03f50c37 100644
--- a/misc/dist/html/editor.html
+++ b/misc/dist/html/editor.html
@@ -265,6 +265,7 @@
<button id="btn-close-editor" class="btn close-btn" disabled="disabled" onclick="closeEditor()">×</button>
<button id="btn-tab-game" class="btn tab-btn" disabled="disabled" onclick="showTab('game')">Game</button>
<button id="btn-close-game" class="btn close-btn" disabled="disabled" onclick="closeGame()">×</button>
+ <button id="btn-tab-update" class="btn tab-btn" style="display: none;">Update</button>
</div>
<div id="tabs">
<div id="tab-loader">
@@ -324,10 +325,39 @@
<div id="status-notice" class="godot" style="display: none;"></div>
</div>
</div>
- <script>
+ <script>//<![CDATA[
window.addEventListener("load", () => {
+ function notifyUpdate(sw) {
+ const btn = document.getElementById("btn-tab-update");
+ btn.onclick = function () {
+ if (!window.confirm("Are you sure you want to update?\nClicking \"OK\" will reload all active instances!")) {
+ return;
+ }
+ sw.postMessage("update");
+ btn.innerHTML = "Updating...";
+ btn.disabled = true;
+ };
+ btn.style.display = "";
+ }
if ("serviceWorker" in navigator) {
- navigator.serviceWorker.register("service.worker.js");
+ navigator.serviceWorker.register("service.worker.js").then(function (reg) {
+ if (reg.waiting) {
+ notifyUpdate(reg.waiting);
+ }
+ reg.addEventListener("updatefound", function () {
+ const update = reg.installing;
+ update.addEventListener("statechange", function () {
+ if (update.state === "installed") {
+ // It's a new install, claim and perform aggressive caching.
+ if (!reg.active) {
+ update.postMessage("claim");
+ } else {
+ notifyUpdate(update);
+ }
+ }
+ });
+ });
+ });
}
if (localStorage.getItem("welcomeModalDismissed") !== 'true') {
@@ -342,7 +372,7 @@
localStorage.setItem("welcomeModalDismissed", 'true');
}
}
- </script>
+ //]]></script>
<script src="godot.tools.js"></script>
<script>//<![CDATA[
diff --git a/misc/dist/html/service-worker.js b/misc/dist/html/service-worker.js
index 063e40a6cb..310574f21d 100644
--- a/misc/dist/html/service-worker.js
+++ b/misc/dist/html/service-worker.js
@@ -4,7 +4,8 @@
// Incrementing CACHE_VERSION will kick off the install event and force
// previously cached resources to be updated from the network.
const CACHE_VERSION = "@GODOT_VERSION@";
-const CACHE_NAME = "@GODOT_NAME@-cache";
+const CACHE_PREFIX = "@GODOT_NAME@-sw-cache-";
+const CACHE_NAME = CACHE_PREFIX + CACHE_VERSION;
const OFFLINE_URL = "@GODOT_OFFLINE_PAGE@";
// Files that will be cached on load.
const CACHED_FILES = @GODOT_CACHE@;
@@ -13,26 +14,35 @@ const CACHABLE_FILES = @GODOT_OPT_CACHE@;
const FULL_CACHE = CACHED_FILES.concat(CACHABLE_FILES);
self.addEventListener("install", (event) => {
- event.waitUntil(async function () {
- const cache = await caches.open(CACHE_NAME);
- // Clear old cache (including optionals).
- await Promise.all(FULL_CACHE.map(path => cache.delete(path)));
- // Insert new one.
- const done = await cache.addAll(CACHED_FILES);
- return done;
- }());
+ event.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(CACHED_FILES)));
});
self.addEventListener("activate", (event) => {
- event.waitUntil(async function () {
- if ("navigationPreload" in self.registration) {
- await self.registration.navigationPreload.enable();
- }
- }());
- // Tell the active service worker to take control of the page immediately.
- self.clients.claim();
+ event.waitUntil(caches.keys().then(
+ function (keys) {
+ // Remove old caches.
+ return Promise.all(keys.filter(key => key.startsWith(CACHE_PREFIX) && key != CACHE_NAME).map(key => caches.delete(key)));
+ }).then(function() {
+ // Enable navigation preload if available.
+ return ("navigationPreload" in self.registration) ? self.registration.navigationPreload.enable() : Promise.resolve();
+ })
+ );
});
+async function fetchAndCache(event, cache, isCachable) {
+ // Use the preloaded response, if it's there
+ let response = await event.preloadResponse;
+ if (!response) {
+ // Or, go over network.
+ response = await self.fetch(event.request);
+ }
+ if (isCachable) {
+ // And update the cache
+ cache.put(event.request, response.clone());
+ }
+ return response;
+}
+
self.addEventListener("fetch", (event) => {
const isNavigate = event.request.mode === "navigate";
const url = event.request.url || "";
@@ -42,32 +52,54 @@ self.addEventListener("fetch", (event) => {
const isCachable = FULL_CACHE.some(v => v === local) || (base === referrer && base.endsWith(CACHED_FILES[0]));
if (isNavigate || isCachable) {
event.respondWith(async function () {
- try {
- // Use the preloaded response, if it's there
- let request = event.request.clone();
- let response = await event.preloadResponse;
- if (!response) {
- // Or, go over network.
- response = await fetch(event.request);
- }
- if (isCachable) {
- // Update the cache
- const cache = await caches.open(CACHE_NAME);
- cache.put(request, response.clone());
- }
- return response;
- } catch (error) {
- const cache = await caches.open(CACHE_NAME);
- if (event.request.mode === "navigate") {
- // Check if we have full cache.
- const cached = await Promise.all(FULL_CACHE.map(name => cache.match(name)));
- const missing = cached.some(v => v === undefined);
- const cachedResponse = missing ? await caches.match(OFFLINE_URL) : await caches.match(CACHED_FILES[0]);
- return cachedResponse;
+ // Try to use cache first
+ const cache = await caches.open(CACHE_NAME);
+ if (event.request.mode === "navigate") {
+ // Check if we have full cache during HTML page request.
+ const fullCache = await Promise.all(FULL_CACHE.map(name => cache.match(name)));
+ const missing = fullCache.some(v => v === undefined);
+ if (missing) {
+ try {
+ // Try network if some cached file is missing (so we can display offline page in case).
+ return await fetchAndCache(event, cache, isCachable);
+ } catch (e) {
+ // And return the hopefully always cached offline page in case of network failure.
+ console.error("Network error: ", e);
+ return await caches.match(OFFLINE_URL);
+ }
}
- const cachedResponse = await caches.match(event.request);
- return cachedResponse;
+ }
+ const cached = await cache.match(event.request);
+ if (cached) {
+ return cached;
+ } else {
+ // Try network if don't have it in cache.
+ return await fetchAndCache(event, cache, isCachable);
}
}());
}
});
+
+self.addEventListener("message", (event) => {
+ // No cross origin
+ if (event.origin != self.origin) {
+ return;
+ }
+ const id = event.source.id || "";
+ const msg = event.data || "";
+ // Ensure it's one of our clients.
+ self.clients.get(id).then(function (client) {
+ if (!client) {
+ return; // Not a valid client.
+ }
+ if (msg === "claim") {
+ self.skipWaiting().then(() => self.clients.claim());
+ } else if (msg === "clear") {
+ caches.delete(CACHE_NAME);
+ } else if (msg === "update") {
+ self.skipWaiting().then(() => self.clients.claim()).then(() => self.clients.matchAll()).then(all => all.forEach(c => c.navigate(c.url)));
+ } else {
+ onClientMessage(event);
+ }
+ });
+});
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index 5ba45c0c13..e2505de3bf 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -325,7 +325,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug";
TARGETED_DEVICE_FAMILY = "$targeted_device_family";
- VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
+ VALID_ARCHS = "arm64 x86_64";
WRAPPER_EXTENSION = app;
};
name = Debug;
@@ -356,7 +356,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "$provisioning_profile_uuid_release";
TARGETED_DEVICE_FAMILY = "$targeted_device_family";
- VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
+ VALID_ARCHS = "arm64 x86_64";
WRAPPER_EXTENSION = app;
};
name = Release;
diff --git a/modules/SCsub b/modules/SCsub
index 5ff4623743..fcc01e2c1b 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -22,36 +22,45 @@ env.CommandNoCache(
),
)
-# Header to be included in `tests/test_main.cpp` to run module-specific tests.
-if env["tests"]:
- env.Depends("modules_tests.gen.h", Value(env.module_list))
- env.CommandNoCache(
- "modules_tests.gen.h",
- Value(env.module_list),
- env.Run(
- modules_builders.generate_modules_tests,
- "Generating modules tests header.",
- # NOTE: No need to run in subprocess since this is still executed serially.
- subprocess=False,
- ),
- )
- env.AlwaysBuild("modules_tests.gen.h")
vs_sources = []
+test_headers = []
# libmodule_<name>.a for each active module.
for name, path in env.module_list.items():
env.modules_sources = []
- if not os.path.isabs(path):
- SConscript(name + "/SCsub") # Built-in.
- else:
- SConscript(path + "/SCsub") # Custom.
+ # Name for built-in modules, (absolute) path for custom ones.
+ base_path = path if os.path.isabs(path) else name
+ SConscript(base_path + "/SCsub")
lib = env_modules.add_library("module_%s" % name, env.modules_sources)
env.Prepend(LIBS=[lib])
if env["vsproj"]:
vs_sources += env.modules_sources
+ if env["tests"]:
+ # Lookup potential headers in `tests` subfolder.
+ import glob
+
+ module_tests = sorted(glob.glob(os.path.join(base_path, "tests", "*.h")))
+ if module_tests != []:
+ test_headers += module_tests
+
+
+# Generate header to be included in `tests/test_main.cpp` to run module-specific tests.
+if env["tests"]:
+ env.Depends("modules_tests.gen.h", test_headers)
+ env.CommandNoCache(
+ "modules_tests.gen.h",
+ test_headers,
+ env.Run(
+ modules_builders.generate_modules_tests,
+ "Generating modules tests header.",
+ # NOTE: No need to run in subprocess since this is still executed serially.
+ subprocess=False,
+ ),
+ )
+
# libmodules.a with only register_module_types.
# Must be last so that all libmodule_<name>.a libraries are on the right side
# in the linker command.
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 6377f8915d..dffcadbcdc 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -105,7 +105,7 @@ private:
};
class SphereShapeBullet : public ShapeBullet {
- real_t radius;
+ real_t radius = 0.0;
public:
SphereShapeBullet();
@@ -137,8 +137,8 @@ private:
};
class CapsuleShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CapsuleShapeBullet();
@@ -155,8 +155,8 @@ private:
};
class CylinderShapeBullet : public ShapeBullet {
- real_t height;
- real_t radius;
+ real_t height = 0.0;
+ real_t radius = 0.0;
public:
CylinderShapeBullet();
diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_osx.h
index 9d67235839..b0db844599 100644
--- a/modules/camera/camera_osx.h
+++ b/modules/camera/camera_osx.h
@@ -39,7 +39,6 @@
class CameraOSX : public CameraServer {
public:
CameraOSX();
- ~CameraOSX();
void update_feeds();
};
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm
index 391006bfc2..d199c31b2f 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_osx.mm
@@ -114,18 +114,12 @@
if (output) {
[self removeOutput:output];
[output setSampleBufferDelegate:nil queue:nullptr];
- [output release];
output = nullptr;
}
[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..
@@ -208,7 +202,6 @@ public:
AVCaptureDevice *get_device() const;
CameraFeedOSX();
- ~CameraFeedOSX();
void set_device(AVCaptureDevice *p_device);
@@ -227,7 +220,6 @@ CameraFeedOSX::CameraFeedOSX() {
void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
device = p_device;
- [device retain];
// get some info
NSString *device_name = p_device.localizedName;
@@ -240,18 +232,6 @@ void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
};
};
-CameraFeedOSX::~CameraFeedOSX() {
- if (capture_session != nullptr) {
- [capture_session release];
- capture_session = nullptr;
- };
-
- if (device != nullptr) {
- [device release];
- device = nullptr;
- };
-};
-
bool CameraFeedOSX::activate_feed() {
if (capture_session) {
// Already recording!
@@ -282,7 +262,6 @@ void CameraFeedOSX::deactivate_feed() {
// end camera capture if we have one
if (capture_session) {
[capture_session cleanup];
- [capture_session release];
capture_session = nullptr;
};
};
@@ -317,8 +296,6 @@ void CameraFeedOSX::deactivate_feed() {
// remove notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
-
- [super dealloc];
}
@end
@@ -376,7 +353,3 @@ CameraOSX::CameraOSX() {
// should only have one of these....
device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
};
-
-CameraOSX::~CameraOSX() {
- [device_notifications release];
-};
diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml
index 4b479ed42e..64b07a6cbb 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a box for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGCombiner3D.xml b/modules/csg/doc_classes/CSGCombiner3D.xml
index 422c5d35b7..16903141de 100644
--- a/modules/csg/doc_classes/CSGCombiner3D.xml
+++ b/modules/csg/doc_classes/CSGCombiner3D.xml
@@ -5,7 +5,9 @@
</brief_description>
<description>
For complex arrangements of shapes, it is sometimes needed to add structure to your CSG nodes. The CSGCombiner3D node allows you to create this structure. The node encapsulates the result of the CSG operations of its children. In this way, it is possible to do operations on one set of shapes that are children of one CSGCombiner3D node, and a set of separate operations on a second set of shapes that are children of a second CSGCombiner3D node, and then do an operation that takes the two end results as its input to create the final shape.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
</class>
diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml
index 1fe2025bab..fa61957c84 100644
--- a/modules/csg/doc_classes/CSGCylinder3D.xml
+++ b/modules/csg/doc_classes/CSGCylinder3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a cylinder (or cone) for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 42fcb7bd2b..660659a98b 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces. See also [CSGPolygon3D] for drawing 2D extruded polygons to be used as CSG nodes.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index 5a49eebc7b..e8ef79a9e5 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
An array of 2D points is extruded to quickly and easily create a variety of 3D meshes. See also [CSGMesh3D] for using 3D meshes as CSG nodes.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
diff --git a/modules/csg/doc_classes/CSGPrimitive3D.xml b/modules/csg/doc_classes/CSGPrimitive3D.xml
index 8f4c8b9451..7eac85368e 100644
--- a/modules/csg/doc_classes/CSGPrimitive3D.xml
+++ b/modules/csg/doc_classes/CSGPrimitive3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
Parent class for various CSG primitives. It contains code and functionality that is common between them. It cannot be used directly. Instead use one of the various classes that inherit from it.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces" default="false">
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index f5031064d6..b2513703c0 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<methods>
<method name="get_collision_layer_value" qualifiers="const">
diff --git a/modules/csg/doc_classes/CSGSphere3D.xml b/modules/csg/doc_classes/CSGSphere3D.xml
index b8dfb4cf5f..1ef20bc7ca 100644
--- a/modules/csg/doc_classes/CSGSphere3D.xml
+++ b/modules/csg/doc_classes/CSGSphere3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a sphere for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml
index 2c0eef8f09..09256af109 100644
--- a/modules/csg/doc_classes/CSGTorus3D.xml
+++ b/modules/csg/doc_classes/CSGTorus3D.xml
@@ -5,8 +5,10 @@
</brief_description>
<description>
This node allows you to create a torus for use with the CSG system.
+ [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
</description>
<tutorials>
+ <link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="0.5">
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index e56177d6e9..1d5a7ff6a3 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -11,7 +11,16 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/cvtt/"
thirdparty_sources = [
- "ConvectionKernels.cpp",
+ "ConvectionKernels_API.cpp",
+ "ConvectionKernels_ETC.cpp",
+ "ConvectionKernels_BC67.cpp",
+ "ConvectionKernels_IndexSelector.cpp",
+ "ConvectionKernels_BC6H_IO.cpp",
+ "ConvectionKernels_S3TC.cpp",
+ "ConvectionKernels_BC7_PrioData.cpp",
+ "ConvectionKernels_SingleFile.cpp",
+ "ConvectionKernels_BCCommon.cpp",
+ "ConvectionKernels_Util.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 9e0579740b..d18340a2c8 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -41,7 +41,7 @@ struct CVTTCompressionJobParams {
bool is_hdr = false;
bool is_signed = false;
int bytes_per_pixel = 0;
-
+ cvtt::BC7EncodingPlan bc7_plan;
cvtt::Options options;
};
@@ -116,7 +116,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
cvtt::Kernels::EncodeBC6HU(output_blocks, input_blocks_hdr, p_job_params.options);
}
} else {
- cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options);
+ cvtt::Kernels::EncodeBC7(output_blocks, input_blocks_ldr, p_job_params.options, p_job_params.bc7_plan);
}
unsigned int num_real_blocks = ((w - x_start) + 3) / 4;
@@ -141,7 +141,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
if (p_image->get_format() >= Image::FORMAT_BPTC_RGBA) {
return; //do not compress, already compressed
}
-
int w = p_image->get_width();
int h = p_image->get_height();
@@ -153,22 +152,8 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
}
cvtt::Options options;
- uint32_t flags = cvtt::Flags::Fastest;
-
- if (p_lossy_quality > 0.85) {
- flags = cvtt::Flags::Ultra;
- } else if (p_lossy_quality > 0.75) {
- flags = cvtt::Flags::Better;
- } else if (p_lossy_quality > 0.55) {
- flags = cvtt::Flags::Default;
- } else if (p_lossy_quality > 0.35) {
- flags = cvtt::Flags::Fast;
- } else if (p_lossy_quality > 0.15) {
- flags = cvtt::Flags::Faster;
- }
-
+ uint32_t flags = cvtt::Flags::Default;
flags |= cvtt::Flags::BC7_RespectPunchThrough;
-
if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normal map
flags |= cvtt::Flags::Uniform;
}
@@ -215,12 +200,15 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
job_queue.job_params.is_signed = is_signed;
job_queue.job_params.options = options;
job_queue.job_params.bytes_per_pixel = is_hdr ? 6 : 4;
+ cvtt::Kernels::ConfigureBC7EncodingPlanFromQuality(job_queue.job_params.bc7_plan, 5);
-#ifdef NO_THREADS
int num_job_threads = 0;
-#else
- int num_job_threads = OS::get_singleton()->can_use_threads() ? (OS::get_singleton()->get_processor_count() - 1) : 0;
-#endif
+ // Amdahl's law (Wikipedia)
+ // If a program needs 20 hours to complete using a single thread, but a one-hour portion of the program cannot be parallelized,
+ // therefore only the remaining 19 hours (p = 0.95) of execution time can be parallelized, then regardless of how many threads are devoted
+ // to a parallelized execution of this program, the minimum execution time cannot be less than one hour.
+ //
+ // The number of executions with different inputs can be increased while the latency is the same.
Vector<CVTTCompressionRowTask> tasks;
@@ -278,7 +266,6 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
memdelete(threads_wb[i]);
}
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
@@ -388,6 +375,5 @@ void image_decompress_cvtt(Image *p_image) {
w >>= 1;
h >>= 1;
}
-
p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
}
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index 4cca907bf2..758c47eecc 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -123,7 +123,7 @@ Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_
bool corrupt = false;
// safer to check this way as there can be different formatted headers
- if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
+ if (fbx_header_string.contains("Kaydara FBX Binary")) {
is_binary = true;
print_verbose("[doc] is binary");
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index bb6f0324a8..0c49694e0b 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -32,7 +32,7 @@
#include "core/variant/variant.h"
-#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "core/math/vector3i.h"
static_assert(sizeof(godot_packed_byte_array) == sizeof(PackedByteArray), "PackedByteArray size mismatch");
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index f4674850e3..7e0ce76c26 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/rect2.h"
#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_rect2) == sizeof(Rect2), "Rect2 size mismatch");
static_assert(sizeof(godot_rect2i) == sizeof(Rect2i), "Rect2i size mismatch");
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index 45ba790dc1..7dc07024e5 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -31,6 +31,7 @@
#include "gdnative/transform2d.h"
#include "core/math/transform_2d.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index eb8ffd74cd..a8d4281d25 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -31,6 +31,8 @@
#include "gdnative/vector2.h"
#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
+#include "core/os/memory.h"
static_assert(sizeof(godot_vector2) == sizeof(Vector2), "Vector2 size mismatch");
static_assert(sizeof(godot_vector2i) == sizeof(Vector2i), "Vector2i size mismatch");
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index f94464826e..33ea078696 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -315,7 +315,6 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
NativePlatformConfig platform_ios;
platform_ios.name = "iOS";
- platform_ios.entries.push_back("armv7");
platform_ios.entries.push_back("arm64");
platform_ios.entries.push_back("x86_64");
// iOS can use both Static and Dynamic libraries.
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index ddde28811c..0309d1d9c7 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -288,7 +288,7 @@ List<ClassAPI> generate_c_api_classes() {
String type;
String name = argument.name;
- if (argument.name.find(":") != -1) {
+ if (argument.name.contains(":")) {
type = argument.name.get_slice(":", 1);
name = argument.name.get_slice(":", 0);
} else {
@@ -324,7 +324,7 @@ List<ClassAPI> generate_c_api_classes() {
property_api.getter = ClassDB::get_property_getter(class_name, p->get().name);
property_api.setter = ClassDB::get_property_setter(class_name, p->get().name);
- if (p->get().name.find(":") != -1) {
+ if (p->get().name.contains(":")) {
property_api.type = p->get().name.get_slice(":", 1);
property_api.name = p->get().name.get_slice(":", 0);
} else {
@@ -355,7 +355,7 @@ List<ClassAPI> generate_c_api_classes() {
//method name
method_api.method_name = method_info.name;
//method return type
- if (method_api.method_name.find(":") != -1) {
+ if (method_api.method_name.contains(":")) {
method_api.return_type = method_api.method_name.get_slice(":", 1);
method_api.method_name = method_api.method_name.get_slice(":", 0);
} else {
@@ -388,7 +388,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_name = arg_info.name;
- if (arg_info.name.find(":") != -1) {
+ if (arg_info.name.contains(":")) {
arg_type = arg_info.name.get_slice(":", 1);
arg_name = arg_info.name.get_slice(":", 0);
} else if (arg_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index be304a43f0..5d5414c694 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -766,7 +766,7 @@ Variant NativeScriptInstance::call(const StringName &p_method, const Variant **p
void NativeScriptInstance::notification(int p_notification) {
#ifdef DEBUG_ENABLED
if (p_notification == MainLoop::NOTIFICATION_CRASH) {
- if (current_method_call != StringName("")) {
+ if (current_method_call != StringName()) {
ERR_PRINT("NativeScriptInstance detected crash on method: " + current_method_call);
current_method_call = "";
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 71ab8ef0a2..ec3c9eb4ff 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -93,7 +93,7 @@ Variant PluginScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
REF ref;
Object *owner = nullptr;
- if (get_instance_base_type() == "") {
+ if (get_instance_base_type() == StringName()) {
owner = memnew(RefCounted);
} else {
owner = ClassDB::instantiate(get_instance_base_type());
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 5cc295bbab..ac6684a29c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -33,18 +33,6 @@
#include "../gdscript_tokenizer.h"
#include "editor/editor_settings.h"
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool _is_hex_symbol(char32_t c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static bool _is_bin_symbol(char32_t c) {
- return (c == '0' || c == '1');
-}
-
Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
@@ -102,7 +90,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = (str[j] >= '0' && str[j] <= '9');
+ bool is_number = is_digit(str[j]);
/* color regions */
if (is_a_symbol || in_region != -1) {
@@ -241,14 +229,14 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
// allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
}
// disallow anything not a 0 or 1
- if (is_bin_notation && (_is_bin_symbol(str[j]))) {
+ if (is_bin_notation && (is_binary_digit(str[j]))) {
is_number = true;
} else if (is_bin_notation) {
is_bin_notation = false;
@@ -270,7 +258,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
}
}
- if (!in_word && _is_char(str[j]) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
in_word = true;
}
@@ -585,7 +573,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
- if (name.find("/") != -1) {
+ if (name.contains("/")) {
continue;
}
member_keywords[name] = member_variable_color;
diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
index 0824d894c5..edaccae018 100644
--- a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
@@ -3,7 +3,7 @@
extends _BASE_
const SPEED: float = 300.0
-const JUMP_FORCE: float = -400.0
+const JUMP_VELOCITY: float = -400.0
# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
@@ -16,7 +16,7 @@ func _physics_process(delta: float) -> void:
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_FORCE
+ motion_velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
index ce6d67ae84..e191e5451a 100644
--- a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
@@ -3,7 +3,7 @@
extends _BASE_
const SPEED: float = 5.0
-const JUMP_FORCE: float = 4.5
+const JUMP_VELOCITY: float = 4.5
# Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
@@ -16,7 +16,7 @@ func _physics_process(delta: float) -> void:
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_FORCE
+ motion_velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd b/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
index 27383b878d..d71f2592fe 100644
--- a/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
+++ b/modules/gdscript/editor_templates/VisualShaderNodeCustom/basic.gd
@@ -34,8 +34,5 @@ func _get_output_port_name(port: int) -> String:
func _get_output_port_type(port: int) -> int:
return PORT_TYPE_SCALAR
-func _get_global_code(mode: Shader.Mode) -> String:
- return ""
-
func _get_code(input_vars: Array[String], output_vars: Array[String], mode: Shader.Mode, type: VisualShader.Type) -> String:
return output_vars[0] + " = 0.0;"
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 84db97625b..58a788e255 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -43,6 +43,7 @@
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
+#include "gdscript_rpc_callable.h"
#include "gdscript_warning.h"
#ifdef TESTS_ENABLED
@@ -827,7 +828,7 @@ Error GDScript::reload(bool p_keep_state) {
return OK;
}
#else
- if (source.find("_BASE_") != -1) {
+ if (source.contains("_BASE_")) {
return OK;
}
#endif
@@ -1375,7 +1376,13 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
while (sl) {
const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name);
if (E) {
- r_ret = Callable(this->owner, E->key());
+ Multiplayer::RPCConfig config;
+ config.name = p_name;
+ if (sptr->rpc_functions.find(config) != -1) {
+ r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key())));
+ } else {
+ r_ret = Callable(this->owner, E->key());
+ }
return true; //index found
}
sl = sl->_base;
@@ -2022,8 +2029,6 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"preload",
"signal",
"super",
- "trait",
- "yield",
// var
"const",
"enum",
@@ -2040,6 +2045,11 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
"return",
"match",
"while",
+ // These keywords are not implemented currently, but reserved for (potential) future use.
+ // We highlight them as keywords to make errors easier to understand.
+ "trait",
+ "namespace",
+ "yield",
nullptr
};
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index d11174227a..94daba4bf6 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::ENUM;
- type.builtin_type = Variant::OBJECT;
+ type.builtin_type = Variant::INT;
type.is_constant = true;
type.is_meta_type = true;
@@ -196,7 +196,7 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
}
if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) {
- if (current_data_type->native_type != StringName("")) {
+ if (current_data_type->native_type != StringName()) {
return check_native_member_name_conflict(
p_member_name,
p_member_node,
@@ -250,7 +250,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "RefCounted";
+ result.native_type = SNAME("RefCounted");
} else {
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -438,7 +438,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name;
- if (first == "Variant") {
+ if (first == SNAME("Variant")) {
result.kind = GDScriptParser::DataType::VARIANT;
if (p_type->type_chain.size() > 1) {
push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]);
@@ -447,9 +447,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return result;
}
- if (first == "Object") {
+ if (first == SNAME("Object")) {
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Object";
+ result.native_type = SNAME("Object");
if (p_type->type_chain.size() > 1) {
push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]);
return GDScriptParser::DataType();
@@ -650,9 +650,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype = specified_type;
if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true)) {
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
} else {
// TODO: Add warning.
@@ -1400,9 +1400,9 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
- if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) {
+ if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) {
+ if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
} else {
// TODO: Add warning.
@@ -1877,11 +1877,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
if (compatible) {
- compatible = is_type_compatible(assignee_type, op_type, true);
+ compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value);
if (!compatible) {
if (assignee_type.is_hard_type()) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, assignee_type, true)) {
+ if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
// TODO: Add warning.
@@ -2416,6 +2416,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
+ if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) {
+ // Enum type is treated as a dictionary value for function calls.
+ base_type.is_meta_type = false;
+ }
+
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
} else if (!is_self && base_type.is_meta_type && !is_static) {
@@ -2474,17 +2479,24 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type);
if (!cast_type.is_set()) {
+ mark_node_unsafe(p_cast);
return;
}
- cast_type.is_meta_type = false; // The casted value won't be a type name.
+ cast_type = type_from_metatype(cast_type); // The casted value won't be a type name.
p_cast->set_datatype(cast_type);
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (!op_type.is_variant()) {
bool valid = false;
- if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
+ if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are compatible between each other, so it's a safe cast.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Convertint int to enum is always valid.
+ valid = true;
+ } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
@@ -2540,7 +2552,7 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Node";
+ result.native_type = SNAME("Node");
result.builtin_type = Variant::OBJECT;
if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
@@ -2586,6 +2598,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &name = p_identifier->name;
+ if (base.kind == GDScriptParser::DataType::ENUM) {
+ if (base.is_meta_type) {
+ if (base.enum_values.has(name)) {
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = base.enum_values[name];
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ result.kind = GDScriptParser::DataType::ENUM;
+ result.is_constant = true;
+ result.builtin_type = Variant::INT;
+ result.native_type = base.native_type;
+ result.enum_type = base.enum_type;
+ p_identifier->set_datatype(result);
+ return;
+ } else {
+ // Consider as a Dictionary, so it can be anything.
+ // This will be evaluated in the next if block.
+ base.kind = GDScriptParser::DataType::BUILTIN;
+ base.builtin_type = Variant::DICTIONARY;
+ base.is_meta_type = false;
+ }
+ } else {
+ push_error(R"(Cannot get property from enum value.)", p_identifier);
+ return;
+ }
+ }
+
if (base.kind == GDScriptParser::DataType::BUILTIN) {
if (base.is_meta_type) {
bool valid = true;
@@ -2632,32 +2672,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
- if (base.kind == GDScriptParser::DataType::ENUM) {
- if (base.is_meta_type) {
- if (base.enum_values.has(name)) {
- p_identifier->is_constant = true;
- p_identifier->reduced_value = base.enum_values[name];
-
- GDScriptParser::DataType result;
- result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- result.kind = GDScriptParser::DataType::ENUM_VALUE;
- result.is_constant = true;
- result.builtin_type = Variant::INT;
- result.native_type = base.native_type;
- result.enum_type = name;
- p_identifier->set_datatype(result);
- } else {
- // Consider as a Dictionary
- GDScriptParser::DataType dummy;
- dummy.kind = GDScriptParser::DataType::VARIANT;
- p_identifier->set_datatype(dummy);
- }
- } else {
- push_error(R"(Cannot get property from enum value.)", p_identifier);
- }
- return;
- }
-
GDScriptParser::ClassNode *base_class = base.class_type;
// TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls).
@@ -2793,7 +2807,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (element.identifier->name == p_identifier->name) {
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN;
+ type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN;
type.builtin_type = Variant::INT;
type.is_constant = true;
if (element.parent_enum->identifier) {
@@ -3493,6 +3507,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars
GDScriptParser::DataType result = p_meta_type;
result.is_meta_type = false;
result.is_constant = false;
+ if (p_meta_type.kind == GDScriptParser::DataType::ENUM) {
+ result.builtin_type = Variant::INT;
+ }
return result;
}
@@ -3507,7 +3524,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.builtin_type = p_property.type;
if (p_property.type == Variant::OBJECT) {
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
+ result.native_type = p_property.class_name == StringName() ? SNAME("Object") : p_property.class_name;
} else {
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = p_property.type;
@@ -3549,6 +3566,18 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source
r_default_arg_count = 0;
StringName function_name = p_function;
+ if (p_base_type.kind == GDScriptParser::DataType::ENUM) {
+ if (p_base_type.is_meta_type) {
+ // Enum type can be treated as a dictionary value.
+ p_base_type.kind = GDScriptParser::DataType::BUILTIN;
+ p_base_type.builtin_type = Variant::DICTIONARY;
+ p_base_type.is_meta_type = false;
+ } else {
+ push_error("Cannot call function on enum value.", p_source);
+ return false;
+ }
+ }
+
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
@@ -3799,6 +3828,22 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
+
+ if (p_a.kind == GDScriptParser::DataType::ENUM) {
+ if (p_a.is_meta_type) {
+ a_type = Variant::DICTIONARY;
+ } else {
+ a_type = Variant::INT;
+ }
+ }
+ if (p_b.kind == GDScriptParser::DataType::ENUM) {
+ if (p_b.is_meta_type) {
+ b_type = Variant::DICTIONARY;
+ } else {
+ b_type = Variant::INT;
+ }
+ }
+
Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type();
@@ -3828,7 +3873,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
}
// TODO: Add safe/unsafe return variable (for variant cases)
-bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const {
+bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) {
// These return "true" so it doesn't affect users negatively.
ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type");
ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type");
@@ -3848,7 +3893,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type);
}
- if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM && !p_source.is_meta_type) {
// Enum value is also integer.
valid = true;
}
@@ -3869,6 +3914,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_target.kind == GDScriptParser::DataType::ENUM) {
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
+#ifdef DEBUG_ENABLED
+ if (p_source_node) {
+ parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM);
+ }
+#endif
return true;
}
if (p_source.kind == GDScriptParser::DataType::ENUM) {
@@ -3876,11 +3926,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
return true;
}
}
- if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
- if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
- return true;
- }
- }
return false;
}
@@ -3935,7 +3980,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -3972,7 +4016,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 4cee5cb44a..2697a6ec2b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -112,7 +112,7 @@ class GDScriptAnalyzer {
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
- bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
+ bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
bool class_exists(const StringName &p_class) const;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index ca125d3a07..108c988add 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -141,10 +141,13 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
}
} break;
case GDScriptParser::DataType::ENUM:
- case GDScriptParser::DataType::ENUM_VALUE:
result.has_type = true;
result.kind = GDScriptDataType::BUILTIN;
- result.builtin_type = Variant::INT;
+ if (p_datatype.is_meta_type) {
+ result.builtin_type = Variant::DICTIONARY;
+ } else {
+ result.builtin_type = Variant::INT;
+ }
break;
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
@@ -469,7 +472,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} break;
case GDScriptParser::Node::CAST: {
const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
- GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type->get_datatype());
+ GDScriptParser::DataType og_cast_type = cn->cast_type->get_datatype();
+ GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type);
+
+ if (og_cast_type.kind == GDScriptParser::DataType::ENUM) {
+ // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer.
+ cast_type.kind = GDScriptDataType::BUILTIN;
+ cast_type.builtin_type = Variant::INT;
+ }
// Create temporary for result first since it will be deleted last.
GDScriptCodeGenerator::Address result = codegen.add_temporary(cast_type);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index d10e120410..f0dc830ed8 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -486,7 +486,7 @@ struct GDScriptCompletionIdentifier {
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
- if (enum_name.find(".") == -1) {
+ if (!enum_name.contains(".")) {
return enum_name;
}
return enum_name.get_slice(".", 1);
@@ -610,7 +610,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
case GDScriptParser::Node::SUBSCRIPT: {
const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
if (sub->is_constant) {
- if (sub->datatype.kind == GDScriptParser::DataType::ENUM_VALUE) {
+ if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
def_val = sub->get_datatype().to_string();
} else if (sub->reduced) {
const Variant::Type vt = sub->reduced_value.get_type();
@@ -652,7 +652,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String
}
static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_annotation->name == "@export_range") {
+ if (p_annotation->name == SNAME("@export_range")) {
if (p_argument == 3 || p_argument == 4) {
// Slider hint.
ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -662,7 +662,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
slider2.insert_text = slider2.display.quote(p_quote_style);
r_result.insert(slider2.display, slider2);
}
- } else if (p_annotation->name == "@export_exp_easing") {
+ } else if (p_annotation->name == SNAME("@export_exp_easing")) {
if (p_argument == 0 || p_argument == 1) {
// Easing hint.
ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -672,7 +672,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
hint2.insert_text = hint2.display.quote(p_quote_style);
r_result.insert(hint2.display, hint2);
}
- } else if (p_annotation->name == "@export_node_path") {
+ } else if (p_annotation->name == SNAME("@export_node_path")) {
ScriptCodeCompletionOption node("Node", ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(node.display, node);
List<StringName> node_types;
@@ -684,7 +684,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
- } else if (p_annotation->name == "@warning_ignore") {
+ } else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptCodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
r_result.insert(warning.display, warning);
@@ -955,7 +955,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
continue;
}
- if (E.name.find("/") != -1) {
+ if (E.name.contains("/")) {
continue;
}
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
@@ -1000,7 +1000,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
for (const PropertyInfo &E : members) {
- if (String(E.name).find("/") == -1) {
+ if (!String(E.name).contains("/")) {
ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
@@ -1384,7 +1384,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
Object *baseptr = base.value;
- if (all_is_const && String(call->function_name) == "get_node" && ClassDB::is_parent_class(native_type.native_type, "Node") && args.size()) {
+ if (all_is_const && call->function_name == SNAME("get_node") && ClassDB::is_parent_class(native_type.native_type, SNAME("Node")) && args.size()) {
String arg1 = args[0];
if (arg1.begins_with("/root/")) {
String which = arg1.get_slice("/", 2);
@@ -2085,7 +2085,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
GDScriptParser::DataType base_type = p_base.type;
bool is_static = base_type.is_meta_type;
- if (is_static && p_method == "new") {
+ if (is_static && p_method == SNAME("new")) {
r_type.type = base_type;
r_type.type.is_meta_type = false;
r_type.type.is_constant = false;
@@ -2182,7 +2182,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
}
static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_enum_hint.find(".") == -1) {
+ if (!p_enum_hint.contains(".")) {
// Global constant or in the current class.
StringName current_enum = p_enum_hint;
if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {
@@ -2274,7 +2274,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_arghint = _make_arguments_hint(info, p_argidx);
}
- if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) {
+ if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (p_method == SNAME("get_node") || p_method == SNAME("has_node"))) {
// Get autoloads
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2291,7 +2291,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
- if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, "InputEvent") && p_method.operator String().find("action") != -1) {
+ if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && p_method.operator String().contains("action")) {
// Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2639,7 +2639,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
ClassDB::get_virtual_methods(class_name, &virtual_methods);
for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
- if (method_hint.find(":") != -1) {
+ if (method_hint.contains(":")) {
method_hint = method_hint.get_slice(":", 0);
}
method_hint += "(";
@@ -2650,7 +2650,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
method_hint += ", ";
}
String arg = mi.arguments[i].name;
- if (arg.find(":") != -1) {
+ if (arg.contains(":")) {
arg = arg.substr(0, arg.find(":"));
}
method_hint += arg;
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 460bd85a86..8e4e457ec1 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -519,7 +519,7 @@ void GDScriptParser::parse_program() {
// Check for @tool annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@tool") {
+ if (annotation->name == SNAME("@tool")) {
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
_is_tool = true;
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
@@ -573,7 +573,7 @@ void GDScriptParser::parse_program() {
// Check for @icon annotation.
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == "@icon") {
+ if (annotation->name == SNAME("@icon")) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
push_error(R"(Expected newline after "@icon" annotation.)");
}
@@ -3503,7 +3503,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
- if (p_annotation->name == "@export") {
+ if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
return false;
@@ -3528,7 +3528,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
break;
case GDScriptParser::DataType::NATIVE:
- if (ClassDB::is_parent_class(export_type.native_type, "Resource")) {
+ if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.native_type;
@@ -3740,8 +3740,6 @@ String GDScriptParser::DataType::to_string() const {
}
case ENUM:
return enum_type.operator String() + " (enum)";
- case ENUM_VALUE:
- return enum_type.operator String() + " (enum value)";
case UNRESOLVED:
return "<unresolved type>";
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index e4311d2d5e..c09b07282f 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -106,8 +106,7 @@ public:
NATIVE,
SCRIPT,
CLASS, // GDScript.
- ENUM, // Full enumeration.
- ENUM_VALUE, // Value from enumeration.
+ ENUM, // Enumeration.
VARIANT, // Can be any type.
UNRESOLVED,
};
@@ -185,8 +184,6 @@ public:
return builtin_type == p_other.builtin_type;
case NATIVE:
case ENUM:
- return native_type == p_other.native_type;
- case ENUM_VALUE:
return native_type == p_other.native_type && enum_type == p_other.enum_type;
case SCRIPT:
return script_type == p_other.script_type;
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
new file mode 100644
index 0000000000..07e5ed4171
--- /dev/null
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -0,0 +1,86 @@
+/*************************************************************************/
+/* gdscript_rpc_callable.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "gdscript_rpc_callable.h"
+
+#include "core/templates/hashfuncs.h"
+#include "scene/main/node.h"
+
+bool GDScriptRPCCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() == p_b->hash();
+}
+
+bool GDScriptRPCCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() < p_b->hash();
+}
+
+uint32_t GDScriptRPCCallable::hash() const {
+ return h;
+}
+
+String GDScriptRPCCallable::get_as_text() const {
+ String class_name = object->get_class();
+ Ref<Script> script = object->get_script();
+ return class_name + "(" + script->get_path().get_file() + ")::" + String(method) + " (rpc)";
+}
+
+CallableCustom::CompareEqualFunc GDScriptRPCCallable::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc GDScriptRPCCallable::get_compare_less_func() const {
+ return compare_less;
+}
+
+ObjectID GDScriptRPCCallable::get_object() const {
+ return object->get_instance_id();
+}
+
+void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ r_return_value = object->call(method, p_arguments, p_argcount, r_call_error);
+}
+
+GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) {
+ object = p_object;
+ method = p_method;
+ h = method.hash();
+ h = hash_djb2_one_64(object->get_instance_id(), h);
+ node = Object::cast_to<Node>(object);
+ ERR_FAIL_COND_MSG(!node, "RPC can only be defined on class that extends Node.");
+}
+
+void GDScriptRPCCallable::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
+ if (unlikely(!node)) {
+ r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+ r_call_error.error = Callable::CallError::CALL_OK;
+ node->rpcp(p_peer_id, method, p_arguments, p_argcount);
+}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
new file mode 100644
index 0000000000..2c8734a74b
--- /dev/null
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* gdscript_rpc_callable.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GDSCRIPT_RPC_CALLABLE
+#define GDSCRIPT_RPC_CALLABLE
+
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+class Node;
+
+class GDScriptRPCCallable : public CallableCustom {
+ Object *object = nullptr;
+ Node *node = nullptr;
+ StringName method;
+ uint32_t h = 0;
+
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ uint32_t hash() const override;
+ String get_as_text() const override;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+ ObjectID get_object() const override;
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+ void rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
+
+ GDScriptRPCCallable(Object *p_object, const StringName &p_method);
+ virtual ~GDScriptRPCCallable() = default;
+};
+
+#endif // GDSCRIPT_RPC_CALLABLE
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 9977b88aa1..d3287ab345 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -312,22 +312,6 @@ GDScriptTokenizer::Token GDScriptTokenizer::pop_error() {
return error;
}
-static bool _is_alphanumeric(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
-static bool _is_digit(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_digit(char32_t c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
-static bool _is_binary_digit(char32_t c) {
- return (c == '0' || c == '1');
-}
-
GDScriptTokenizer::Token GDScriptTokenizer::make_token(Token::Type p_type) {
Token token(p_type);
token.start_line = start_line;
@@ -448,10 +432,10 @@ GDScriptTokenizer::Token GDScriptTokenizer::check_vcs_marker(char32_t p_test, To
}
GDScriptTokenizer::Token GDScriptTokenizer::annotation() {
- if (!_is_alphanumeric(_peek())) {
+ if (!is_ascii_identifier_char(_peek())) {
push_error("Expected annotation identifier after \"@\".");
}
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
// Consume all identifier characters.
_advance();
}
@@ -526,7 +510,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::potential_identifier() {
#define MAX_KEYWORD_LENGTH 10
// Consume all alphanumeric characters.
- while (_is_alphanumeric(_peek())) {
+ while (is_ascii_identifier_char(_peek())) {
_advance();
}
@@ -612,7 +596,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
bool has_decimal = false;
bool has_exponent = false;
bool has_error = false;
- bool (*digit_check_func)(char32_t) = _is_digit;
+ bool (*digit_check_func)(char32_t) = is_digit;
if (_peek(-1) == '.') {
has_decimal = true;
@@ -620,20 +604,20 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
if (_peek() == 'x') {
// Hexadecimal.
base = 16;
- digit_check_func = _is_hex_digit;
+ digit_check_func = is_hex_digit;
_advance();
} else if (_peek() == 'b') {
// Binary.
base = 2;
- digit_check_func = _is_binary_digit;
+ digit_check_func = is_binary_digit;
_advance();
}
}
// Allow '_' to be used in a number, for readability.
bool previous_was_underscore = false;
- while (digit_check_func(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (digit_check_func(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -682,7 +666,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
// Consume decimal digits.
- while (_is_digit(_peek()) || _peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
_advance();
}
}
@@ -696,7 +680,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
_advance();
}
// Consume exponent digits.
- if (!_is_digit(_peek())) {
+ if (!is_digit(_peek())) {
Token error = make_error(R"(Expected exponent value after "e".)");
error.start_column = column;
error.leftmost_column = column;
@@ -705,8 +689,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = false;
- while (_is_digit(_peek()) || _peek() == '_') {
- if (_peek() == '_') {
+ while (is_digit(_peek()) || is_underscore(_peek())) {
+ if (is_underscore(_peek())) {
if (previous_was_underscore) {
Token error = make_error(R"(Only one underscore can be used as a numeric separator.)");
error.start_column = column;
@@ -733,7 +717,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
error.rightmost_column = column + 1;
push_error(error);
has_error = true;
- } else if (_is_alphanumeric(_peek())) {
+ } else if (is_ascii_identifier_char(_peek())) {
// Letter at the end of the number.
push_error("Invalid numeric notation.");
}
@@ -865,7 +849,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
char32_t digit = _peek();
char32_t value = 0;
- if (digit >= '0' && digit <= '9') {
+ if (is_digit(digit)) {
value = digit - '0';
} else if (digit >= 'a' && digit <= 'f') {
value = digit - 'a';
@@ -1322,9 +1306,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
line_continuation = false;
- if (_is_digit(c)) {
+ if (is_digit(c)) {
return number();
- } else if (_is_alphanumeric(c)) {
+ } else if (is_ascii_identifier_char(c)) {
return potential_identifier();
}
@@ -1392,7 +1376,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (_peek() == '.') {
_advance();
return make_token(Token::PERIOD_PERIOD);
- } else if (_is_digit(_peek())) {
+ } else if (is_digit(_peek())) {
// Number starting with '.'.
return number();
} else {
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 73536f5f8e..ad96e36640 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -152,6 +152,9 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(3);
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
}
+ case INT_ASSIGNED_TO_ENUM: {
+ return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -199,6 +202,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"REDUNDANT_AWAIT",
"EMPTY_FILE",
"SHADOWED_GLOBAL_IDENTIFIER",
+ "INT_ASSIGNED_TO_ENUM",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 112b40781a..82efe3568f 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -70,6 +70,7 @@ public:
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
EMPTY_FILE, // A script file is empty.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
+ INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
WARNING_MAX,
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 49f5303ae6..17886181d5 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -541,7 +541,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
for (int c = p_position.character; c >= 0; c--) {
start_pos = c;
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
@@ -550,7 +550,7 @@ String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &
int end_pos = p_position.character;
for (int c = p_position.character; c < line.length(); c++) {
char32_t ch = line[c];
- bool valid_char = (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_';
+ bool valid_char = is_ascii_identifier_char(ch);
if (!valid_char) {
break;
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index ed879b088a..d20b243616 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -63,7 +63,7 @@ void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStr
String function_signature = "func " + function;
String source = script->get_source_code();
- if (source.find(function_signature) != -1) {
+ if (source.contains(function_signature)) {
return;
}
@@ -380,7 +380,7 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
- if (data.qualifiers.find("vararg") != -1) {
+ if (data.qualifiers.contains("vararg")) {
params += params.is_empty() ? "..." : ", ...";
}
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 8e503a9df9..a63f9df918 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -1940,7 +1940,7 @@ static String marked_documentation(const String &p_bbcode) {
line = "\t" + line.substr(code_block_indent, line.length());
}
- if (in_code_block && line.find("[/codeblock]") != -1) {
+ if (in_code_block && line.contains("[/codeblock]")) {
line = "\n";
in_code_block = false;
}
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 47772b8039..c2bb2caa29 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -73,23 +73,21 @@ void init_autoloads() {
RES res = ResourceLoader::load(info.path);
ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
Node *n = nullptr;
- if (res->is_class("PackedScene")) {
- Ref<PackedScene> ps = res;
- n = ps->instantiate();
- } else if (res->is_class("Script")) {
- Ref<Script> script_res = res;
- StringName ibt = script_res->get_instance_base_type();
+ Ref<PackedScene> scn = res;
+ Ref<Script> script = res;
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ } else if (script.is_valid()) {
+ StringName ibt = script->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "Node");
ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
Object *obj = ClassDB::instantiate(ibt);
- ERR_CONTINUE_MSG(obj == nullptr,
- "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
- String(ibt));
+ ERR_CONTINUE_MSG(!obj, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt) + ".");
n = Object::cast_to<Node>(obj);
- n->set_script(script_res);
+ n->set_script(script);
}
ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
@@ -134,12 +132,14 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
}
+#ifdef DEBUG_ENABLED
// Enable all warnings for GDScript, so we can test them.
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
}
+#endif
// Enable printing to show results
_print_line_enabled = true;
@@ -153,6 +153,21 @@ GDScriptTestRunner::~GDScriptTestRunner() {
}
}
+#ifndef DEBUG_ENABLED
+static String strip_warnings(const String &p_expected) {
+ // On release builds we don't have warnings. Here we remove them from the output before comparison
+ // so it doesn't fail just because of difference in warnings.
+ String expected_no_warnings;
+ for (String line : p_expected.split("\n")) {
+ if (line.begins_with(">> ")) {
+ continue;
+ }
+ expected_no_warnings += line + "\n";
+ }
+ return expected_no_warnings.strip_edges() + "\n";
+}
+#endif
+
int GDScriptTestRunner::run_tests() {
if (!make_tests()) {
FAIL("An error occurred while making the tests.");
@@ -170,6 +185,9 @@ int GDScriptTestRunner::run_tests() {
GDScriptTest::TestResult result = test.run_test();
String expected = FileAccess::get_file_as_string(test.get_output_file());
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
INFO(test.get_source_file());
if (!result.passed) {
INFO(expected);
@@ -233,6 +251,22 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
}
} else {
if (next.get_extension().to_lower() == "gd") {
+#ifndef DEBUG_ENABLED
+ // On release builds, skip tests marked as debug only.
+ Error open_err = OK;
+ FileAccessRef script_file(FileAccess::open(current_dir.plus_file(next), FileAccess::READ, &open_err));
+ if (open_err != OK) {
+ ERR_PRINT(vformat(R"(Couldn't open test file "%s".)", next));
+ next = dir->get_next();
+ continue;
+ } else {
+ if (script_file->get_line() == "#debug-only") {
+ next = dir->get_next();
+ continue;
+ }
+ }
+#endif
+
String out_file = next.get_basename() + ".out";
if (!is_generating && !dir->file_exists(out_file)) {
ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
@@ -387,6 +421,10 @@ bool GDScriptTest::check_output(const String &p_output) const {
String got = p_output.strip_edges(); // TODO: may be hacky.
got += "\n"; // Make sure to insert newline for CI static checks.
+#ifndef DEBUG_ENABLED
+ expected = strip_warnings(expected);
+#endif
+
return got == expected;
}
@@ -469,6 +507,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
return result;
}
+#ifdef DEBUG_ENABLED
StringBuilder warning_string;
for (const GDScriptWarning &E : parser.get_warnings()) {
const GDScriptWarning warning = E;
@@ -482,6 +521,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
warning_string.append("\n");
}
result.output += warning_string.as_string();
+#endif
// Test compiling.
GDScriptCompiler compiler;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..928c886650
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,10 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..03a1711d7b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+# Different enum types can't be assigned without casting.
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+
+func test():
+ print(class_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..d08d3dd7b2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.gd
@@ -0,0 +1,8 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
new file mode 100644
index 0000000000..fde7e92f8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "MyOtherEnum (enum)" to a target of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
new file mode 100644
index 0000000000..ca6d892218
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.gd
@@ -0,0 +1,6 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+func test():
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
new file mode 100644
index 0000000000..b1710c798d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
new file mode 100644
index 0000000000..edb785c8b6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: int = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: int = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_enum_to_int_typed_var.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
new file mode 100644
index 0000000000..726e4fd413
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = 0 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = 1 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = 0 as MyEnum
+ print(local_var)
+ local_var = 1 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_int_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
new file mode 100644
index 0000000000..798912c987
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.gd
@@ -0,0 +1,14 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+enum MyOtherEnum { OTHER_ENUM_VALUE_1, OTHER_ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+
+func test():
+ print(class_var)
+ class_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(class_var)
+
+ var local_var: MyEnum = MyOtherEnum.OTHER_ENUM_VALUE_1 as MyEnum
+ print(local_var)
+ local_var = MyOtherEnum.OTHER_ENUM_VALUE_2 as MyEnum
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_other_enum_cast_to_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
new file mode 100644
index 0000000000..2bfb318c3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.gd
@@ -0,0 +1,13 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+var class_var: MyEnum = MyEnum.ENUM_VALUE_1
+
+func test():
+ print(class_var)
+ class_var = MyEnum.ENUM_VALUE_2
+ print(class_var)
+
+ var local_var: MyEnum = MyEnum.ENUM_VALUE_1
+ print(local_var)
+ local_var = MyEnum.ENUM_VALUE_2
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
new file mode 100644
index 0000000000..5f53802c33
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_assign_same_enum.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
new file mode 100644
index 0000000000..7022d14566
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.gd
@@ -0,0 +1,21 @@
+# Enum is equivalent to int for comparisons and operations.
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+enum OtherEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ print(MyEnum.ZERO == OtherEnum.ZERO)
+ print(MyEnum.ZERO == 1)
+ print(MyEnum.ZERO != OtherEnum.ONE)
+ print(MyEnum.ZERO != 0)
+
+ print(MyEnum.ONE + OtherEnum.TWO)
+ print(2 - MyEnum.ONE)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
new file mode 100644
index 0000000000..c8f34c11db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_is_treated_as_int.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+true
+false
+true
+false
+3
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
new file mode 100644
index 0000000000..885d70408a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.gd
@@ -0,0 +1,13 @@
+enum MyEnum {
+ ZERO,
+ ONE,
+ TWO,
+}
+
+func test():
+ for key in MyEnum.keys():
+ prints(key, MyEnum[key])
+
+ # https://github.com/godotengine/godot/issues/55491
+ for key in MyEnum:
+ prints(key, MyEnum[key])
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
new file mode 100644
index 0000000000..d29f53109c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_type_is_treated_as_dictionary.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+ZERO 0
+ONE 1
+TWO 2
+ZERO 0
+ONE 1
+TWO 2
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
new file mode 100644
index 0000000000..2be1024214
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.gd
@@ -0,0 +1,15 @@
+enum MyEnum { ENUM_VALUE_1, ENUM_VALUE_2 }
+
+# Assigning int value to enum-typed variable without explicit cast causes a warning.
+# While it is valid it may be a mistake in the assignment.
+var class_var: MyEnum = 0
+
+func test():
+ print(class_var)
+ class_var = 1
+ print(class_var)
+
+ var local_var: MyEnum = 0
+ print(local_var)
+ local_var = 1
+ print(local_var)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
new file mode 100644
index 0000000000..eef13bbff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 9
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 12
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+>> WARNING
+>> Line: 14
+>> INT_ASSIGNED_TO_ENUM
+>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
+0
+1
+0
+1
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
index 10780b5379..7b3c112fe9 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
@@ -1,3 +1,4 @@
+#debug-only
func test():
var node := Node.new()
var inside_tree = node.is_inside_tree
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
index e585c374e2..fe48ade26b 100644
--- a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
@@ -2,5 +2,5 @@ GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/callable_call_after_free_object.gd
->> 5
+>> 6
>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 51608273a1..0056fee7a4 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -50,13 +50,13 @@
#include "core/io/file_access.h"
#include "core/io/file_access_memory.h"
#include "core/io/json.h"
+#include "core/io/stream_peer.h"
#include "core/math/disjoint_set.h"
#include "core/math/vector2.h"
#include "core/variant/dictionary.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "drivers/png/png_driver_common.h"
#include "editor/import/resource_importer_scene.h"
#include "scene/2d/node_2d.h"
@@ -6277,7 +6277,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
p_track.rotation_track.values.write[key_i] = rotation;
}
} else if (track_type == Animation::TYPE_VALUE) {
- if (path.find(":position") != -1) {
+ if (path.contains(":position")) {
p_track.position_track.times = times;
p_track.position_track.interpolation = gltf_interpolation;
@@ -6288,7 +6288,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 position = p_animation->track_get_key_value(p_track_i, key_i);
p_track.position_track.values.write[key_i] = position;
}
- } else if (path.find(":rotation") != -1) {
+ } else if (path.contains(":rotation")) {
p_track.rotation_track.times = times;
p_track.rotation_track.interpolation = gltf_interpolation;
@@ -6299,7 +6299,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
}
- } else if (path.find(":scale") != -1) {
+ } else if (path.contains(":scale")) {
p_track.scale_track.times = times;
p_track.scale_track.interpolation = gltf_interpolation;
@@ -6312,7 +6312,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
}
}
} else if (track_type == Animation::TYPE_BEZIER) {
- if (path.find("/scale") != -1) {
+ if (path.contains("/scale")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.scale_track.times.size()) {
Vector<real_t> new_times;
@@ -6333,16 +6333,16 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.scale_track.values[key_i];
- if (path.find("/scale:x") != -1) {
+ if (path.contains("/scale:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:y") != -1) {
+ } else if (path.contains("/scale:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/scale:z") != -1) {
+ } else if (path.contains("/scale:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
- } else if (path.find("/position") != -1) {
+ } else if (path.contains("/position")) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.position_track.times.size()) {
Vector<real_t> new_times;
@@ -6359,11 +6359,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.position_track.values[key_i];
- if (path.find("/position:x") != -1) {
+ if (path.contains("/position:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:y") != -1) {
+ } else if (path.contains("/position:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- } else if (path.find("/position:z") != -1) {
+ } else if (path.contains("/position:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
}
p_track.position_track.values.write[key_i] = bezier_track;
@@ -6384,7 +6384,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
String orig_track_path = animation->track_get_path(track_i);
- if (String(orig_track_path).find(":position") != -1) {
+ if (String(orig_track_path).contains(":position")) {
const Vector<String> node_suffix = String(orig_track_path).split(":position");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6400,7 +6400,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":rotation_degrees") != -1) {
+ } else if (String(orig_track_path).contains(":rotation_degrees")) {
const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6416,7 +6416,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":scale") != -1) {
+ } else if (String(orig_track_path).contains(":scale")) {
const Vector<String> node_suffix = String(orig_track_path).split(":scale");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6432,7 +6432,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(node_index, track);
}
}
- } else if (String(orig_track_path).find(":transform") != -1) {
+ } else if (String(orig_track_path).contains(":transform")) {
const Vector<String> node_suffix = String(orig_track_path).split(":transform");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
@@ -6443,7 +6443,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks().insert(transform_track_i.key, track);
}
}
- } else if (String(orig_track_path).find(":blend_shapes/") != -1) {
+ } else if (String(orig_track_path).contains(":blend_shapes/")) {
const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
const NodePath path = node_suffix[0];
const String suffix = node_suffix[1];
@@ -6499,7 +6499,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
tracks[mesh_index] = track;
}
- } else if (String(orig_track_path).find(":") != -1) {
+ } else if (String(orig_track_path).contains(":")) {
//Process skeleton
const Vector<String> node_suffix = String(orig_track_path).split(":");
const String node = node_suffix[0];
@@ -6529,7 +6529,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
gltf_animation->get_tracks()[node_i] = track;
}
}
- } else if (String(orig_track_path).find(":") == -1) {
+ } else if (!String(orig_track_path).contains(":")) {
ERR_CONTINUE(!ap->get_parent());
Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path);
for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
@@ -6654,8 +6654,8 @@ Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
Dictionary asset;
asset["version"] = version;
- String hash = VERSION_HASH;
- asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.length() == 0 ? String("unknown") : hash);
+ String hash = String(VERSION_HASH);
+ asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.is_empty() ? String("unknown") : hash);
state->json["asset"] = asset;
ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED);
ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED);
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 67deedf839..6df7835855 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -813,6 +813,7 @@ void GridMap::_update_octants_callback() {
}
while (to_delete.front()) {
+ memdelete(octant_map[to_delete.front()->get()]);
octant_map.erase(to_delete.front()->get());
to_delete.pop_front();
}
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 57a1b697be..597c12ed23 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -38,6 +38,9 @@ void register_meshoptimizer_types() {
SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
SurfaceTool::simplify_scale_func = meshopt_simplifyScale;
SurfaceTool::simplify_sloppy_func = meshopt_simplifySloppy;
+ SurfaceTool::generate_remap_func = meshopt_generateVertexRemap;
+ SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer;
+ SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
}
void unregister_meshoptimizer_types() {
@@ -45,4 +48,7 @@ void unregister_meshoptimizer_types() {
SurfaceTool::simplify_func = nullptr;
SurfaceTool::simplify_scale_func = nullptr;
SurfaceTool::simplify_sloppy_func = nullptr;
+ SurfaceTool::generate_remap_func = nullptr;
+ SurfaceTool::remap_vertex_func = nullptr;
+ SurfaceTool::remap_index_func = nullptr;
}
diff --git a/modules/modules_builders.py b/modules/modules_builders.py
index 2243162555..13d5a2075a 100644
--- a/modules/modules_builders.py
+++ b/modules/modules_builders.py
@@ -14,13 +14,10 @@ def generate_modules_enabled(target, source, env):
def generate_modules_tests(target, source, env):
import os
- import glob
with open(target[0].path, "w") as f:
- for name, path in env.module_list.items():
- headers = glob.glob(os.path.join(path, "tests", "*.h"))
- for h in headers:
- f.write('#include "%s"\n' % (os.path.normpath(h)))
+ for header in source:
+ f.write('#include "%s"\n' % (os.path.normpath(header.path)))
if __name__ == "__main__":
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 26436e3ec0..085ab9a467 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -3245,6 +3245,8 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
CRASH_COND(!valid);
#endif
+ GD_MONO_SCOPE_THREAD_ATTACH;
+
if (native) {
StringName native_name = NATIVE_GDMONOCLASS_NAME(native);
if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
@@ -3257,8 +3259,6 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
}
}
- GD_MONO_SCOPE_THREAD_ATTACH;
-
Callable::CallError unchecked_error;
return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 2be588cac4..2de923c125 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -103,8 +103,6 @@ private:
bool valid = false;
bool reload_invalidated = false;
- bool builtin;
-
GDMonoClass *base = nullptr;
GDMonoClass *native = nullptr;
GDMonoClass *script_class = nullptr;
diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
index ce2b378623..7a4641dbbc 100644
--- a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs
@@ -47,9 +47,13 @@ namespace GodotTools.OpenVisualStudio
if (dte == null)
{
// Open a new instance
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.17.0");
- var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true);
- dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+ if (dte == null)
+ {
+ // Launch of VS 2022 failed, fallback to 2019
+ dte = TryVisualStudioLaunch("VisualStudio.DTE.16.0");
+ }
dte.UserControl = true;
@@ -133,6 +137,21 @@ namespace GodotTools.OpenVisualStudio
return 0;
}
+ private static DTE TryVisualStudioLaunch(string version)
+ {
+ try
+ {
+ var visualStudioDteType = Type.GetTypeFromProgID(version, throwOnError: true);
+ var dte = (DTE)Activator.CreateInstance(visualStudioDteType);
+
+ return dte;
+ }
+ catch (COMException)
+ {
+ return null;
+ }
+ }
+
private static DTE FindInstanceEditingSolution(string solutionPath)
{
if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0)
@@ -164,7 +183,7 @@ namespace GodotTools.OpenVisualStudio
continue;
// The digits after the colon are the process ID
- if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]"))
+ if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.1[6-7].0:[0-9]"))
continue;
if (pprot.GetObject(moniker[0], out object ppunkObject) == 0)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index 37e6a34977..ed758cc137 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -537,7 +537,6 @@ MONO_AOT_MODE_LAST = 1000,
{
var iosArchs = new[]
{
- "armv7",
"arm64"
};
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 1b4ab0ef4b..1de41821f9 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -690,11 +690,11 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
continue;
}
- if (parts[curr_prefix_length][0] >= '0' && parts[curr_prefix_length][0] <= '9') {
+ if (is_digit(parts[curr_prefix_length][0])) {
// The name of enum constants may begin with a numeric digit when strip from the enum prefix,
// so we make the prefix for this constant one word shorter in those cases.
for (curr_prefix_length = curr_prefix_length - 1; curr_prefix_length > 0; curr_prefix_length--) {
- if (parts[curr_prefix_length][0] < '0' || parts[curr_prefix_length][0] > '9') {
+ if (!is_digit(parts[curr_prefix_length][0])) {
break;
}
}
diff --git a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
index 6e9547ce9b..b0ded3133f 100644
--- a/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
+++ b/modules/mono/editor_templates/CharacterBody2D/basic_movement.cs
@@ -6,7 +6,7 @@ using System;
public partial class _CLASS_ : _BASE_
{
public const float Speed = 300.0f;
- public const float JumpForce = -400.0f;
+ public const float JumpVelocity = -400.0f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
public float gravity = (float)ProjectSettings.GetSetting("physics/2d/default_gravity");
@@ -21,7 +21,7 @@ public partial class _CLASS_ : _BASE_
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
- motionVelocity.y = JumpForce;
+ motionVelocity.y = JumpVelocity;
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
index 13be4bbcb1..d8c2f67ac8 100644
--- a/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
+++ b/modules/mono/editor_templates/CharacterBody3D/basic_movement.cs
@@ -6,7 +6,7 @@ using System;
public partial class _CLASS_ : _BASE_
{
public const float Speed = 5.0f;
- public const float JumpForce = 4.5f;
+ public const float JumpVelocity = 4.5f;
// Get the gravity from the project settings to be synced with RigidDynamicBody nodes.
public float gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
@@ -21,7 +21,7 @@ public partial class _CLASS_ : _BASE_
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
- motionVelocity.y = JumpForce;
+ motionVelocity.y = JumpVelocity;
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
diff --git a/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
index 00fdc9968e..a1b93e7daa 100644
--- a/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor_templates/VisualShaderNodeCustom/basic.cs
@@ -55,11 +55,6 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return 0;
}
- public override string _GetGlobalCode(Shader.Mode mode)
- {
- return "";
- }
-
public override string _GetCode(Godot.Collections.Array inputVars, Godot.Collections.Array outputVars, Shader.Mode mode, VisualShader.Type type)
{
return "";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index eba0ea9a79..a1f058ffe5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -266,7 +266,7 @@ namespace Godot
/// <returns>The capitalized string.</returns>
public static string Capitalize(this string instance)
{
- string aux = instance.Replace("_", " ").ToLower();
+ string aux = instance.CamelcaseToUnderscore(true).Replace("_", " ").Trim();
string cap = string.Empty;
for (int i = 0; i < aux.GetSliceCount(" "); i++)
@@ -284,6 +284,51 @@ namespace Godot
return cap;
}
+ private static string CamelcaseToUnderscore(this string instance, bool lowerCase)
+ {
+ string newString = string.Empty;
+ int startIndex = 0;
+
+ for (int i = 1; i < instance.Length; i++)
+ {
+ bool isUpper = char.IsUpper(instance[i]);
+ bool isNumber = char.IsDigit(instance[i]);
+
+ bool areNext2Lower = false;
+ bool isNextLower = false;
+ bool isNextNumber = false;
+ bool wasPrecedentUpper = char.IsUpper(instance[i - 1]);
+ bool wasPrecedentNumber = char.IsDigit(instance[i - 1]);
+
+ if (i + 2 < instance.Length)
+ {
+ areNext2Lower = char.IsLower(instance[i + 1]) && char.IsLower(instance[i + 2]);
+ }
+
+ if (i + 1 < instance.Length)
+ {
+ isNextLower = char.IsLower(instance[i + 1]);
+ isNextNumber = char.IsDigit(instance[i + 1]);
+ }
+
+ bool condA = isUpper && !wasPrecedentUpper && !wasPrecedentNumber;
+ bool condB = wasPrecedentUpper && isUpper && areNext2Lower;
+ bool condC = isNumber && !wasPrecedentNumber;
+ bool canBreakNumberLetter = isNumber && !wasPrecedentNumber && isNextLower;
+ bool canBreakLetterNumber = !isNumber && wasPrecedentNumber && (isNextLower || isNextNumber);
+
+ bool shouldSplit = condA || condB || condC || canBreakNumberLetter || canBreakLetterNumber;
+ if (shouldSplit)
+ {
+ newString += instance.Substring(startIndex, i - startIndex) + "_";
+ startIndex = i;
+ }
+ }
+
+ newString += instance.Substring(startIndex, instance.Length - startIndex);
+ return lowerCase ? newString.ToLower() : newString;
+ }
+
/// <summary>
/// Performs a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 1f5282e88f..fa7838633c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -512,24 +512,24 @@ namespace Godot
/// Returns the result of the spherical linear interpolation between
/// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
- /// Note: Both vectors must be normalized.
+ /// This method also handles interpolating the lengths if the input vectors have different lengths.
+ /// For the special case of one or both input vectors having zero length, this method behaves like [method lerp].
/// </summary>
- /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Slerp(Vector2 to, real_t weight)
{
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Vector2.Slerp: From vector is not normalized.");
+ real_t startLengthSquared = LengthSquared();
+ real_t endLengthSquared = to.LengthSquared();
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
- if (!to.IsNormalized())
- {
- throw new InvalidOperationException($"Vector2.Slerp: `{nameof(to)}` is not normalized.");
- }
-#endif
- return Rotated(AngleTo(to) * weight);
+ real_t startLength = Mathf.Sqrt(startLengthSquared);
+ real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
+ real_t angle = AngleTo(to);
+ return Rotated(angle * weight) * (resultLength / startLength);
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 433a5d9dc9..0faf13f8b7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -549,25 +549,24 @@ namespace Godot
/// Returns the result of the spherical linear interpolation between
/// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
- /// Note: Both vectors must be normalized.
+ /// This method also handles interpolating the lengths if the input vectors have different lengths.
+ /// For the special case of one or both input vectors having zero length, this method behaves like [method lerp].
/// </summary>
- /// <param name="to">The destination vector for interpolation. Must be normalized.</param>
+ /// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector3 Slerp(Vector3 to, real_t weight)
{
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Vector3.Slerp: From vector is not normalized.");
+ real_t startLengthSquared = LengthSquared();
+ real_t endLengthSquared = to.LengthSquared();
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
- if (!to.IsNormalized())
- {
- throw new InvalidOperationException($"Vector3.Slerp: `{nameof(to)}` is not normalized.");
- }
-#endif
- real_t theta = AngleTo(to);
- return Rotated(Cross(to), theta * weight);
+ real_t startLength = Mathf.Sqrt(startLengthSquared);
+ real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
+ real_t angle = AngleTo(to);
+ return Rotated(Cross(to).Normalized(), angle * weight) * (resultLength / startLength);
}
/// <summary>
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
index 7e56b14a24..73e3cb4fff 100644
--- a/modules/ogg/ogg_packet_sequence.h
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -104,7 +104,7 @@ class OGGPacketSequencePlayback : public RefCounted {
mutable ogg_packet *packet;
- uint64_t data_version;
+ uint64_t data_version = 0;
mutable int64_t packetno = 0;
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index dd5eef8b2b..2e0d17fb28 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -270,13 +270,14 @@ void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance,
OccluderInstance &instance = scenario.instances[p_instance];
+ bool changed = false;
+
if (instance.removed) {
instance.removed = false;
scenario.removed_instances.erase(p_instance);
+ changed = true; // It was removed and re-added, we might have missed some changes
}
- bool changed = false;
-
if (instance.occluder != p_occluder) {
Occluder *old_occluder = occluder_owner.get_or_null(instance.occluder);
if (old_occluder) {
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index e50a5337cb..c7511f587e 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -303,22 +303,6 @@ _FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
return (prop != U_JT_RIGHT_JOINING) && (prop != U_JT_NON_JOINING) ? !is_ligature(p_pchr, p_chr) : false;
}
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
/*************************************************************************/
String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index dd520a2e40..182d2a02ad 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -44,30 +44,6 @@
#endif
/*************************************************************************/
-/* Character properties. */
-/*************************************************************************/
-
-_FORCE_INLINE_ bool is_control(char32_t p_char) {
- return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
-}
-
-_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
- return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
-}
-
-_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
- return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
-}
-
-_FORCE_INLINE_ bool is_punct(char32_t p_char) {
- return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x005E) || (p_char == 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
-}
-
-_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
- return (p_char == 0x005F);
-}
-
-/*************************************************************************/
String TextServerFallback::interface_name = "Fallback";
uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index b00b5a2ddd..5ea8eaff00 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -653,7 +653,6 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
List<int> ids;
script->get_node_list(&ids);
- StringName editor_icons = "EditorIcons";
for (int &E : ids) {
if (p_only_id >= 0 && p_only_id != E) {
@@ -2803,7 +2802,7 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra
String name = p_args[i];
Variant::Type type = Variant::NIL;
- if (name.find(":") != -1) {
+ if (name.contains(":")) {
String tt = name.get_slice(":", 1);
name = name.get_slice(":", 0);
for (int j = 0; j < Variant::VARIANT_MAX; j++) {
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index f1b01aa6dc..b01732b2fd 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -173,10 +173,10 @@ class VisualScriptEditor : public ScriptEditorBase {
String member_name;
PortAction port_action;
- int port_action_node;
- int port_action_output;
+ int port_action_node = 0;
+ int port_action_output = 0;
Vector2 port_action_pos;
- int port_action_new_node;
+ int port_action_new_node = 0;
bool saved_pos_dirty = false;
@@ -196,7 +196,7 @@ class VisualScriptEditor : public ScriptEditorBase {
int _create_new_node_from_name(const String &p_text, const Vector2 &p_point);
void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
- int error_line;
+ int error_line = -1;
void _node_selected(Node *p_node);
void _center_on_node(int p_id);
@@ -267,12 +267,12 @@ class VisualScriptEditor : public ScriptEditorBase {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- int editing_id;
- int editing_input;
+ int editing_id = 0;
+ int editing_input = 0;
- bool can_swap;
- int data_disconnect_node;
- int data_disconnect_port;
+ bool can_swap = false;
+ int data_disconnect_node = 0;
+ int data_disconnect_port = 0;
void _default_value_changed();
void _default_value_edited(Node *p_button, int p_id, int p_input_port);
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index 1059d126bc..bba5410629 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -379,8 +379,6 @@ void VisualScriptPropertySelector::_bind_methods() {
}
VisualScriptPropertySelector::VisualScriptPropertySelector() {
- virtuals_only = false;
-
vbox = memnew(VBoxContainer);
add_child(vbox);
@@ -1116,14 +1114,14 @@ TreeItem *VisualScriptPropertySelector::SearchRunner::_create_class_item(TreeIte
String details = p_doc->name;
if (p_doc->category.begins_with("VisualScriptCustomNode/")) {
Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
text_0 = path[path.size() - 1];
text_1 = "VisualScriptCustomNode";
what = "VisualScriptCustomNode";
details = "CustomNode";
} else if (p_doc->category.begins_with("VisualScriptNode/")) {
Vector<String> path = p_doc->name.split("/");
- icon = ui_service->get_theme_icon("VisualScript", "EditorIcons");
+ icon = ui_service->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
text_0 = path[path.size() - 1];
if (p_doc->category.begins_with("VisualScriptNode/deconstruct")) {
text_0 = "deconstruct " + text_0;
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 3970c4473e..6b5112f1af 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -96,16 +96,16 @@ class VisualScriptPropertySelector : public ConfirmationDialog {
EditorHelpBit *help_bit;
- bool properties;
- bool visual_script_generic;
- bool connecting;
+ bool properties = false;
+ bool visual_script_generic = false;
+ bool connecting = false;
String selected;
Variant::Type type;
String base_type;
String base_script;
ObjectID script;
Object *instance;
- bool virtuals_only;
+ bool virtuals_only = false;
VBoxContainer *vbox;
protected:
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index fbdf3a654b..88445f2f98 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -1164,9 +1164,6 @@ void VisualScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_custom_signal", "name"), &VisualScript::remove_custom_signal);
ClassDB::bind_method(D_METHOD("rename_custom_signal", "name", "new_name"), &VisualScript::rename_custom_signal);
- //ClassDB::bind_method(D_METHOD("set_variable_info","name","info"),&VScript::set_variable_info);
- //ClassDB::bind_method(D_METHOD("get_variable_info","name"),&VScript::set_variable_info);
-
ClassDB::bind_method(D_METHOD("set_instance_base_type", "type"), &VisualScript::set_instance_base_type);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 17a3566ed2..e8942b9788 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -377,13 +377,13 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
- if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
+ if (!is_hex_digit(c)) {
_set_error("Malformed hex constant in string");
r_token.type = TK_ERROR;
return ERR_PARSE_ERROR;
}
char32_t v;
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
v = c - '0';
} else if (c >= 'a' && c <= 'f') {
v = c - 'a';
@@ -457,7 +457,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
break;
}
- if (cchar >= '0' && cchar <= '9') {
+ if (is_digit(cchar)) {
//a number
String num;
@@ -476,7 +476,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
while (true) {
switch (reading) {
case READING_INT: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
//pass
} else if (c == '.') {
reading = READING_DEC;
@@ -489,7 +489,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_DEC: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
} else if (c == 'e') {
reading = READING_EXP;
@@ -499,7 +499,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
} break;
case READING_EXP: {
- if (c >= '0' && c <= '9') {
+ if (is_digit(c)) {
exp_beg = true;
} else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) {
@@ -532,11 +532,11 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
}
return OK;
- } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') {
+ } else if (is_ascii_char(cchar) || cchar == '_') {
String id;
bool first = true;
- while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) {
+ while (is_ascii_char(cchar) || cchar == '_' || (!first && is_digit(cchar))) {
id += String::chr(cchar);
cchar = GET_CHAR();
first = false;
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index f3594e5164..e7f4e542c1 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -2495,7 +2495,7 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const
VisualScriptSceneNode::TypeGuess VisualScriptSceneNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
VisualScriptSceneNode::TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "Node";
+ tg.gdclass = SNAME("Node");
#ifdef TOOLS_ENABLED
Ref<Script> script = get_visual_script();
@@ -2649,7 +2649,7 @@ VisualScriptNodeInstance *VisualScriptSceneTree::instantiate(VisualScriptInstanc
VisualScriptSceneTree::TypeGuess VisualScriptSceneTree::guess_output_type(TypeGuess *p_inputs, int p_output) const {
TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "SceneTree";
+ tg.gdclass = SNAME("SceneTree");
return tg;
}
@@ -2766,11 +2766,11 @@ PropertyInfo VisualScriptSelf::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptSelf::get_output_value_port_info(int p_idx) const {
- String type_name;
+ StringName type_name;
if (get_visual_script().is_valid()) {
type_name = get_visual_script()->get_instance_base_type();
} else {
- type_name = "instance";
+ type_name = SNAME("instance");
}
return PropertyInfo(Variant::OBJECT, type_name);
@@ -2801,7 +2801,7 @@ VisualScriptNodeInstance *VisualScriptSelf::instantiate(VisualScriptInstance *p_
VisualScriptSelf::TypeGuess VisualScriptSelf::guess_output_type(TypeGuess *p_inputs, int p_output) const {
VisualScriptSceneNode::TypeGuess tg;
tg.type = Variant::OBJECT;
- tg.gdclass = "Object";
+ tg.gdclass = SNAME("Object");
Ref<Script> script = get_visual_script();
if (!script.is_valid()) {
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index be1c75c354..bebb198126 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -163,22 +163,24 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
_peer = Ref<WSLPeer>(memnew(WSLPeer));
if (p_host.is_valid_ip_address()) {
- ip_candidates.clear();
- ip_candidates.push_back(IPAddress(p_host));
+ _ip_candidates.push_back(IPAddress(p_host));
} else {
- ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
- }
-
- ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
-
- String port = "";
- if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
- port = ":" + itos(p_port);
+ // Queue hostname for resolution.
+ _resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
+ ERR_FAIL_COND_V(_resolver_id == IP::RESOLVER_INVALID_ID, ERR_INVALID_PARAMETER);
+ // Check if it was found in cache.
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
}
- Error err = ERR_BUG; // Should be at least one entry.
- while (ip_candidates.size() > 0) {
- err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
+ // We assume OK while hostname resultion is pending.
+ Error err = _resolver_id != IP::RESOLVER_INVALID_ID ? OK : FAILED;
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), p_port);
if (err == OK) {
break;
}
@@ -200,8 +202,11 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
_key = WSLPeer::generate_key();
- // TODO custom extra headers (allow overriding this too?)
String request = "GET " + p_path + " HTTP/1.1\r\n";
+ String port = "";
+ if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+ port = ":" + itos(p_port);
+ }
request += "Host: " + p_host + port + "\r\n";
request += "Upgrade: websocket\r\n";
request += "Connection: Upgrade\r\n";
@@ -231,6 +236,30 @@ int WSLClient::get_max_packet_size() const {
}
void WSLClient::poll() {
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(_resolver_id);
+ if (ip_status == IP::RESOLVER_STATUS_WAITING) {
+ return;
+ }
+ // Anything else is either a candidate or a failure.
+ Error err = FAILED;
+ if (ip_status == IP::RESOLVER_STATUS_DONE) {
+ _ip_candidates = IP::get_singleton()->get_resolve_item_addresses(_resolver_id);
+ while (_ip_candidates.size()) {
+ err = _tcp->connect_to_host(_ip_candidates.pop_front(), _port);
+ if (err == OK) {
+ break;
+ }
+ }
+ }
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ if (err != OK) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ }
if (_peer->is_connected_to_host()) {
_peer->poll();
if (!_peer->is_connected_to_host()) {
@@ -251,7 +280,7 @@ void WSLClient::poll() {
_on_error();
break;
case StreamPeerTCP::STATUS_CONNECTED: {
- ip_candidates.clear();
+ _ip_candidates.clear();
Ref<StreamPeerSSL> ssl;
if (_use_ssl) {
if (_connection == _tcp) {
@@ -282,9 +311,9 @@ void WSLClient::poll() {
_do_handshake();
} break;
case StreamPeerTCP::STATUS_ERROR:
- while (ip_candidates.size() > 0) {
+ while (_ip_candidates.size() > 0) {
_tcp->disconnect_from_host();
- if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+ if (_tcp->connect_to_host(_ip_candidates.pop_front(), _port) == OK) {
return;
}
}
@@ -307,7 +336,7 @@ MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
return CONNECTION_CONNECTED;
}
- if (_tcp->is_connected_to_host()) {
+ if (_tcp->is_connected_to_host() || _resolver_id != IP::RESOLVER_INVALID_ID) {
return CONNECTION_CONNECTING;
}
@@ -330,7 +359,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0;
- ip_candidates.clear();
+ if (_resolver_id != IP::RESOLVER_INVALID_ID) {
+ IP::get_singleton()->erase_resolve_item(_resolver_id);
+ _resolver_id = IP::RESOLVER_INVALID_ID;
+ }
+
+ _ip_candidates.clear();
}
IPAddress WSLClient::get_connected_host() const {
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 4839d7ab9b..d846e6be00 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -63,10 +63,11 @@ private:
String _key;
String _host;
- int _port;
- Array ip_candidates;
+ uint16_t _port;
+ Array _ip_candidates;
Vector<String> _protocols;
bool _use_ssl = false;
+ IP::ResolverID _resolver_id = IP::RESOLVER_INVALID_ID;
void _do_handshake();
bool _verify_headers(String &r_protocol);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 3d0dabc56e..b0f16337ed 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -161,6 +161,16 @@ int DisplayServerAndroid::screen_get_dpi(int p_screen) const {
return godot_io_java->get_screen_dpi();
}
+float DisplayServerAndroid::screen_get_refresh_rate(int p_screen) const {
+ GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
+ if (!godot_io_java) {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+
+ return godot_io_java->get_screen_refresh_rate(SCREEN_REFRESH_RATE_FALLBACK);
+}
+
bool DisplayServerAndroid::screen_is_touchscreen(int p_screen) const {
return true;
}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index e52e07bf1a..23077a6529 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -106,6 +106,7 @@ public:
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 61d2f897ef..df2d32e152 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -416,10 +416,10 @@ String EditorExportPlatformAndroid::get_package_name(const String &p_package) co
bool first = true;
for (int i = 0; i < basename.length(); i++) {
char32_t c = basename[i];
- if (c >= '0' && c <= '9' && first) {
+ if (is_digit(c) && first) {
continue;
}
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
+ if (is_ascii_alphanumeric_char(c)) {
name += String::chr(c);
first = false;
}
@@ -462,19 +462,19 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package,
first = true;
continue;
}
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+ if (!is_ascii_identifier_char(c)) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c));
}
return false;
}
- if (first && (c >= '0' && c <= '9')) {
+ if (first && is_digit(c)) {
if (r_error) {
*r_error = TTR("A digit cannot be the first character in a package segment.");
}
return false;
}
- if (first && c == '_') {
+ if (first && is_underscore(c)) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c));
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index d679fd92c0..b151e7eec1 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -226,6 +226,14 @@ public class GodotIO {
return (int)(metrics.density * 160f);
}
+ public double getScreenRefreshRate(double fallback) {
+ Display display = activity.getWindowManager().getDefaultDisplay();
+ if (display != null) {
+ return display.getRefreshRate();
+ }
+ return fallback;
+ }
+
public int[] screenGetUsableRect() {
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
Display display = activity.getWindowManager().getDefaultDisplay();
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index e0a535f16e..8a2788e848 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -53,6 +53,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
+ _get_screen_refresh_rate = p_env->GetMethodID(cls, "getScreenRefreshRate", "(D)D");
_screen_get_usable_rect = p_env->GetMethodID(cls, "screenGetUsableRect", "()[I"),
_get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;");
_show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V");
@@ -136,6 +137,19 @@ int GodotIOJavaWrapper::get_screen_dpi() {
}
}
+float GodotIOJavaWrapper::get_screen_refresh_rate(float fallback) {
+ if (_get_screen_refresh_rate) {
+ JNIEnv *env = get_jni_env();
+ if (env == nullptr) {
+ ERR_PRINT("An error occured while trying to get screen refresh rate.");
+ return fallback;
+ }
+ return (float)env->CallDoubleMethod(godot_io_instance, _get_screen_refresh_rate, (double)fallback);
+ }
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return fallback;
+}
+
void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
if (_screen_get_usable_rect) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index c96abf1101..38a2b710a9 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -51,6 +51,7 @@ private:
jmethodID _get_locale = 0;
jmethodID _get_model = 0;
jmethodID _get_screen_DPI = 0;
+ jmethodID _get_screen_refresh_rate = 0;
jmethodID _screen_get_usable_rect = 0;
jmethodID _get_unique_id = 0;
jmethodID _show_keyboard = 0;
@@ -71,6 +72,7 @@ public:
String get_locale();
String get_model();
int get_screen_dpi();
+ float get_screen_refresh_rate(float fallback);
void screen_get_usable_rect(int (&p_rect_xywh)[4]);
String get_unique_id();
bool has_vk();
diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py
index 4e4b0d81c3..f442235e7c 100644
--- a/platform/iphone/detect.py
+++ b/platform/iphone/detect.py
@@ -120,7 +120,6 @@ def configure(env):
)
)
env.Append(CPPDEFINES=["NEED_LONG_INT"])
- env.Append(CPPDEFINES=["LIBYUV_DISABLE_NEON"])
# Disable exceptions on non-tools (template) builds
if not env["tools"]:
diff --git a/platform/iphone/display_server_iphone.h b/platform/iphone/display_server_iphone.h
index 6434483641..7441550f67 100644
--- a/platform/iphone/display_server_iphone.h
+++ b/platform/iphone/display_server_iphone.h
@@ -129,6 +129,7 @@ public:
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
diff --git a/platform/iphone/display_server_iphone.mm b/platform/iphone/display_server_iphone.mm
index 48bda89fc3..9491c9cf90 100644
--- a/platform/iphone/display_server_iphone.mm
+++ b/platform/iphone/display_server_iphone.mm
@@ -393,6 +393,10 @@ int DisplayServerIPhone::screen_get_dpi(int p_screen) const {
}
}
+float DisplayServerIPhone::screen_get_refresh_rate(int p_screen) const {
+ return [UIScreen mainScreen].maximumFramesPerSecond;
+}
+
float DisplayServerIPhone::screen_get_scale(int p_screen) const {
return [UIScreen mainScreen].nativeScale;
}
diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp
index fe00b1a3cd..122e64d6a1 100644
--- a/platform/iphone/export/export_plugin.cpp
+++ b/platform/iphone/export/export_plugin.cpp
@@ -43,7 +43,6 @@ void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset>
Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() {
Vector<ExportArchitecture> archs;
- archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates.
archs.push_back(ExportArchitecture("arm64", true));
return archs;
}
diff --git a/platform/iphone/export/export_plugin.h b/platform/iphone/export/export_plugin.h
index 756bca14dd..93b23f7ee2 100644
--- a/platform/iphone/export/export_plugin.h
+++ b/platform/iphone/export/export_plugin.h
@@ -130,7 +130,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
for (int i = 0; i < pname.length(); i++) {
char32_t c = pname[i];
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (!(is_ascii_alphanumeric_char(c) || c == '-' || c == '.')) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
}
diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp
index 0c4accccc3..4190b24b8e 100644
--- a/platform/javascript/api/api.cpp
+++ b/platform/javascript/api/api.cpp
@@ -71,6 +71,9 @@ void JavaScript::_bind_methods() {
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi);
}
ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScript::download_buffer, DEFVAL("application/octet-stream"));
+ ClassDB::bind_method(D_METHOD("pwa_needs_update"), &JavaScript::pwa_needs_update);
+ ClassDB::bind_method(D_METHOD("pwa_update"), &JavaScript::pwa_update);
+ ADD_SIGNAL(MethodInfo("pwa_update_available"));
}
#if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED)
@@ -102,6 +105,12 @@ Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount,
}
#endif
#if !defined(JAVASCRIPT_ENABLED)
+bool JavaScript::pwa_needs_update() const {
+ return false;
+}
+Error JavaScript::pwa_update() {
+ return ERR_UNAVAILABLE;
+}
void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
}
#endif
diff --git a/platform/javascript/api/javascript_singleton.h b/platform/javascript/api/javascript_singleton.h
index 63f1aec624..e93b0a18a1 100644
--- a/platform/javascript/api/javascript_singleton.h
+++ b/platform/javascript/api/javascript_singleton.h
@@ -59,6 +59,8 @@ public:
Ref<JavaScriptObject> create_callback(const Callable &p_callable);
Variant _create_object_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
void download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime = "application/octet-stream");
+ bool pwa_needs_update() const;
+ Error pwa_update();
static JavaScript *get_singleton();
JavaScript();
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index 2842fc2f5e..a0e1246c55 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -663,7 +663,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
godot_js_config_canvas_id_get(canvas_id, 256);
// Handle contextmenu, webglcontextlost
- godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, p_window_mode == WINDOW_MODE_FULLSCREEN, OS::get_singleton()->is_hidpi_allowed() ? 1 : 0);
+ godot_js_display_setup_canvas(p_resolution.x, p_resolution.y, (p_window_mode == WINDOW_MODE_FULLSCREEN || p_window_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), OS::get_singleton()->is_hidpi_allowed() ? 1 : 0);
// Check if it's windows.
swap_cancel_ok = godot_js_display_is_swap_ok_cancel() == 1;
@@ -794,6 +794,10 @@ float DisplayServerJavaScript::screen_get_scale(int p_screen) const {
return godot_js_display_pixel_ratio_get();
}
+float DisplayServerJavaScript::screen_get_refresh_rate(int p_screen) const {
+ return SCREEN_REFRESH_RATE_FALLBACK; // Javascript doesn't have much of a need for the screen refresh rate, and there's no native way to do so.
+}
+
Vector<DisplayServer::WindowID> DisplayServerJavaScript::get_window_list() const {
Vector<WindowID> ret;
ret.push_back(MAIN_WINDOW_ID);
@@ -897,6 +901,7 @@ void DisplayServerJavaScript::window_set_mode(WindowMode p_mode, WindowID p_wind
}
window_mode = WINDOW_MODE_WINDOWED;
} break;
+ case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
int result = godot_js_display_fullscreen_request();
ERR_FAIL_COND_MSG(result, "The request was denied. Remember that enabling fullscreen is only possible from an input callback for the HTML5 platform.");
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index 1ae5d68787..b50956d91c 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -139,6 +139,7 @@ public:
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1) override;
virtual void virtual_keyboard_hide() override;
diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp
index db0d506cdf..d4c198d631 100644
--- a/platform/javascript/export/export_plugin.cpp
+++ b/platform/javascript/export/export_plugin.cpp
@@ -139,8 +139,7 @@ void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Re
}
if (p_preset->get("progressive_web_app/enabled")) {
head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n";
- head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" +
- p_name + ".service.worker.js');}});</script>\n";
+ config["serviceWorker"] = p_name + ".service.worker.js";
}
// Replaces HTML string
@@ -188,35 +187,46 @@ Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, c
}
Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) {
+ String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
+ if (proj_name.is_empty()) {
+ proj_name = "Godot Game";
+ }
+
// Service worker
const String dir = p_path.get_base_dir();
const String name = p_path.get_file().get_basename();
const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type");
Map<String, String> replaces;
- replaces["@GODOT_VERSION@"] = "1";
- replaces["@GODOT_NAME@"] = name;
+ replaces["@GODOT_VERSION@"] = String::num_int64(OS::get_singleton()->get_unix_time()) + "|" + String::num_int64(OS::get_singleton()->get_ticks_usec());
+ replaces["@GODOT_NAME@"] = proj_name.substr(0, 16);
replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html";
- Array files;
- replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string();
- files.push_back(name + ".html");
- files.push_back(name + ".js");
- files.push_back(name + ".wasm");
- files.push_back(name + ".pck");
- files.push_back(name + ".offline.html");
+
+ // Files cached during worker install.
+ Array cache_files;
+ cache_files.push_back(name + ".html");
+ cache_files.push_back(name + ".js");
+ cache_files.push_back(name + ".offline.html");
if (p_preset->get("html/export_icon")) {
- files.push_back(name + ".icon.png");
- files.push_back(name + ".apple-touch-icon.png");
+ cache_files.push_back(name + ".icon.png");
+ cache_files.push_back(name + ".apple-touch-icon.png");
}
if (mode == EXPORT_MODE_THREADS) {
- files.push_back(name + ".worker.js");
- files.push_back(name + ".audio.worklet.js");
- } else if (mode == EXPORT_MODE_GDNATIVE) {
- files.push_back(name + ".side.wasm");
+ cache_files.push_back(name + ".worker.js");
+ cache_files.push_back(name + ".audio.worklet.js");
+ }
+ replaces["@GODOT_CACHE@"] = Variant(cache_files).to_json_string();
+
+ // Heavy files that are cached on demand.
+ Array opt_cache_files;
+ opt_cache_files.push_back(name + ".wasm");
+ opt_cache_files.push_back(name + ".pck");
+ if (mode == EXPORT_MODE_GDNATIVE) {
+ opt_cache_files.push_back(name + ".side.wasm");
for (int i = 0; i < p_shared_objects.size(); i++) {
- files.push_back(p_shared_objects[i].path.get_file());
+ opt_cache_files.push_back(p_shared_objects[i].path.get_file());
}
}
- replaces["@GODOT_CACHE@"] = Variant(files).to_json_string();
+ replaces["@GODOT_OPT_CACHE@"] = Variant(opt_cache_files).to_json_string();
const String sw_path = dir.plus_file(name + ".service.worker.js");
Vector<uint8_t> sw;
@@ -256,10 +266,6 @@ Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &
const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3);
Dictionary manifest;
- String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name");
- if (proj_name.is_empty()) {
- proj_name = "Godot Game";
- }
manifest["name"] = proj_name;
manifest["start_url"] = "./" + name + ".html";
manifest["display"] = String::utf8(modes[display]);
@@ -658,7 +664,7 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();
if (theme.is_valid()) {
- stop_icon = theme->get_icon("Stop", "EditorIcons");
+ stop_icon = theme->get_icon(SNAME("Stop"), SNAME("EditorIcons"));
} else {
stop_icon.instantiate();
}
diff --git a/platform/javascript/export/export_plugin.h b/platform/javascript/export/export_plugin.h
index c55a881911..278e317430 100644
--- a/platform/javascript/export/export_plugin.h
+++ b/platform/javascript/export/export_plugin.h
@@ -87,7 +87,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
icon.instantiate();
const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges();
if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) {
- return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image();
+ return EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("DefaultProjectIcon"), SNAME("EditorIcons"))->get_image();
}
return icon;
}
diff --git a/platform/javascript/godot_js.h b/platform/javascript/godot_js.h
index 15de8b5dc7..2cb5c3025c 100644
--- a/platform/javascript/godot_js.h
+++ b/platform/javascript/godot_js.h
@@ -49,6 +49,8 @@ extern void godot_js_os_fs_sync(void (*p_callback)());
extern int godot_js_os_execute(const char *p_json);
extern void godot_js_os_shell_open(const char *p_uri);
extern int godot_js_os_hw_concurrency_get();
+extern int godot_js_pwa_cb(void (*p_callback)());
+extern int godot_js_pwa_update();
// Input
extern void godot_js_input_mouse_button_cb(int (*p_callback)(int p_pressed, int p_button, double p_x, double p_y, int p_modifiers));
diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp
index b10e8007c0..77858bff01 100644
--- a/platform/javascript/javascript_singleton.cpp
+++ b/platform/javascript/javascript_singleton.cpp
@@ -30,6 +30,7 @@
#include "api/javascript_singleton.h"
#include "emscripten.h"
+#include "os_javascript.h"
extern "C" {
extern void godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime);
@@ -355,3 +356,10 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
void JavaScript::download_buffer(Vector<uint8_t> p_arr, const String &p_name, const String &p_mime) {
godot_js_os_download_buffer(p_arr.ptr(), p_arr.size(), p_name.utf8().get_data(), p_mime.utf8().get_data());
}
+
+bool JavaScript::pwa_needs_update() const {
+ return OS_JavaScript::get_singleton()->pwa_needs_update();
+}
+Error JavaScript::pwa_update() {
+ return OS_JavaScript::get_singleton()->pwa_update();
+}
diff --git a/platform/javascript/js/engine/config.js b/platform/javascript/js/engine/config.js
index 5628ddbab5..2e5e1ed0d1 100644
--- a/platform/javascript/js/engine/config.js
+++ b/platform/javascript/js/engine/config.js
@@ -107,6 +107,13 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
experimentalVK: false,
/**
+ * The progressive web app service worker to install.
+ * @memberof EngineConfig
+ * @default
+ * @type {string}
+ */
+ serviceWorker: '',
+ /**
* @ignore
* @type {Array.<string>}
*/
@@ -225,34 +232,36 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
*/
Config.prototype.update = function (opts) {
const config = opts || {};
- const me = this;
- function parse(key) {
+ // NOTE: We must explicitly pass the default, accessing it via
+ // the key will fail due to closure compiler renames.
+ function parse(key, def) {
if (typeof (config[key]) === 'undefined') {
- return me[key];
+ return def;
}
return config[key];
}
// Module config
- this.unloadAfterInit = parse('unloadAfterInit');
- this.onPrintError = parse('onPrintError');
- this.onPrint = parse('onPrint');
- this.onProgress = parse('onProgress');
+ this.unloadAfterInit = parse('unloadAfterInit', this.unloadAfterInit);
+ this.onPrintError = parse('onPrintError', this.onPrintError);
+ this.onPrint = parse('onPrint', this.onPrint);
+ this.onProgress = parse('onProgress', this.onProgress);
// Godot config
- this.canvas = parse('canvas');
- this.executable = parse('executable');
- this.mainPack = parse('mainPack');
- this.locale = parse('locale');
- this.canvasResizePolicy = parse('canvasResizePolicy');
- this.persistentPaths = parse('persistentPaths');
- this.persistentDrops = parse('persistentDrops');
- this.experimentalVK = parse('experimentalVK');
- this.focusCanvas = parse('focusCanvas');
- this.gdnativeLibs = parse('gdnativeLibs');
- this.fileSizes = parse('fileSizes');
- this.args = parse('args');
- this.onExecute = parse('onExecute');
- this.onExit = parse('onExit');
+ this.canvas = parse('canvas', this.canvas);
+ this.executable = parse('executable', this.executable);
+ this.mainPack = parse('mainPack', this.mainPack);
+ this.locale = parse('locale', this.locale);
+ this.canvasResizePolicy = parse('canvasResizePolicy', this.canvasResizePolicy);
+ this.persistentPaths = parse('persistentPaths', this.persistentPaths);
+ this.persistentDrops = parse('persistentDrops', this.persistentDrops);
+ this.experimentalVK = parse('experimentalVK', this.experimentalVK);
+ this.focusCanvas = parse('focusCanvas', this.focusCanvas);
+ this.serviceWorker = parse('serviceWorker', this.serviceWorker);
+ this.gdnativeLibs = parse('gdnativeLibs', this.gdnativeLibs);
+ this.fileSizes = parse('fileSizes', this.fileSizes);
+ this.args = parse('args', this.args);
+ this.onExecute = parse('onExecute', this.onExecute);
+ this.onExit = parse('onExit', this.onExit);
};
/**
diff --git a/platform/javascript/js/engine/engine.js b/platform/javascript/js/engine/engine.js
index 17a8df9e29..d2ba595083 100644
--- a/platform/javascript/js/engine/engine.js
+++ b/platform/javascript/js/engine/engine.js
@@ -189,6 +189,9 @@ const Engine = (function () {
preloader.preloadedFiles.length = 0; // Clear memory
me.rtenv['callMain'](me.config.args);
initPromise = null;
+ if (me.config.serviceWorker && 'serviceWorker' in navigator) {
+ navigator.serviceWorker.register(me.config.serviceWorker);
+ }
resolve();
});
});
diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js
index 662d215443..12d06a8d51 100644
--- a/platform/javascript/js/libs/library_godot_os.js
+++ b/platform/javascript/js/libs/library_godot_os.js
@@ -368,3 +368,58 @@ const GodotEventListeners = {
},
};
mergeInto(LibraryManager.library, GodotEventListeners);
+
+const GodotPWA = {
+
+ $GodotPWA__deps: ['$GodotRuntime', '$GodotEventListeners'],
+ $GodotPWA: {
+ hasUpdate: false,
+
+ updateState: function (cb, reg) {
+ if (!reg) {
+ return;
+ }
+ if (!reg.active) {
+ return;
+ }
+ if (reg.waiting) {
+ GodotPWA.hasUpdate = true;
+ cb();
+ }
+ GodotEventListeners.add(reg, 'updatefound', function () {
+ const installing = reg.installing;
+ GodotEventListeners.add(installing, 'statechange', function () {
+ if (installing.state === 'installed') {
+ GodotPWA.hasUpdate = true;
+ cb();
+ }
+ });
+ });
+ },
+ },
+
+ godot_js_pwa_cb__sig: 'vi',
+ godot_js_pwa_cb: function (p_update_cb) {
+ if ('serviceWorker' in navigator) {
+ const cb = GodotRuntime.get_func(p_update_cb);
+ navigator.serviceWorker.getRegistration().then(GodotPWA.updateState.bind(null, cb));
+ }
+ },
+
+ godot_js_pwa_update__sig: 'i',
+ godot_js_pwa_update: function () {
+ if ('serviceWorker' in navigator && GodotPWA.hasUpdate) {
+ navigator.serviceWorker.getRegistration().then(function (reg) {
+ if (!reg || !reg.waiting) {
+ return;
+ }
+ reg.waiting.postMessage('update');
+ });
+ return 0;
+ }
+ return 1;
+ },
+};
+
+autoAddDeps(GodotPWA, '$GodotPWA');
+mergeInto(LibraryManager.library, GodotPWA);
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index 39eea13ca1..de5ca44f9d 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -45,6 +45,7 @@
#include <emscripten.h>
#include <stdlib.h>
+#include "api/javascript_singleton.h"
#include "godot_js.h"
void OS_JavaScript::alert(const String &p_alert, const String &p_title) {
@@ -203,6 +204,19 @@ void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags
}
}
+void OS_JavaScript::update_pwa_state_callback() {
+ if (OS_JavaScript::get_singleton()) {
+ OS_JavaScript::get_singleton()->pwa_is_waiting = true;
+ }
+ if (JavaScript::get_singleton()) {
+ JavaScript::get_singleton()->emit_signal("pwa_update_available");
+ }
+}
+
+Error OS_JavaScript::pwa_update() {
+ return godot_js_pwa_update() ? FAILED : OK;
+}
+
bool OS_JavaScript::is_userfs_persistent() const {
return idb_available;
}
@@ -226,6 +240,8 @@ OS_JavaScript::OS_JavaScript() {
godot_js_config_locale_get(locale_ptr, 16);
setenv("LANG", locale_ptr, true);
+ godot_js_pwa_cb(&OS_JavaScript::update_pwa_state_callback);
+
if (AudioDriverJavaScript::is_available()) {
#ifdef NO_THREADS
audio_drivers.push_back(memnew(AudioDriverScriptProcessor));
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index 3404fe9dcf..9e272f9aa1 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -45,11 +45,13 @@ class OS_JavaScript : public OS_Unix {
bool idb_is_syncing = false;
bool idb_available = false;
bool idb_needs_sync = false;
+ bool pwa_is_waiting = false;
static void main_loop_callback();
static void file_access_close_callback(const String &p_file, int p_flags);
static void fs_sync_callback();
+ static void update_pwa_state_callback();
protected:
void initialize() override;
@@ -65,6 +67,9 @@ public:
// Override return type to make writing static callbacks less tedious.
static OS_JavaScript *get_singleton();
+ bool pwa_needs_update() const { return pwa_is_waiting; }
+ Error pwa_update();
+
void initialize_joypads() override;
MainLoop *get_main_loop() const override;
diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index e9369fefdd..b4ec7924f6 100644
--- a/platform/linuxbsd/crash_handler_linuxbsd.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -33,7 +33,6 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "main/main.h"
#ifdef DEBUG_ENABLED
@@ -71,10 +70,10 @@ static void handle_crash(int sig) {
}
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
- if (String(VERSION_HASH).length() != 0) {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
+ if (String(VERSION_HASH).is_empty()) {
+ fprintf(stderr, "Engine version: %s\n", VERSION_FULL_NAME);
} else {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
+ fprintf(stderr, "Engine version: %s (%s)\n", VERSION_FULL_NAME, VERSION_HASH);
}
fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 198eacd1f3..bf9c9b1766 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -33,6 +33,7 @@
#ifdef X11_ENABLED
#include "core/config/project_settings.h"
+#include "core/math/math_funcs.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "detect_prime_x11.h"
@@ -1054,6 +1055,67 @@ int DisplayServerX11::screen_get_dpi(int p_screen) const {
return 96;
}
+float DisplayServerX11::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ //invalid screen?
+ ERR_FAIL_INDEX_V(p_screen, get_screen_count(), SCREEN_REFRESH_RATE_FALLBACK);
+
+ //Use xrandr to get screen refresh rate.
+ if (xrandr_ext_ok) {
+ XRRScreenResources *screen_info = XRRGetScreenResources(x11_display, windows[MAIN_WINDOW_ID].x11_window);
+ if (screen_info) {
+ RRMode current_mode = 0;
+ xrr_monitor_info *monitors = nullptr;
+
+ if (xrr_get_monitors) {
+ int count = 0;
+ monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count);
+ ERR_FAIL_INDEX_V(p_screen, count, SCREEN_REFRESH_RATE_FALLBACK);
+ } else {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+
+ bool found_active_mode = false;
+ for (int crtc = 0; crtc < screen_info->ncrtc; crtc++) { // Loop through outputs to find which one is currently outputting.
+ XRRCrtcInfo *monitor_info = XRRGetCrtcInfo(x11_display, screen_info, screen_info->crtcs[crtc]);
+ if (monitor_info->x != monitors[p_screen].x || monitor_info->y != monitors[p_screen].y) { // If X and Y aren't the same as the monitor we're looking for, this isn't the right monitor. Continue.
+ continue;
+ }
+
+ if (monitor_info->mode != None) {
+ current_mode = monitor_info->mode;
+ found_active_mode = true;
+ break;
+ }
+ }
+
+ if (found_active_mode) {
+ for (int mode = 0; mode < screen_info->nmode; mode++) {
+ XRRModeInfo m_info = screen_info->modes[mode];
+ if (m_info.id == current_mode) {
+ // Snap to nearest 0.01 to stay consistent with other platforms.
+ return Math::snapped((float)m_info.dotClock / ((float)m_info.hTotal * (float)m_info.vTotal), 0.01);
+ }
+ }
+ }
+
+ ERR_PRINT("An error occured while trying to get the screen refresh rate."); // We should have returned the refresh rate by now. An error must have occured.
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ } else {
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+ }
+ }
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+}
+
bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -1773,7 +1835,7 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
Hints hints;
Atom property;
hints.flags = 2;
- hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1;
+ hints.decorations = wd.borderless ? 0 : 1;
property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
if (property != None) {
XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
@@ -1825,6 +1887,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
} break;
+ case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
//Remove full-screen
wd.fullscreen = false;
@@ -1877,6 +1940,7 @@ void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
} break;
+ case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
wd.last_position_before_fs = wd.position;
@@ -2417,7 +2481,7 @@ Key DisplayServerX11::keyboard_get_keycode_from_physical(Key p_keycode) const {
Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
unsigned int xkeycode = KeyMappingX11::get_xlibcode(keycode_no_mod);
KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, 0, 0);
- if (xkeysym >= 'a' && xkeysym <= 'z') {
+ if (is_ascii_lower_case(xkeysym)) {
xkeysym -= ('a' - 'A');
}
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index c17e120db5..2d07361deb 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -303,6 +303,7 @@ public:
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
#if defined(DBUS_ENABLED)
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 5eda42fea6..8e963238e3 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -333,8 +333,9 @@ void JoypadLinux::open_joypad(const char *p_path) {
}
// Check if the device supports basic gamepad events
- if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
- test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit))) {
+ bool has_abs_left = (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit));
+ bool has_abs_right = (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit));
+ if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && (has_abs_left || has_abs_right))) {
close(fd);
return;
}
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index 8ba106d1c2..d72a75af04 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -6,14 +6,21 @@ from platform_methods import run_in_subprocess
import platform_osx_builders
files = [
- "crash_handler_osx.mm",
"os_osx.mm",
+ "godot_application.mm",
+ "godot_application_delegate.mm",
+ "crash_handler_osx.mm",
+ "osx_terminal_logger.mm",
"display_server_osx.mm",
+ "godot_content_view.mm",
+ "godot_window_delegate.mm",
+ "godot_window.mm",
+ "key_mapping_osx.mm",
"godot_main_osx.mm",
"dir_access_osx.mm",
"joypad_osx.cpp",
"vulkan_context_osx.mm",
- "gl_manager_osx.mm",
+ "gl_manager_osx_legacy.mm",
]
prog = env.add_program("#bin/godot", files)
diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm
index 16be941308..3e640b3bf3 100644
--- a/platform/osx/crash_handler_osx.mm
+++ b/platform/osx/crash_handler_osx.mm
@@ -33,7 +33,6 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "main/main.h"
#include <string.h>
@@ -94,10 +93,10 @@ static void handle_crash(int sig) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
- if (String(VERSION_HASH).length() != 0) {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
+ if (String(VERSION_HASH).is_empty()) {
+ fprintf(stderr, "Engine version: %s\n", VERSION_FULL_NAME);
} else {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
+ fprintf(stderr, "Engine version: %s (%s)\n", VERSION_FULL_NAME, VERSION_HASH);
}
fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
char **strings = backtrace_symbols(bt_buffer, size);
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index c67791b340..0ff93bedb4 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -78,14 +78,16 @@ def configure(env):
env["osxcross"] = True
if env["arch"] == "arm64":
- print("Building for macOS 10.15+, platform arm64.")
- env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
- env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15"])
+ print("Building for macOS 11.0+, platform arm64.")
+ env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.0"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=11.0"])
else:
print("Building for macOS 10.12+, platform x86_64.")
env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
+ env.Append(CCFLAGS=["-fobjc-arc"])
+
if not "osxcross" in env: # regular native build
if env["macports_clang"] != "no":
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
@@ -188,6 +190,8 @@ def configure(env):
env.Append(CCFLAGS=["-Wno-deprecated-declarations"]) # Disable deprecation warnings
env.Append(LINKFLAGS=["-framework", "OpenGL"])
+ env.Append(LINKFLAGS=["-rpath", "@executable_path/../Frameworks", "-rpath", "@executable_path"])
+
if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED"])
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "QuartzCore", "-framework", "IOSurface"])
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index 719a45c2a8..eaf03d298e 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -37,13 +37,13 @@
#include "servers/display_server.h"
#if defined(GLES3_ENABLED)
-#include "gl_manager_osx.h"
-#endif
+#include "gl_manager_osx_legacy.h"
+#endif // GLES3_ENABLED
#if defined(VULKAN_ENABLED)
#include "drivers/vulkan/rendering_device_vulkan.h"
#include "platform/osx/vulkan_context_osx.h"
-#endif
+#endif // VULKAN_ENABLED
#include <AppKit/AppKit.h>
#include <AppKit/NSCursor.h>
@@ -59,27 +59,8 @@ class DisplayServerOSX : public DisplayServer {
_THREAD_SAFE_CLASS_
public:
- void _send_event(NSEvent *p_event);
- NSMenu *_get_dock_menu() const;
- void _menu_callback(id p_sender);
-
-#if defined(GLES3_ENABLED)
- GLManager_OSX *gl_manager = nullptr;
-#endif
-#if defined(VULKAN_ENABLED)
- VulkanContextOSX *context_vulkan = nullptr;
- RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
-#endif
-
- const NSMenu *_get_menu_root(const String &p_menu_root) const;
- NSMenu *_get_menu_root(const String &p_menu_root);
-
- NSMenu *apple_menu = nullptr;
- NSMenu *dock_menu = nullptr;
- Map<String, NSMenu *> submenu;
-
struct KeyEvent {
- WindowID window_id;
+ WindowID window_id = INVALID_WINDOW_ID;
unsigned int osx_state = false;
bool pressed = false;
bool echo = false;
@@ -89,20 +70,6 @@ public:
uint32_t unicode = 0;
};
- struct WarpEvent {
- NSTimeInterval timestamp;
- NSPoint delta;
- };
-
- List<WarpEvent> warp_events;
- NSTimeInterval last_warp = 0;
- bool ignore_warp = false;
-
- float display_max_scale = 1.f;
-
- Vector<KeyEvent> key_event_buffer;
- int key_event_pos;
-
struct WindowData {
id window_delegate;
id window_object;
@@ -116,8 +83,6 @@ public:
Size2i max_size;
Size2i size;
- bool mouse_down_control = false;
-
bool im_active = false;
Size2i im_position;
@@ -140,49 +105,102 @@ public:
bool no_focus = false;
};
+private:
+#if defined(GLES3_ENABLED)
+ GLManager_OSX *gl_manager = nullptr;
+#endif
+#if defined(VULKAN_ENABLED)
+ VulkanContextOSX *context_vulkan = nullptr;
+ RenderingDeviceVulkan *rendering_device_vulkan = nullptr;
+#endif
+ String rendering_driver;
+
+ NSMenu *apple_menu = nullptr;
+ NSMenu *dock_menu = nullptr;
+ Map<String, NSMenu *> submenu;
+
+ struct WarpEvent {
+ NSTimeInterval timestamp;
+ NSPoint delta;
+ };
+ List<WarpEvent> warp_events;
+ NSTimeInterval last_warp = 0;
+ bool ignore_warp = false;
+
+ Vector<KeyEvent> key_event_buffer;
+ int key_event_pos = 0;
+
Point2i im_selection;
String im_text;
- Map<WindowID, WindowData> windows;
+ CGEventSourceRef event_source;
+ MouseMode mouse_mode = MOUSE_MODE_VISIBLE;
+ MouseButton last_button_state = MouseButton::NONE;
- WindowID last_focused_window = INVALID_WINDOW_ID;
+ bool drop_events = false;
+ bool in_dispatch_input_event = false;
+
+ struct LayoutInfo {
+ String name;
+ String code;
+ };
+ Vector<LayoutInfo> kbd_layouts;
+ int current_layout = 0;
+ bool keyboard_layout_dirty = true;
+ WindowID last_focused_window = INVALID_WINDOW_ID;
WindowID window_id_counter = MAIN_WINDOW_ID;
+ float display_max_scale = 1.f;
+ Point2i origin;
+ bool displays_arrangement_dirty = true;
- WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
- void _update_window(WindowData p_wd);
- void _send_window_event(const WindowData &wd, WindowEvent p_event);
- static void _dispatch_input_events(const Ref<InputEvent> &p_event);
- void _dispatch_input_event(const Ref<InputEvent> &p_event);
- WindowID _find_window_id(id p_window);
+ CursorShape cursor_shape = CURSOR_ARROW;
+ NSCursor *cursors[CURSOR_MAX];
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ Map<WindowID, WindowData> windows;
+
+ const NSMenu *_get_menu_root(const String &p_menu_root) const;
+ NSMenu *_get_menu_root(const String &p_menu_root);
+ WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect);
+ void _update_window_style(WindowData p_wd);
void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
+ void _update_displays_arrangement();
Point2i _get_screens_origin() const;
Point2i _get_native_screen_position(int p_screen) const;
+ static void _displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info);
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
void _push_input(const Ref<InputEvent> &p_event);
void _process_key_events();
- void _release_pressed_events();
+ void _update_keyboard_layouts();
+ static void _keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info);
- String rendering_driver;
+ static NSCursor *_cursor_from_selector(SEL p_selector, SEL p_fallback = nil);
- id autoreleasePool;
- CGEventSourceRef eventSource;
+public:
+ NSMenu *get_dock_menu() const;
+ void menu_callback(id p_sender);
- CursorShape cursor_shape;
- NSCursor *cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant>> cursors_cache;
+ bool has_window(WindowID p_window) const;
+ WindowData &get_window(WindowID p_window);
- MouseMode mouse_mode;
- Point2i last_mouse_pos;
- MouseButton last_button_state = MouseButton::NONE;
+ void send_event(NSEvent *p_event);
+ void send_window_event(const WindowData &p_wd, WindowEvent p_event);
+ void release_pressed_events();
+ void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) const;
+ void update_mouse_pos(WindowData &p_wd, NSPoint p_location_in_window);
+ void push_to_key_event_buffer(const KeyEvent &p_event);
+ void update_im_text(const Point2i &p_selection, const String &p_text);
+ void set_last_focused_window(WindowID p_window);
- bool window_focused;
- bool drop_events;
- bool in_dispatch_input_event = false;
+ void window_update(WindowID p_window);
+ void window_destroy(WindowID p_window);
+ void window_resize(WindowID p_window, int p_width, int p_height);
-public:
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
@@ -216,8 +234,10 @@ public:
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;
+ bool update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp);
virtual void mouse_warp_to_position(const Point2i &p_to) override;
virtual Point2i mouse_get_position() const override;
+ void mouse_set_button_state(MouseButton p_state);
virtual MouseButton mouse_get_button_state() const override;
virtual void clipboard_set(const String &p_text) override;
@@ -230,6 +250,7 @@ public:
virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual float screen_get_max_scale() const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Vector<int> get_window_list() const override;
@@ -296,6 +317,7 @@ public:
virtual Point2i ime_get_selection() const override;
virtual String ime_get_text() const override;
+ void cursor_update_shape();
virtual void cursor_set_shape(CursorShape p_shape) override;
virtual CursorShape cursor_get_shape() const override;
virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index f7add5b688..2691664b96 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -30,6 +30,11 @@
#include "display_server_osx.h"
+#include "godot_content_view.h"
+#include "godot_menu_item.h"
+#include "godot_window.h"
+#include "godot_window_delegate.h"
+#include "key_mapping_osx.h"
#include "os_osx.h"
#include "core/io/marshalls.h"
@@ -47,217 +52,122 @@
#if defined(GLES3_ENABLED)
#include "drivers/gles3/rasterizer_gles3.h"
-
-#import <AppKit/NSOpenGLView.h>
#endif
#if defined(VULKAN_ENABLED)
#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
-
-#include <QuartzCore/CAMetalLayer.h>
-#endif
-
-#ifndef NSAppKitVersionNumber10_14
-#define NSAppKitVersionNumber10_14 1671
#endif
-#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
-
-static bool ignore_momentum_scroll = false;
-
-static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) {
- r_state->set_shift_pressed((p_osx_state & NSEventModifierFlagShift));
- r_state->set_ctrl_pressed((p_osx_state & NSEventModifierFlagControl));
- r_state->set_alt_pressed((p_osx_state & NSEventModifierFlagOption));
- r_state->set_meta_pressed((p_osx_state & NSEventModifierFlagCommand));
-}
-
-static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow) {
- const NSRect contentRect = [p_wd.window_view frame];
- const float scale = DS_OSX->screen_get_max_scale();
- p_wd.mouse_pos.x = p_locationInWindow.x * scale;
- p_wd.mouse_pos.y = (contentRect.size.height - p_locationInWindow.y) * scale;
- DS_OSX->last_mouse_pos = p_wd.mouse_pos;
- Input::get_singleton()->set_mouse_position(p_wd.mouse_pos);
- return p_wd.mouse_pos;
-}
-
-static void _push_to_key_event_buffer(const DisplayServerOSX::KeyEvent &p_event) {
- Vector<DisplayServerOSX::KeyEvent> &buffer = DS_OSX->key_event_buffer;
- if (DS_OSX->key_event_pos >= buffer.size()) {
- buffer.resize(1 + DS_OSX->key_event_pos);
- }
- buffer.write[DS_OSX->key_event_pos++] = p_event;
-}
-
-static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
- if ([NSCursor respondsToSelector:selector]) {
- id object = [NSCursor performSelector:selector];
- if ([object isKindOfClass:[NSCursor class]]) {
- return object;
+const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
+ const NSMenu *menu = nullptr;
+ if (p_menu_root == "") {
+ // Main menu.
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (submenu.has(p_menu_root)) {
+ menu = submenu[p_menu_root];
}
}
- if (fallback) {
- // Fallback should be a reasonable default, no need to check.
- return [NSCursor performSelector:fallback];
- }
- return [NSCursor arrowCursor];
-}
-
-/*************************************************************************/
-/* GlobalMenuItem */
-/*************************************************************************/
-
-@interface GlobalMenuItem : NSObject {
-@public
- Callable callback;
- Variant meta;
- bool checkable;
-}
-
-@end
-
-@implementation GlobalMenuItem
-@end
-
-/*************************************************************************/
-/* GodotWindowDelegate */
-/*************************************************************************/
-
-@interface GodotWindowDelegate : NSObject {
- DisplayServerOSX::WindowID window_id;
-}
-
-- (void)windowWillClose:(NSNotification *)notification;
-- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
-
-@end
-
-@implementation GodotWindowDelegate
-
-- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
- window_id = wid;
-}
-
-- (BOOL)windowShouldClose:(id)sender {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return YES;
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return nullptr;
}
- DS_OSX->_send_window_event(DS_OSX->windows[window_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- return NO;
+ return menu;
}
-- (void)windowWillClose:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
- }
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- while (wd.transient_children.size()) {
- DS_OSX->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID);
- }
-
- if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
- DS_OSX->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
- }
-
-#if defined(GLES3_ENABLED)
- if (DS_OSX->gl_manager) {
- DS_OSX->gl_manager->window_destroy(window_id);
- }
-#endif
-#ifdef VULKAN_ENABLED
- if (DS_OSX->context_vulkan) {
- DS_OSX->context_vulkan->window_destroy(window_id);
+NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
+ NSMenu *menu = nullptr;
+ if (p_menu_root == "") {
+ // Main menu.
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (!submenu.has(p_menu_root)) {
+ NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
+ submenu[p_menu_root] = n_menu;
+ }
+ menu = submenu[p_menu_root];
}
-#endif
-
- DS_OSX->windows.erase(window_id);
-}
-
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return nullptr;
}
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- wd.fullscreen = true;
-
- [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
- [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
- // Force window resize event.
- [self windowDidResize:notification];
+ return menu;
}
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
- }
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- wd.fullscreen = false;
-
- const float scale = DS_OSX->screen_get_max_scale();
- if (wd.min_size != Size2i()) {
- Size2i size = wd.min_size / scale;
- [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- }
- if (wd.max_size != Size2i()) {
- Size2i size = wd.max_size / scale;
- [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- }
-
- if (wd.resize_disabled) {
- [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
- }
-
- if (wd.on_top) {
- [wd.window_object setLevel:NSFloatingWindowLevel];
- }
- // Force window resize event.
- [self windowDidResize:notification];
-}
+DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) {
+ WindowID id;
+ const float scale = screen_get_max_scale();
+ {
+ WindowData wd;
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
- if (!DisplayServerOSX::get_singleton()) {
- return;
- }
+ wd.window_delegate = [[GodotWindowDelegate alloc] init];
+ ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate");
+ [wd.window_delegate setWindowID:window_id_counter];
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ Point2i position = p_rect.position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value.
+ position.y *= -1;
+ position += _get_screens_origin();
- CGFloat newBackingScaleFactor = [wd.window_object backingScaleFactor];
- CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
+ // initWithContentRect uses bottom-left corner of the window’s frame as origin.
+ wd.window_object = [[GodotWindow alloc]
+ initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale)
+ styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window");
+ [wd.window_object setWindowID:window_id_counter];
- if (newBackingScaleFactor != oldBackingScaleFactor) {
- //Set new display scale and window size
- const float scale = DS_OSX->screen_get_max_scale();
- const NSRect contentRect = [wd.window_view frame];
+ wd.window_view = [[GodotContentView alloc] init];
+ ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view");
+ [wd.window_view setWindowID:window_id_counter];
+ [wd.window_view setWantsLayer:TRUE];
- wd.size.width = contentRect.size.width * scale;
- wd.size.height = contentRect.size.height * scale;
+ [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ [wd.window_object setContentView:wd.window_view];
+ [wd.window_object setDelegate:wd.window_delegate];
+ [wd.window_object setAcceptsMouseMovedEvents:YES];
+ [wd.window_object setRestorable:NO];
+ [wd.window_object setColorSpace:[NSColorSpace sRGBColorSpace]];
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE);
+ if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) {
+ [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
+ }
CALayer *layer = [wd.window_view layer];
if (layer) {
layer.contentsScale = scale;
}
- //Force window resize event
- [self windowDidResize:notification];
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
+ }
+#endif
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ Error err = gl_manager->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context");
+ }
+#endif
+ id = window_id_counter++;
+ windows[id] = wd;
}
-}
-- (void)windowDidResize:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
- }
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ WindowData &wd = windows[id];
+ window_set_mode(p_mode, id);
const NSRect contentRect = [wd.window_view frame];
-
- const float scale = DS_OSX->screen_get_max_scale();
wd.size.width = contentRect.size.width * scale;
wd.size.height = contentRect.size.height * scale;
@@ -267,1204 +177,448 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
}
#if defined(GLES3_ENABLED)
- if (DS_OSX->gl_manager) {
- DS_OSX->gl_manager->window_resize(window_id, wd.size.width, wd.size.height);
+ if (gl_manager) {
+ gl_manager->window_resize(id, wd.size.width, wd.size.height);
}
#endif
#if defined(VULKAN_ENABLED)
- if (DS_OSX->context_vulkan) {
- DS_OSX->context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
+ if (context_vulkan) {
+ context_vulkan->window_resize(id, wd.size.width, wd.size.height);
}
#endif
- if (!wd.rect_changed_callback.is_null()) {
- Variant size = Rect2i(DS_OSX->window_get_position(window_id), DS_OSX->window_get_size(window_id));
- Variant *sizep = &size;
- Variant ret;
- Callable::CallError ce;
- wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
- }
+ return id;
}
-- (void)windowDidMove:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
- }
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- DS_OSX->_release_pressed_events();
+void DisplayServerOSX::_update_window_style(WindowData p_wd) {
+ bool borderless_full = false;
- if (!wd.rect_changed_callback.is_null()) {
- Variant size = Rect2i(DS_OSX->window_get_position(window_id), DS_OSX->window_get_size(window_id));
- Variant *sizep = &size;
- Variant ret;
- Callable::CallError ce;
- wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
- }
-}
+ if (p_wd.borderless) {
+ NSRect frameRect = [p_wd.window_object frame];
+ NSRect screenRect = [[p_wd.window_object screen] frame];
-- (void)windowDidBecomeKey:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
+ // Check if our window covers up the screen.
+ if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
+ frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
+ borderless_full = true;
+ }
}
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CAPTURED) {
- const NSRect contentRect = [wd.window_view frame];
- NSRect pointInWindowRect = NSMakeRect(contentRect.size.width / 2, contentRect.size.height / 2, 0, 0);
- NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
- CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
- CGWarpMouseCursorPosition(lMouseWarpPos);
+ if (borderless_full) {
+ // If the window covers up the screen set the level to above the main menu and hide on deactivate.
+ [p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
+ [p_wd.window_object setHidesOnDeactivate:YES];
} else {
- _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
- Input::get_singleton()->set_mouse_position(wd.mouse_pos);
- }
-
- DS_OSX->window_focused = true;
- DS_OSX->last_focused_window = window_id;
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
-}
-
-- (void)windowDidResignKey:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
+ // Reset these when our window is not a borderless window that covers up the screen.
+ if (p_wd.on_top && !p_wd.fullscreen) {
+ [p_wd.window_object setLevel:NSFloatingWindowLevel];
+ } else {
+ [p_wd.window_object setLevel:NSNormalWindowLevel];
+ }
+ [p_wd.window_object setHidesOnDeactivate:NO];
}
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- DS_OSX->window_focused = false;
-
- DS_OSX->_release_pressed_events();
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
}
-- (void)windowDidMiniaturize:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
- return;
- }
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- DS_OSX->window_focused = false;
-
- DS_OSX->_release_pressed_events();
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
-}
+void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
-- (void)windowDidDeminiaturize:(NSNotification *)notification {
- if (!DS_OSX || !DS_OSX->windows.has(window_id)) {
+ if (!OS::get_singleton()->is_layered_allowed()) {
return;
}
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- DS_OSX->window_focused = true;
- DS_OSX->last_focused_window = window_id;
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
-}
-
-@end
-
-/*************************************************************************/
-/* GodotContentView */
-/*************************************************************************/
-
+ if (wd.layered_window != p_enabled) {
+ if (p_enabled) {
+ [wd.window_object setBackgroundColor:[NSColor clearColor]];
+ [wd.window_object setOpaque:NO];
+ [wd.window_object setHasShadow:NO];
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ [layer setBackgroundColor:[NSColor clearColor].CGColor];
+ [layer setOpaque:NO];
+ }
#if defined(GLES3_ENABLED)
-@interface GodotContentView : NSOpenGLView <NSTextInputClient> {
-#else
-@interface GodotContentView : NSView <NSTextInputClient> {
-#endif
-
- DisplayServerOSX::WindowID window_id;
- NSTrackingArea *trackingArea;
- NSMutableAttributedString *markedText;
- bool imeInputEventInProgress;
-}
-
-- (void)cancelComposition;
-- (CALayer *)makeBackingLayer;
-- (BOOL)wantsUpdateLayer;
-- (void)updateLayer;
-- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
-
-@end
-
-@implementation GodotContentView
-
-- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
- window_id = wid;
-}
-
-+ (void)initialize {
- if (self == [GodotContentView class]) {
- // nothing left to do here at the moment..
- }
-}
-
-- (CALayer *)makeBackingLayer {
-#if defined(VULKAN_ENABLED)
- if (DS_OSX->context_vulkan) {
- CALayer *layer = [[CAMetalLayer class] layer];
- return layer;
- }
+ if (gl_manager) {
+ gl_manager->window_set_per_pixel_transparency_enabled(p_window, true);
+ }
#endif
- return [super makeBackingLayer];
-}
-
-- (void)updateLayer {
+ wd.layered_window = true;
+ } else {
+ [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ [wd.window_object setOpaque:YES];
+ [wd.window_object setHasShadow:YES];
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ [layer setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1].CGColor];
+ [layer setOpaque:YES];
+ }
#if defined(GLES3_ENABLED)
- if (DS_OSX->gl_manager) {
- DS_OSX->gl_manager->window_update(window_id);
- }
+ if (gl_manager) {
+ gl_manager->window_set_per_pixel_transparency_enabled(p_window, false);
+ }
#endif
-#if defined(VULKAN_ENABLED)
- if (DS_OSX->context_vulkan) {
- [super updateLayer];
+ wd.layered_window = false;
+ }
+ NSRect frameRect = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:YES];
+ [wd.window_object setFrame:frameRect display:YES];
}
-#endif
}
-- (BOOL)wantsUpdateLayer {
- return YES;
-}
+void DisplayServerOSX::_update_displays_arrangement() {
+ origin = Point2i();
-- (id)init {
- self = [super init];
- trackingArea = nil;
- imeInputEventInProgress = false;
- [self updateTrackingAreas];
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
-#else
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
-#endif
- markedText = [[NSMutableAttributedString alloc] init];
- return self;
-}
-
-- (void)dealloc {
- [trackingArea release];
- [markedText release];
- [super dealloc];
+ for (int i = 0; i < get_screen_count(); i++) {
+ Point2i position = _get_native_screen_position(i);
+ if (position.x < origin.x) {
+ origin.x = position.x;
+ }
+ if (position.y > origin.y) {
+ origin.y = position.y;
+ }
+ }
+ displays_arrangement_dirty = false;
}
-static const NSRange kEmptyRange = { NSNotFound, 0 };
-
-- (BOOL)hasMarkedText {
- return (markedText.length > 0);
-}
+Point2i DisplayServerOSX::_get_screens_origin() const {
+ // Returns the native top-left screen coordinate of the smallest rectangle
+ // that encompasses all screens. Needed in get_screen_position(),
+ // window_get_position, and window_set_position()
+ // to convert between OS X native screen coordinates and the ones expected by Godot.
-- (NSRange)markedRange {
- return NSMakeRange(0, markedText.length);
-}
+ if (displays_arrangement_dirty) {
+ const_cast<DisplayServerOSX *>(this)->_update_displays_arrangement();
+ }
-- (NSRange)selectedRange {
- return kEmptyRange;
+ return origin;
}
-- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- [markedText initWithAttributedString:aString];
- } else {
- [markedText initWithString:aString];
- }
- if (markedText.length == 0) {
- [self unmarkText];
- return;
+Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const {
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
+ // Return the top-left corner of the screen, for OS X the y starts at the bottom.
+ return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * screen_get_max_scale();
}
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- if (wd.im_active) {
- imeInputEventInProgress = true;
- DS_OSX->im_text.parse_utf8([[markedText mutableString] UTF8String]);
- DS_OSX->im_selection = Point2i(selectedRange.location, selectedRange.length);
+ return Point2i();
+}
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+void DisplayServerOSX::_displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ ds->displays_arrangement_dirty = true;
}
}
-- (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector]) {
- [self performSelector:aSelector];
- }
+void DisplayServerOSX::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerOSX *)(get_singleton()))->_dispatch_input_event(p_event);
}
-- (void)unmarkText {
- imeInputEventInProgress = false;
- [[markedText mutableString] setString:@""];
+void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+ _THREAD_SAFE_METHOD_
+ if (!in_dispatch_input_event) {
+ in_dispatch_input_event = true;
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
- if (wd.im_active) {
- DS_OSX->im_text = String();
- DS_OSX->im_selection = Point2i();
+ Ref<InputEventFromWindow> event_from_window = p_event;
+ if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
+ // Send to a window.
+ if (windows.has(event_from_window->get_window_id())) {
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ } else {
+ // Send to all windows.
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ in_dispatch_input_event = false;
}
}
-- (NSArray *)validAttributesForMarkedText {
- return [NSArray array];
-}
-
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- return nil;
-}
-
-- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
- return 0;
-}
-
-- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NSMakeRect(0, 0, 0, 0));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- const NSRect contentRect = [wd.window_view frame];
- const float scale = DS_OSX->screen_get_max_scale();
- NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / scale, contentRect.size.height - (wd.im_position.y / scale) - 1, 0, 0);
- NSPoint pointOnScreen = [wd.window_object convertRectToScreen:pointInWindowRect].origin;
-
- return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
+void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEvent> ev = p_event;
+ Input::get_singleton()->parse_input_event(ev);
}
-- (void)cancelComposition {
- [self unmarkText];
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
-}
+void DisplayServerOSX::_process_key_events() {
+ Ref<InputEventKey> k;
+ for (int i = 0; i < key_event_pos; i++) {
+ const KeyEvent &ke = key_event_buffer[i];
+ if (ke.raw) {
+ // Non IME input - no composite characters, pass events as is.
+ k.instantiate();
-- (void)insertText:(id)aString {
- [self insertText:aString replacementRange:NSMakeRange(0, 0)];
-}
+ k->set_window_id(ke.window_id);
+ get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode((Key)ke.physical_keycode);
+ k->set_unicode(ke.unicode);
-- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
- NSEvent *event = [NSApp currentEvent];
+ _push_input(k);
+ } else {
+ // IME input.
+ if ((i == 0 && ke.keycode == Key::NONE) || (i > 0 && key_event_buffer[i - 1].keycode == Key::NONE)) {
+ k.instantiate();
- NSString *characters;
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- characters = [aString string];
- } else {
- characters = (NSString *)aString;
- }
+ k->set_window_id(ke.window_id);
+ get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(Key::NONE);
+ k->set_physical_keycode(Key::NONE);
+ k->set_unicode(ke.unicode);
- NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
- NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
- if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
- [self cancelComposition];
- return;
- }
+ _push_input(k);
+ }
+ if (ke.keycode != Key::NONE) {
+ k.instantiate();
- Char16String text;
- text.resize([characters length] + 1);
- [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+ k->set_window_id(ke.window_id);
+ get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode((Key)ke.physical_keycode);
- String u32text;
- u32text.parse_utf16(text.ptr(), text.length());
+ if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == Key::NONE) {
+ k->set_unicode(key_event_buffer[i + 1].unicode);
+ }
- for (int i = 0; i < u32text.length(); i++) {
- const char32_t codepoint = u32text[i];
- if ((codepoint & 0xFF00) == 0xF700) {
- continue;
+ _push_input(k);
+ }
}
-
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = false;
- ke.raw = false; // IME input event
- ke.keycode = Key::NONE;
- ke.physical_keycode = Key::NONE;
- ke.unicode = codepoint;
-
- _push_to_key_event_buffer(ke);
}
- [self cancelComposition];
-}
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
+ key_event_pos = 0;
}
-- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
-}
+void DisplayServerOSX::_update_keyboard_layouts() {
+ kbd_layouts.clear();
+ current_layout = 0;
-- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
- ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NO);
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
+ NSString *cur_name = (__bridge NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
+ CFRelease(cur_source);
- if (!wd.drop_files_callback.is_null()) {
- Vector<String> files;
- NSPasteboard *pboard = [sender draggingPasteboard];
+ // Enum IME layouts.
+ NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
+ NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false);
+ for (NSUInteger i = 0; i < [list_ime count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
-#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
- NSArray *items = pboard.pasteboardItems;
- for (NSPasteboardItem *item in items) {
- NSString *path = [item stringForType:NSPasteboardTypeFileURL];
- NSString *ns = [NSURL URLWithString:path].path;
- char *utfs = strdup([ns UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
- files.push_back(ret);
- }
-#else
- NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
- for (NSString *ns in filenames) {
- char *utfs = strdup([ns UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
- files.push_back(ret);
- }
-#endif
+ NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
- Variant v = files;
- Variant *vp = &v;
- Variant ret;
- Callable::CallError ce;
- wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
+ }
}
- return NO;
-}
+ // Enum plain keyboard layouts.
+ NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
+ NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false);
+ for (NSUInteger i = 0; i < [list_kbd count]; i++) {
+ LayoutInfo ly;
+ NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ ly.name.parse_utf8([name UTF8String]);
-- (BOOL)isOpaque {
- return YES;
-}
+ NSArray *langs = (__bridge NSArray *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
+ ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
+ kbd_layouts.push_back(ly);
-- (BOOL)canBecomeKeyView {
- if (DS_OSX->windows.has(window_id)) {
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
- if (wd.no_focus) {
- return NO;
+ if ([name isEqualToString:cur_name]) {
+ current_layout = kbd_layouts.size() - 1;
}
}
- return YES;
-}
-- (BOOL)acceptsFirstResponder {
- return YES;
-}
-
-- (void)cursorUpdate:(NSEvent *)event {
- DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
- DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
- DS_OSX->cursor_set_shape(p_shape);
+ keyboard_layout_dirty = false;
}
-static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, MouseButton index, MouseButton mask, bool pressed) {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- if (pressed) {
- DS_OSX->last_button_state |= mask;
- } else {
- DS_OSX->last_button_state &= (MouseButton)~mask;
- }
-
- Ref<InputEventMouseButton> mb;
- mb.instantiate();
- mb->set_window_id(window_id);
- const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow]);
- _get_key_modifier_state([event modifierFlags], mb);
- mb->set_button_index(index);
- mb->set_pressed(pressed);
- mb->set_position(pos);
- mb->set_global_position(pos);
- mb->set_button_mask(DS_OSX->last_button_state);
- if (index == MouseButton::LEFT && pressed) {
- mb->set_double_click([event clickCount] == 2);
+void DisplayServerOSX::_keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ ds->keyboard_layout_dirty = true;
}
-
- Input::get_singleton()->parse_input_event(mb);
}
-- (void)mouseDown:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- if (([event modifierFlags] & NSEventModifierFlagControl)) {
- wd.mouse_down_control = true;
- _mouseDownEvent(window_id, event, MouseButton::RIGHT, MouseButton::MASK_RIGHT, true);
- } else {
- wd.mouse_down_control = false;
- _mouseDownEvent(window_id, event, MouseButton::LEFT, MouseButton::MASK_LEFT, true);
+NSCursor *DisplayServerOSX::_cursor_from_selector(SEL p_selector, SEL p_fallback) {
+ if ([NSCursor respondsToSelector:p_selector]) {
+ id object = [NSCursor performSelector:p_selector];
+ if ([object isKindOfClass:[NSCursor class]]) {
+ return object;
+ }
}
-}
-
-- (void)mouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)mouseUp:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- if (wd.mouse_down_control) {
- _mouseDownEvent(window_id, event, MouseButton::RIGHT, MouseButton::MASK_RIGHT, false);
- } else {
- _mouseDownEvent(window_id, event, MouseButton::LEFT, MouseButton::MASK_LEFT, false);
+ if (p_fallback) {
+ // Fallback should be a reasonable default, no need to check.
+ return [NSCursor performSelector:p_fallback];
}
+ return [NSCursor arrowCursor];
}
-- (void)mouseMoved:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
- NSPoint mpos = [event locationInWindow];
+NSMenu *DisplayServerOSX::get_dock_menu() const {
+ return dock_menu;
+}
- if (DS_OSX->ignore_warp) {
- // Discard late events, before warp
- if (([event timestamp]) < DS_OSX->last_warp) {
- return;
- }
- DS_OSX->ignore_warp = false;
+void DisplayServerOSX::menu_callback(id p_sender) {
+ if (![p_sender representedObject]) {
return;
}
- if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED || DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED_HIDDEN) {
- // Discard late events
- if (([event timestamp]) < DS_OSX->last_warp) {
- return;
- }
+ GodotMenuItem *value = [p_sender representedObject];
- // Warp affects next event delta, subtract previous warp deltas
- List<DisplayServerOSX::WarpEvent>::Element *F = DS_OSX->warp_events.front();
- while (F) {
- if (F->get().timestamp < [event timestamp]) {
- List<DisplayServerOSX::WarpEvent>::Element *E = F;
- delta.x -= E->get().delta.x;
- delta.y -= E->get().delta.y;
- F = F->next();
- DS_OSX->warp_events.erase(E);
+ if (value) {
+ if (value->checkable) {
+ if ([p_sender state] == NSControlStateValueOff) {
+ [p_sender setState:NSControlStateValueOn];
} else {
- F = F->next();
+ [p_sender setState:NSControlStateValueOff];
}
}
- // Confine mouse position to the window, and update delta
- NSRect frame = [wd.window_object frame];
- NSPoint conf_pos = mpos;
- conf_pos.x = CLAMP(conf_pos.x + delta.x, 0.f, frame.size.width);
- conf_pos.y = CLAMP(conf_pos.y - delta.y, 0.f, frame.size.height);
- delta.x = conf_pos.x - mpos.x;
- delta.y = mpos.y - conf_pos.y;
- mpos = conf_pos;
-
- // Move mouse cursor
- NSRect pointInWindowRect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0);
- conf_pos = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
- conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y;
- CGWarpMouseCursorPosition(conf_pos);
-
- // Save warp data
- DS_OSX->last_warp = [[NSProcessInfo processInfo] systemUptime];
- DisplayServerOSX::WarpEvent ev;
- ev.timestamp = DS_OSX->last_warp;
- ev.delta = delta;
- DS_OSX->warp_events.push_back(ev);
- }
-
- Ref<InputEventMouseMotion> mm;
- mm.instantiate();
-
- mm->set_window_id(window_id);
- mm->set_button_mask(DS_OSX->last_button_state);
- const Vector2i pos = _get_mouse_pos(wd, mpos);
- mm->set_position(pos);
- mm->set_pressure([event pressure]);
- if ([event subtype] == NSEventSubtypeTabletPoint) {
- const NSPoint p = [event tilt];
- mm->set_tilt(Vector2(p.x, p.y));
+ if (value->callback != Callable()) {
+ Variant tag = value->meta;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ value->callback.call((const Variant **)&tagp, 1, ret, ce);
+ }
}
- mm->set_global_position(pos);
- mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
- const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * DS_OSX->screen_get_max_scale();
- mm->set_relative(relativeMotion);
- _get_key_modifier_state([event modifierFlags], mm);
-
- Input::get_singleton()->parse_input_event(mm);
-}
-
-- (void)rightMouseDown:(NSEvent *)event {
- _mouseDownEvent(window_id, event, MouseButton::RIGHT, MouseButton::MASK_RIGHT, true);
-}
-
-- (void)rightMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)rightMouseUp:(NSEvent *)event {
- _mouseDownEvent(window_id, event, MouseButton::RIGHT, MouseButton::MASK_RIGHT, false);
}
-- (void)otherMouseDown:(NSEvent *)event {
- if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(window_id, event, MouseButton::MIDDLE, MouseButton::MASK_MIDDLE, true);
- } else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(window_id, event, MouseButton::MB_XBUTTON1, MouseButton::MASK_XBUTTON1, true);
- } else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(window_id, event, MouseButton::MB_XBUTTON2, MouseButton::MASK_XBUTTON2, true);
- } else {
- return;
- }
+bool DisplayServerOSX::has_window(WindowID p_window) const {
+ return windows.has(p_window);
}
-- (void)otherMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
+DisplayServerOSX::WindowData &DisplayServerOSX::get_window(WindowID p_window) {
+ return windows[p_window];
}
-- (void)otherMouseUp:(NSEvent *)event {
- if ((int)[event buttonNumber] == 2) {
- _mouseDownEvent(window_id, event, MouseButton::MIDDLE, MouseButton::MASK_MIDDLE, false);
- } else if ((int)[event buttonNumber] == 3) {
- _mouseDownEvent(window_id, event, MouseButton::MB_XBUTTON1, MouseButton::MASK_XBUTTON1, false);
- } else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(window_id, event, MouseButton::MB_XBUTTON2, MouseButton::MASK_XBUTTON2, false);
- } else {
- return;
- }
-}
+void DisplayServerOSX::send_event(NSEvent *p_event) {
+ // Special case handling of command-period, which is traditionally a special
+ // shortcut in macOS and doesn't arrive at our regular keyDown handler.
+ if ([p_event type] == NSEventTypeKeyDown) {
+ if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
+ Ref<InputEventKey> k;
+ k.instantiate();
-- (void)mouseExited:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ get_key_modifier_state([p_event modifierFlags], k);
+ k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
+ k->set_pressed(true);
+ k->set_keycode(Key::PERIOD);
+ k->set_physical_keycode(Key::PERIOD);
+ k->set_echo([p_event isARepeat]);
- if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) {
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT);
+ Input::get_singleton()->parse_input_event(k);
+ }
}
}
-- (void)mouseEntered:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+void DisplayServerOSX::send_window_event(const WindowData &wd, WindowEvent p_event) {
+ _THREAD_SAFE_METHOD_
- if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED) {
- DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER);
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
}
-
- DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
- DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
- DS_OSX->cursor_set_shape(p_shape);
}
-- (void)magnifyWithEvent:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- Ref<InputEventMagnifyGesture> ev;
- ev.instantiate();
- ev->set_window_id(window_id);
- _get_key_modifier_state([event modifierFlags], ev);
- ev->set_position(_get_mouse_pos(wd, [event locationInWindow]));
- ev->set_factor([event magnification] + 1.0);
-
- Input::get_singleton()->parse_input_event(ev);
-}
-
-- (void)viewDidChangeBackingProperties {
- // nothing left to do here
-}
-
-- (void)updateTrackingAreas {
- if (trackingArea != nil) {
- [self removeTrackingArea:trackingArea];
- [trackingArea release];
- }
-
- NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect;
- trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
-
- [self addTrackingArea:trackingArea];
- [super updateTrackingAreas];
-}
-
-static bool isNumpadKey(unsigned int key) {
- static const unsigned int table[] = {
- 0x41, /* kVK_ANSI_KeypadDecimal */
- 0x43, /* kVK_ANSI_KeypadMultiply */
- 0x45, /* kVK_ANSI_KeypadPlus */
- 0x47, /* kVK_ANSI_KeypadClear */
- 0x4b, /* kVK_ANSI_KeypadDivide */
- 0x4c, /* kVK_ANSI_KeypadEnter */
- 0x4e, /* kVK_ANSI_KeypadMinus */
- 0x51, /* kVK_ANSI_KeypadEquals */
- 0x52, /* kVK_ANSI_Keypad0 */
- 0x53, /* kVK_ANSI_Keypad1 */
- 0x54, /* kVK_ANSI_Keypad2 */
- 0x55, /* kVK_ANSI_Keypad3 */
- 0x56, /* kVK_ANSI_Keypad4 */
- 0x57, /* kVK_ANSI_Keypad5 */
- 0x58, /* kVK_ANSI_Keypad6 */
- 0x59, /* kVK_ANSI_Keypad7 */
- 0x5b, /* kVK_ANSI_Keypad8 */
- 0x5c, /* kVK_ANSI_Keypad9 */
- 0x5f, /* kVK_JIS_KeypadComma */
- 0x00
- };
- for (int i = 0; table[i] != 0; i++) {
- if (key == table[i]) {
- return true;
- }
+void DisplayServerOSX::release_pressed_events() {
+ _THREAD_SAFE_METHOD_
+ if (Input::get_singleton()) {
+ Input::get_singleton()->release_pressed_events();
}
- return false;
}
-// Keyboard symbol translation table
-static const Key _osx_to_godot_table[128] = {
- /* 00 */ Key::A,
- /* 01 */ Key::S,
- /* 02 */ Key::D,
- /* 03 */ Key::F,
- /* 04 */ Key::H,
- /* 05 */ Key::G,
- /* 06 */ Key::Z,
- /* 07 */ Key::X,
- /* 08 */ Key::C,
- /* 09 */ Key::V,
- /* 0a */ Key::SECTION, /* ISO Section */
- /* 0b */ Key::B,
- /* 0c */ Key::Q,
- /* 0d */ Key::W,
- /* 0e */ Key::E,
- /* 0f */ Key::R,
- /* 10 */ Key::Y,
- /* 11 */ Key::T,
- /* 12 */ Key::KEY_1,
- /* 13 */ Key::KEY_2,
- /* 14 */ Key::KEY_3,
- /* 15 */ Key::KEY_4,
- /* 16 */ Key::KEY_6,
- /* 17 */ Key::KEY_5,
- /* 18 */ Key::EQUAL,
- /* 19 */ Key::KEY_9,
- /* 1a */ Key::KEY_7,
- /* 1b */ Key::MINUS,
- /* 1c */ Key::KEY_8,
- /* 1d */ Key::KEY_0,
- /* 1e */ Key::BRACERIGHT,
- /* 1f */ Key::O,
- /* 20 */ Key::U,
- /* 21 */ Key::BRACELEFT,
- /* 22 */ Key::I,
- /* 23 */ Key::P,
- /* 24 */ Key::ENTER,
- /* 25 */ Key::L,
- /* 26 */ Key::J,
- /* 27 */ Key::APOSTROPHE,
- /* 28 */ Key::K,
- /* 29 */ Key::SEMICOLON,
- /* 2a */ Key::BACKSLASH,
- /* 2b */ Key::COMMA,
- /* 2c */ Key::SLASH,
- /* 2d */ Key::N,
- /* 2e */ Key::M,
- /* 2f */ Key::PERIOD,
- /* 30 */ Key::TAB,
- /* 31 */ Key::SPACE,
- /* 32 */ Key::QUOTELEFT,
- /* 33 */ Key::BACKSPACE,
- /* 34 */ Key::UNKNOWN,
- /* 35 */ Key::ESCAPE,
- /* 36 */ Key::META,
- /* 37 */ Key::META,
- /* 38 */ Key::SHIFT,
- /* 39 */ Key::CAPSLOCK,
- /* 3a */ Key::ALT,
- /* 3b */ Key::CTRL,
- /* 3c */ Key::SHIFT,
- /* 3d */ Key::ALT,
- /* 3e */ Key::CTRL,
- /* 3f */ Key::UNKNOWN, /* Function */
- /* 40 */ Key::UNKNOWN, /* F17 */
- /* 41 */ Key::KP_PERIOD,
- /* 42 */ Key::UNKNOWN,
- /* 43 */ Key::KP_MULTIPLY,
- /* 44 */ Key::UNKNOWN,
- /* 45 */ Key::KP_ADD,
- /* 46 */ Key::UNKNOWN,
- /* 47 */ Key::NUMLOCK, /* Really KeypadClear... */
- /* 48 */ Key::VOLUMEUP, /* VolumeUp */
- /* 49 */ Key::VOLUMEDOWN, /* VolumeDown */
- /* 4a */ Key::VOLUMEMUTE, /* Mute */
- /* 4b */ Key::KP_DIVIDE,
- /* 4c */ Key::KP_ENTER,
- /* 4d */ Key::UNKNOWN,
- /* 4e */ Key::KP_SUBTRACT,
- /* 4f */ Key::UNKNOWN, /* F18 */
- /* 50 */ Key::UNKNOWN, /* F19 */
- /* 51 */ Key::EQUAL, /* KeypadEqual */
- /* 52 */ Key::KP_0,
- /* 53 */ Key::KP_1,
- /* 54 */ Key::KP_2,
- /* 55 */ Key::KP_3,
- /* 56 */ Key::KP_4,
- /* 57 */ Key::KP_5,
- /* 58 */ Key::KP_6,
- /* 59 */ Key::KP_7,
- /* 5a */ Key::UNKNOWN, /* F20 */
- /* 5b */ Key::KP_8,
- /* 5c */ Key::KP_9,
- /* 5d */ Key::YEN, /* JIS Yen */
- /* 5e */ Key::UNDERSCORE, /* JIS Underscore */
- /* 5f */ Key::COMMA, /* JIS KeypadComma */
- /* 60 */ Key::F5,
- /* 61 */ Key::F6,
- /* 62 */ Key::F7,
- /* 63 */ Key::F3,
- /* 64 */ Key::F8,
- /* 65 */ Key::F9,
- /* 66 */ Key::UNKNOWN, /* JIS Eisu */
- /* 67 */ Key::F11,
- /* 68 */ Key::UNKNOWN, /* JIS Kana */
- /* 69 */ Key::F13,
- /* 6a */ Key::F16,
- /* 6b */ Key::F14,
- /* 6c */ Key::UNKNOWN,
- /* 6d */ Key::F10,
- /* 6e */ Key::MENU,
- /* 6f */ Key::F12,
- /* 70 */ Key::UNKNOWN,
- /* 71 */ Key::F15,
- /* 72 */ Key::INSERT, /* Really Help... */
- /* 73 */ Key::HOME,
- /* 74 */ Key::PAGEUP,
- /* 75 */ Key::KEY_DELETE,
- /* 76 */ Key::F4,
- /* 77 */ Key::END,
- /* 78 */ Key::F2,
- /* 79 */ Key::PAGEDOWN,
- /* 7a */ Key::F1,
- /* 7b */ Key::LEFT,
- /* 7c */ Key::RIGHT,
- /* 7d */ Key::DOWN,
- /* 7e */ Key::UP,
- /* 7f */ Key::UNKNOWN,
-};
-
-// Translates a OS X keycode to a Godot keycode
-static Key translateKey(unsigned int key) {
- if (key >= 128) {
- return Key::UNKNOWN;
- }
-
- return _osx_to_godot_table[key];
-}
-
-// Translates a Godot keycode back to a OSX keycode
-static unsigned int unmapKey(Key key) {
- for (int i = 0; i <= 126; i++) {
- if (_osx_to_godot_table[i] == key) {
- return i;
- }
- }
- return 127;
-}
-
-struct _KeyCodeMap {
- UniChar kchar;
- Key kcode;
-};
-
-static const _KeyCodeMap _keycodes[55] = {
- { '`', Key::QUOTELEFT },
- { '~', Key::ASCIITILDE },
- { '0', Key::KEY_0 },
- { '1', Key::KEY_1 },
- { '2', Key::KEY_2 },
- { '3', Key::KEY_3 },
- { '4', Key::KEY_4 },
- { '5', Key::KEY_5 },
- { '6', Key::KEY_6 },
- { '7', Key::KEY_7 },
- { '8', Key::KEY_8 },
- { '9', Key::KEY_9 },
- { '-', Key::MINUS },
- { '_', Key::UNDERSCORE },
- { '=', Key::EQUAL },
- { '+', Key::PLUS },
- { 'q', Key::Q },
- { 'w', Key::W },
- { 'e', Key::E },
- { 'r', Key::R },
- { 't', Key::T },
- { 'y', Key::Y },
- { 'u', Key::U },
- { 'i', Key::I },
- { 'o', Key::O },
- { 'p', Key::P },
- { '[', Key::BRACELEFT },
- { ']', Key::BRACERIGHT },
- { '{', Key::BRACELEFT },
- { '}', Key::BRACERIGHT },
- { 'a', Key::A },
- { 's', Key::S },
- { 'd', Key::D },
- { 'f', Key::F },
- { 'g', Key::G },
- { 'h', Key::H },
- { 'j', Key::J },
- { 'k', Key::K },
- { 'l', Key::L },
- { ';', Key::SEMICOLON },
- { ':', Key::COLON },
- { '\'', Key::APOSTROPHE },
- { '\"', Key::QUOTEDBL },
- { '\\', Key::BACKSLASH },
- { '#', Key::NUMBERSIGN },
- { 'z', Key::Z },
- { 'x', Key::X },
- { 'c', Key::C },
- { 'v', Key::V },
- { 'b', Key::B },
- { 'n', Key::N },
- { 'm', Key::M },
- { ',', Key::COMMA },
- { '.', Key::PERIOD },
- { '/', Key::SLASH }
-};
-
-static Key remapKey(unsigned int key, unsigned int state) {
- if (isNumpadKey(key)) {
- return translateKey(key);
- }
-
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard) {
- return translateKey(key);
- }
-
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData) {
- return translateKey(key);
- }
-
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
-
- OSStatus err = UCKeyTranslate(keyboardLayout,
- key,
- kUCKeyActionDisplay,
- (state >> 8) & 0xFF,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
-
- if (err != noErr) {
- return translateKey(key);
- }
-
- for (unsigned int i = 0; i < 55; i++) {
- if (_keycodes[i].kchar == chars[0]) {
- return _keycodes[i].kcode;
- }
- }
- return translateKey(key);
-}
-
-- (void)keyDown:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- ignore_momentum_scroll = true;
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- if (!wd.im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
- // Fallback unicode character handler used if IME is not active
- Char16String text;
- text.resize([characters length] + 1);
- [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
-
- String u32text;
- u32text.parse_utf16(text.ptr(), text.length());
-
- for (int i = 0; i < u32text.length(); i++) {
- const char32_t codepoint = u32text[i];
-
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = codepoint;
-
- _push_to_key_event_buffer(ke);
- }
- } else {
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = false;
- ke.unicode = 0;
-
- _push_to_key_event_buffer(ke);
- }
- }
-
- // Pass events to IME handler
- if (wd.im_active) {
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
- }
+void DisplayServerOSX::get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) const {
+ r_state->set_shift_pressed((p_osx_state & NSEventModifierFlagShift));
+ r_state->set_ctrl_pressed((p_osx_state & NSEventModifierFlagControl));
+ r_state->set_alt_pressed((p_osx_state & NSEventModifierFlagOption));
+ r_state->set_meta_pressed((p_osx_state & NSEventModifierFlagCommand));
}
-- (void)flagsChanged:(NSEvent *)event {
- ignore_momentum_scroll = true;
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.echo = false;
- ke.raw = true;
-
- int key = [event keyCode];
- int mod = [event modifierFlags];
-
- if (key == 0x36 || key == 0x37) {
- if (mod & NSEventModifierFlagCommand) {
- mod &= ~NSEventModifierFlagCommand;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x38 || key == 0x3c) {
- if (mod & NSEventModifierFlagShift) {
- mod &= ~NSEventModifierFlagShift;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3a || key == 0x3d) {
- if (mod & NSEventModifierFlagOption) {
- mod &= ~NSEventModifierFlagOption;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3b || key == 0x3e) {
- if (mod & NSEventModifierFlagControl) {
- mod &= ~NSEventModifierFlagControl;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else {
- return;
- }
-
- ke.osx_state = mod;
- ke.keycode = remapKey(key, mod);
- ke.physical_keycode = translateKey(key);
- ke.unicode = 0;
-
- _push_to_key_event_buffer(ke);
- }
+void DisplayServerOSX::update_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_location_in_window) {
+ const NSRect content_rect = [p_wd.window_view frame];
+ const float scale = screen_get_max_scale();
+ p_wd.mouse_pos.x = p_location_in_window.x * scale;
+ p_wd.mouse_pos.y = (content_rect.size.height - p_location_in_window.y) * scale;
+ Input::get_singleton()->set_mouse_position(p_wd.mouse_pos);
}
-- (void)keyUp:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- // Fallback unicode character handler used if IME is not active
- if (!wd.im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
- Char16String text;
- text.resize([characters length] + 1);
- [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
-
- String u32text;
- u32text.parse_utf16(text.ptr(), text.length());
-
- for (int i = 0; i < u32text.length(); i++) {
- const char32_t codepoint = u32text[i];
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.osx_state = [event modifierFlags];
- ke.pressed = false;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = codepoint;
-
- _push_to_key_event_buffer(ke);
- }
- } else {
- DisplayServerOSX::KeyEvent ke;
-
- ke.window_id = window_id;
- ke.osx_state = [event modifierFlags];
- ke.pressed = false;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = 0;
-
- _push_to_key_event_buffer(ke);
- }
+void DisplayServerOSX::push_to_key_event_buffer(const DisplayServerOSX::KeyEvent &p_event) {
+ if (key_event_pos >= key_event_buffer.size()) {
+ key_event_buffer.resize(1 + key_event_pos);
}
+ key_event_buffer.write[key_event_pos++] = p_event;
}
-inline void sendScrollEvent(DisplayServer::WindowID window_id, MouseButton button, double factor, int modifierFlags) {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- MouseButton mask = mouse_button_to_mask(button);
-
- Ref<InputEventMouseButton> sc;
- sc.instantiate();
+void DisplayServerOSX::update_im_text(const Point2i &p_selection, const String &p_text) {
+ im_selection = p_selection;
+ im_text = p_text;
- sc->set_window_id(window_id);
- _get_key_modifier_state(modifierFlags, sc);
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(true);
- sc->set_position(wd.mouse_pos);
- sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state |= (MouseButton)mask;
- sc->set_button_mask(DS_OSX->last_button_state);
-
- Input::get_singleton()->parse_input_event(sc);
-
- sc.instantiate();
- sc->set_window_id(window_id);
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(false);
- sc->set_position(wd.mouse_pos);
- sc->set_global_position(wd.mouse_pos);
- DS_OSX->last_button_state &= (MouseButton)~mask;
- sc->set_button_mask(DS_OSX->last_button_state);
-
- Input::get_singleton()->parse_input_event(sc);
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
}
-inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy, int modifierFlags) {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- Ref<InputEventPanGesture> pg;
- pg.instantiate();
-
- pg->set_window_id(window_id);
- _get_key_modifier_state(modifierFlags, pg);
- pg->set_position(wd.mouse_pos);
- pg->set_delta(Vector2(-dx, -dy));
-
- Input::get_singleton()->parse_input_event(pg);
+void DisplayServerOSX::set_last_focused_window(WindowID p_window) {
+ last_focused_window = p_window;
}
-- (void)scrollWheel:(NSEvent *)event {
- ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
- DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
-
- double deltaX, deltaY;
-
- _ALLOW_DISCARD_ _get_mouse_pos(wd, [event locationInWindow]);
-
- deltaX = [event scrollingDeltaX];
- deltaY = [event scrollingDeltaY];
-
- if ([event hasPreciseScrollingDeltas]) {
- deltaX *= 0.03;
- deltaY *= 0.03;
+void DisplayServerOSX::window_update(WindowID p_window) {
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->window_update(p_window);
}
+#endif
+}
- if ([event momentumPhase] != NSEventPhaseNone) {
- if (ignore_momentum_scroll) {
- return;
- }
- } else {
- ignore_momentum_scroll = false;
+void DisplayServerOSX::window_destroy(WindowID p_window) {
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->window_destroy(p_window);
}
-
- if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
- sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]);
- } else {
- if (fabs(deltaX)) {
- sendScrollEvent(window_id, 0 > deltaX ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
- }
- if (fabs(deltaY)) {
- sendScrollEvent(window_id, 0 < deltaY ? MouseButton::WHEEL_UP : MouseButton::WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
- }
+#endif
+#ifdef VULKAN_ENABLED
+ if (context_vulkan) {
+ context_vulkan->window_destroy(p_window);
}
+#endif
+ windows.erase(p_window);
}
-@end
-
-/*************************************************************************/
-/* GodotWindow */
-/*************************************************************************/
-
-@interface GodotWindow : NSWindow {
-}
-
-@end
-
-@implementation GodotWindow
-
-- (BOOL)canBecomeKeyWindow {
- // Required for NSBorderlessWindowMask windows
- for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) {
- if (E->get().window_object == self) {
- if (E->get().no_focus) {
- return NO;
- }
- }
+void DisplayServerOSX::window_resize(WindowID p_window, int p_width, int p_height) {
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->window_resize(p_window, p_width, p_height);
}
- return YES;
-}
-
-- (BOOL)canBecomeMainWindow {
- // Required for NSBorderlessWindowMask windows
- for (Map<DisplayServer::WindowID, DisplayServerOSX::WindowData>::Element *E = DS_OSX->windows.front(); E; E = E->next()) {
- if (E->get().window_object == self) {
- if (E->get().no_focus) {
- return NO;
- }
- }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ context_vulkan->window_resize(p_window, p_width, p_height);
}
- return YES;
+#endif
}
-@end
-
-/*************************************************************************/
-/* DisplayServerOSX */
-/*************************************************************************/
-
bool DisplayServerOSX::has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_GLOBAL_MENU:
@@ -1494,57 +648,13 @@ String DisplayServerOSX::get_name() const {
return "OSX";
}
-const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
- const NSMenu *menu = nullptr;
- if (p_menu_root == "") {
- // Main menu.x
- menu = [NSApp mainMenu];
- } else if (p_menu_root.to_lower() == "_dock") {
- // macOS dock menu.
- menu = dock_menu;
- } else {
- // Submenu.
- if (submenu.has(p_menu_root)) {
- menu = submenu[p_menu_root];
- }
- }
- if (menu == apple_menu) {
- // Do not allow to change Apple menu.
- return nullptr;
- }
- return menu;
-}
-
-NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
- NSMenu *menu = nullptr;
- if (p_menu_root == "") {
- // Main menu.
- menu = [NSApp mainMenu];
- } else if (p_menu_root.to_lower() == "_dock") {
- // macOS dock menu.
- menu = dock_menu;
- } else {
- // Submenu.
- if (!submenu.has(p_menu_root)) {
- NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
- submenu[p_menu_root] = n_menu;
- }
- menu = submenu[p_menu_root];
- }
- if (menu == apple_menu) {
- // Do not allow to change Apple menu.
- return nullptr;
- }
- return menu;
-}
-
void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
_THREAD_SAFE_METHOD_
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
obj->callback = p_callback;
obj->meta = p_tag;
obj->checkable = false;
@@ -1558,7 +668,7 @@ void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, con
NSMenu *menu = _get_menu_root(p_menu_root);
if (menu) {
NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
obj->callback = p_callback;
obj->meta = p_tag;
obj->checkable = true;
@@ -1614,7 +724,7 @@ bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root,
if (menu) {
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
if (obj) {
return obj->checkable;
}
@@ -1630,7 +740,7 @@ Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_ro
if (menu) {
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
if (obj) {
return obj->callback;
}
@@ -1646,7 +756,7 @@ Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, in
if (menu) {
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
if (obj) {
return obj->meta;
}
@@ -1662,10 +772,8 @@ String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, in
if (menu) {
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- char *utfs = strdup([[menu_item title] UTF8String]);
String ret;
- ret.parse_utf8(utfs);
- free(utfs);
+ ret.parse_utf8([[menu_item title] UTF8String]);
return ret;
}
}
@@ -1720,7 +828,7 @@ void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root,
}
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
obj->checkable = p_checkable;
}
}
@@ -1736,7 +844,7 @@ void DisplayServerOSX::global_menu_set_item_callback(const String &p_menu_root,
}
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
obj->callback = p_callback;
}
}
@@ -1752,7 +860,7 @@ void DisplayServerOSX::global_menu_set_item_tag(const String &p_menu_root, int p
}
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
if (menu_item) {
- GlobalMenuItem *obj = [menu_item representedObject];
+ GodotMenuItem *obj = [menu_item representedObject];
obj->meta = p_tag;
}
}
@@ -1869,7 +977,6 @@ Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector
p_callback.call((const Variant **)&buttonp, 1, ret, ce);
}
- [window release];
return OK;
}
@@ -1891,10 +998,8 @@ Error DisplayServerOSX::dialog_input_text(String p_title, String p_description,
[window runModal];
- char *utfs = strdup([[input stringValue] UTF8String]);
String ret;
- ret.parse_utf8(utfs);
- free(utfs);
+ ret.parse_utf8([[input stringValue] UTF8String]);
if (!p_callback.is_null()) {
Variant text = ret;
@@ -1904,7 +1009,6 @@ Error DisplayServerOSX::dialog_input_text(String p_title, String p_description,
p_callback.call((const Variant **)&textp, 1, ret, ce);
}
- [window release];
return OK;
}
@@ -1959,9 +1063,7 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
mouse_mode = p_mode;
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- CursorShape p_shape = cursor_shape;
- cursor_shape = DisplayServer::CURSOR_MAX;
- cursor_set_shape(p_shape);
+ cursor_update_shape();
}
}
@@ -1969,25 +1071,82 @@ DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const {
return mouse_mode;
}
+bool DisplayServerOSX::update_mouse_wrap(WindowData &p_wd, NSPoint &r_delta, NSPoint &r_mpos, NSTimeInterval p_timestamp) {
+ _THREAD_SAFE_METHOD_
+
+ if (ignore_warp) {
+ // Discard late events, before warp.
+ if (p_timestamp < last_warp) {
+ return true;
+ }
+ ignore_warp = false;
+ return true;
+ }
+
+ if (mouse_mode == DisplayServer::MOUSE_MODE_CONFINED || mouse_mode == DisplayServer::MOUSE_MODE_CONFINED_HIDDEN) {
+ // Discard late events.
+ if (p_timestamp < last_warp) {
+ return true;
+ }
+
+ // Warp affects next event delta, subtract previous warp deltas.
+ List<WarpEvent>::Element *F = warp_events.front();
+ while (F) {
+ if (F->get().timestamp < p_timestamp) {
+ List<DisplayServerOSX::WarpEvent>::Element *E = F;
+ r_delta.x -= E->get().delta.x;
+ r_delta.y -= E->get().delta.y;
+ F = F->next();
+ warp_events.erase(E);
+ } else {
+ F = F->next();
+ }
+ }
+
+ // Confine mouse position to the window, and update delta.
+ NSRect frame = [p_wd.window_object frame];
+ NSPoint conf_pos = r_mpos;
+ conf_pos.x = CLAMP(conf_pos.x + r_delta.x, 0.f, frame.size.width);
+ conf_pos.y = CLAMP(conf_pos.y - r_delta.y, 0.f, frame.size.height);
+ r_delta.x = conf_pos.x - r_mpos.x;
+ r_delta.y = r_mpos.y - conf_pos.y;
+ r_mpos = conf_pos;
+
+ // Move mouse cursor.
+ NSRect point_in_window_rect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0);
+ conf_pos = [[p_wd.window_view window] convertRectToScreen:point_in_window_rect].origin;
+ conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y;
+ CGWarpMouseCursorPosition(conf_pos);
+
+ // Save warp data.
+ last_warp = [[NSProcessInfo processInfo] systemUptime];
+
+ DisplayServerOSX::WarpEvent ev;
+ ev.timestamp = last_warp;
+ ev.delta = r_delta;
+ warp_events.push_back(ev);
+ }
+
+ return false;
+}
+
void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
_THREAD_SAFE_METHOD_
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- last_mouse_pos = p_to;
- } else {
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
WindowID window_id = windows.has(last_focused_window) ? last_focused_window : MAIN_WINDOW_ID;
WindowData &wd = windows[window_id];
- //local point in window coords
+ // Local point in window coords.
const NSRect contentRect = [wd.window_view frame];
const float scale = screen_get_max_scale();
NSRect pointInWindowRect = NSMakeRect(p_to.x / scale, contentRect.size.height - (p_to.y / scale - 1), 0, 0);
NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
- //point in scren coords
+ // Point in scren coords.
CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
- //do the warping
+ // Do the warping.
CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
CGAssociateMouseAndMouseCursorPosition(false);
@@ -2013,6 +1172,10 @@ Point2i DisplayServerOSX::mouse_get_position() const {
return Vector2i();
}
+void DisplayServerOSX::mouse_set_button_state(MouseButton p_state) {
+ last_button_state = p_state;
+}
+
MouseButton DisplayServerOSX::mouse_get_button_state() const {
return last_button_state;
}
@@ -2044,11 +1207,8 @@ String DisplayServerOSX::clipboard_get() const {
NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
NSString *string = [objectsToPaste objectAtIndex:0];
- char *utfs = strdup([string UTF8String]);
String ret;
- ret.parse_utf8(utfs);
- free(utfs);
-
+ ret.parse_utf8([string UTF8String]);
return ret;
}
@@ -2059,48 +1219,6 @@ int DisplayServerOSX::get_screen_count() const {
return [screenArray count];
}
-// Returns the native top-left screen coordinate of the smallest rectangle
-// that encompasses all screens. Needed in get_screen_position(),
-// window_get_position, and window_set_position()
-// to convert between OS X native screen coordinates and the ones expected by Godot
-
-static bool displays_arrangement_dirty = true;
-static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
- displays_arrangement_dirty = true;
-}
-
-Point2i DisplayServerOSX::_get_screens_origin() const {
- static Point2i origin;
-
- if (displays_arrangement_dirty) {
- origin = Point2i();
-
- for (int i = 0; i < get_screen_count(); i++) {
- Point2i position = _get_native_screen_position(i);
- if (position.x < origin.x) {
- origin.x = position.x;
- }
- if (position.y > origin.y) {
- origin.y = position.y;
- }
- }
- displays_arrangement_dirty = false;
- }
-
- return origin;
-}
-
-Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const {
- NSArray *screenArray = [NSScreen screens];
- if ((NSUInteger)p_screen < [screenArray count]) {
- NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
- // Return the top-left corner of the screen, for OS X the y starts at the bottom
- return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * screen_get_max_scale();
- }
-
- return Point2i();
-}
-
Point2i DisplayServerOSX::screen_get_position(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -2110,7 +1228,7 @@ Point2i DisplayServerOSX::screen_get_position(int p_screen) const {
Point2i position = _get_native_screen_position(p_screen) - _get_screens_origin();
// OS X native y-coordinate relative to _get_screens_origin() is negative,
- // Godot expects a positive value
+ // Godot expects a positive value.
position.y *= -1;
return position;
}
@@ -2124,7 +1242,7 @@ Size2i DisplayServerOSX::screen_get_size(int p_screen) const {
NSArray *screenArray = [NSScreen screens];
if ((NSUInteger)p_screen < [screenArray count]) {
- // Note: Use frame to get the whole screen size
+ // Note: Use frame to get the whole screen size.
NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
return Size2i(nsrect.size.width, nsrect.size.height) * screen_get_max_scale();
}
@@ -2203,6 +1321,24 @@ Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
return Rect2i();
}
+float DisplayServerOSX::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
+ const CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode([[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+ const double displayRefreshRate = CGDisplayModeGetRefreshRate(displayMode);
+ return (float)displayRefreshRate;
+ }
+ ERR_PRINT("An error occured while trying to get the screen refresh rate.");
+ return SCREEN_REFRESH_RATE_FALLBACK;
+}
+
Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
_THREAD_SAFE_METHOD_
@@ -2236,56 +1372,6 @@ void DisplayServerOSX::show_window(WindowID p_id) {
}
}
-void DisplayServerOSX::_send_window_event(const WindowData &wd, WindowEvent p_event) {
- _THREAD_SAFE_METHOD_
-
- if (!wd.event_callback.is_null()) {
- Variant event = int(p_event);
- Variant *eventp = &event;
- Variant ret;
- Callable::CallError ce;
- wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
- }
-}
-
-DisplayServerOSX::WindowID DisplayServerOSX::_find_window_id(id p_window) {
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if (E->get().window_object == p_window) {
- return E->key();
- }
- }
- return INVALID_WINDOW_ID;
-}
-
-void DisplayServerOSX::_update_window(WindowData p_wd) {
- bool borderless_full = false;
-
- if (p_wd.borderless) {
- NSRect frameRect = [p_wd.window_object frame];
- NSRect screenRect = [[p_wd.window_object screen] frame];
-
- // Check if our window covers up the screen
- if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
- frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
- borderless_full = true;
- }
- }
-
- if (borderless_full) {
- // If the window covers up the screen set the level to above the main menu and hide on deactivate
- [p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
- [p_wd.window_object setHidesOnDeactivate:YES];
- } else {
- // Reset these when our window is not a borderless window that covers up the screen
- if (p_wd.on_top && !p_wd.fullscreen) {
- [p_wd.window_object setLevel:NSFloatingWindowLevel];
- } else {
- [p_wd.window_object setLevel:NSNormalWindowLevel];
- }
- [p_wd.window_object setHidesOnDeactivate:NO];
- }
-}
-
void DisplayServerOSX::delete_sub_window(WindowID p_id) {
_THREAD_SAFE_METHOD_
@@ -2298,24 +1384,6 @@ void DisplayServerOSX::delete_sub_window(WindowID p_id) {
[wd.window_object close];
}
-void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window) {
- _THREAD_SAFE_METHOD_
-
- ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
-
- [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
-}
-
-void DisplayServerOSX::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
- _THREAD_SAFE_METHOD_
-
- ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
-
- wd.mpath = p_region;
-}
-
void DisplayServerOSX::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2354,6 +1422,24 @@ void DisplayServerOSX::window_set_drop_files_callback(const Callable &p_callable
wd.drop_files_callback = p_callable;
}
+void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
+}
+
+void DisplayServerOSX::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.mpath = p_region;
+}
+
int DisplayServerOSX::window_get_current_screen(WindowID p_window) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V(!windows.has(p_window), -1);
@@ -2385,39 +1471,6 @@ void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window
}
}
-void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) {
- _THREAD_SAFE_METHOD_
- ERR_FAIL_COND(p_window == p_parent);
-
- ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd_window = windows[p_window];
-
- ERR_FAIL_COND(wd_window.transient_parent == p_parent);
-
- ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
- if (p_parent == INVALID_WINDOW_ID) {
- //remove transient
- ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
- ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
-
- WindowData &wd_parent = windows[wd_window.transient_parent];
-
- wd_window.transient_parent = INVALID_WINDOW_ID;
- wd_parent.transient_children.erase(p_window);
-
- [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- } else {
- ERR_FAIL_COND(!windows.has(p_parent));
- ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
- WindowData &wd_parent = windows[p_parent];
-
- wd_window.transient_parent = p_parent;
- wd_parent.transient_children.insert(p_window);
-
- [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
- }
-}
-
Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
_THREAD_SAFE_METHOD_
@@ -2429,14 +1482,14 @@ Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect];
Point2i pos;
- // Return the position of the top-left corner, for OS X the y starts at the bottom
+ // Return the position of the top-left corner, for OS X the y starts at the bottom.
const float scale = screen_get_max_scale();
pos.x = nsrect.origin.x;
pos.y = (nsrect.origin.y + nsrect.size.height);
pos *= scale;
pos -= _get_screens_origin();
// OS X native y-coordinate relative to _get_screens_origin() is negative,
- // Godot expects a positive value
+ // Godot expects a positive value.
pos.y *= -1;
return pos;
}
@@ -2449,7 +1502,7 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p
Point2i position = p_position;
// OS X native y-coordinate relative to _get_screens_origin() is negative,
- // Godot passes a positive value
+ // Godot passes a positive value.
position.y *= -1;
position += _get_screens_origin();
position /= screen_get_max_scale();
@@ -2465,8 +1518,41 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p
[wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)];
- _update_window(wd);
- _ALLOW_DISCARD_ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ _update_window_style(wd);
+ update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+}
+
+void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+ ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
+ if (p_parent == INVALID_WINDOW_ID) {
+ // Remove transient.
+ ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ } else {
+ ERR_FAIL_COND(!windows.has(p_parent));
+ ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
+ }
}
void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) {
@@ -2547,7 +1633,7 @@ void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) {
[wd.window_object setFrame:new_frame display:YES];
- _update_window(wd);
+ _update_window_style(wd);
}
Size2i DisplayServerOSX::window_get_size(WindowID p_window) const {
@@ -2567,73 +1653,6 @@ Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const {
return Size2i(frame.size.width, frame.size.height) * screen_get_max_scale();
}
-bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const {
- return true;
-}
-
-void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) {
- ERR_FAIL_COND(!windows.has(p_window));
- WindowData &wd = windows[p_window];
-
- if (!OS_OSX::get_singleton()->is_layered_allowed()) {
- return;
- }
- if (wd.layered_window != p_enabled) {
- if (p_enabled) {
- [wd.window_object setBackgroundColor:[NSColor clearColor]];
- [wd.window_object setOpaque:NO];
- [wd.window_object setHasShadow:NO];
- CALayer *layer = [wd.window_view layer];
- if (layer) {
- [layer setOpaque:NO];
- }
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- //TODO - implement transparency for Vulkan
- }
-#endif
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- //TODO - reimplement OpenGLES
- }
-#endif
- wd.layered_window = true;
- } else {
- [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
- [wd.window_object setOpaque:YES];
- [wd.window_object setHasShadow:YES];
- CALayer *layer = [wd.window_view layer];
- if (layer) {
- [layer setOpaque:YES];
- }
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- //TODO - implement transparency for Vulkan
- }
-#endif
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- //TODO - reimplement OpenGLES
- }
-#endif
- wd.layered_window = false;
- }
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- //TODO - reimplement OpenGLES
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- //TODO - implement transparency for Vulkan
- }
-#endif
- NSRect frameRect = [wd.window_object frame];
- [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:YES];
- [wd.window_object setFrame:frameRect display:YES];
- }
-}
-
void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2642,22 +1661,21 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
WindowMode old_mode = window_get_mode(p_window);
if (old_mode == p_mode) {
- return; // do nothing
+ return; // Do nothing.
}
switch (old_mode) {
case WINDOW_MODE_WINDOWED: {
- //do nothing
+ // Do nothing.
} break;
case WINDOW_MODE_MINIMIZED: {
[wd.window_object deminiaturize:nil];
} break;
+ case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
[wd.window_object setLevel:NSNormalWindowLevel];
- if (wd.layered_window) {
- _set_window_per_pixel_transparency_enabled(true, p_window);
- }
- if (wd.resize_disabled) { //restore resize disabled
+ _set_window_per_pixel_transparency_enabled(true, p_window);
+ if (wd.resize_disabled) { // Restore resize disabled.
[wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
}
if (wd.min_size != Size2i()) {
@@ -2680,16 +1698,17 @@ void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
switch (p_mode) {
case WINDOW_MODE_WINDOWED: {
- //do nothing
+ // Do nothing.
} break;
case WINDOW_MODE_MINIMIZED: {
[wd.window_object performMiniaturize:nil];
} break;
+ case WINDOW_MODE_EXCLUSIVE_FULLSCREEN:
case WINDOW_MODE_FULLSCREEN: {
- if (wd.layered_window)
- _set_window_per_pixel_transparency_enabled(false, p_window);
- if (wd.resize_disabled) //fullscreen window should be resizable to work
+ _set_window_per_pixel_transparency_enabled(false, p_window);
+ if (wd.resize_disabled) { // Fullscreen window should be resizable to work.
[wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ }
[wd.window_object setContentMinSize:NSMakeSize(0, 0)];
[wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
[wd.window_object toggleFullScreen:nil];
@@ -2709,7 +1728,7 @@ DisplayServer::WindowMode DisplayServerOSX::window_get_mode(WindowID p_window) c
ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
const WindowData &wd = windows[p_window];
- if (wd.fullscreen) { //if fullscreen, it's not in another mode
+ if (wd.fullscreen) { // If fullscreen, it's not in another mode.
return WINDOW_MODE_FULLSCREEN;
}
if ([wd.window_object isZoomed] && !wd.resize_disabled) {
@@ -2721,10 +1740,14 @@ DisplayServer::WindowMode DisplayServerOSX::window_get_mode(WindowID p_window) c
}
}
- // all other discarded, return windowed.
+ // All other discarded, return windowed.
return WINDOW_MODE_WINDOWED;
}
+bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const {
+ return true;
+}
+
void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
_THREAD_SAFE_METHOD_
@@ -2734,7 +1757,7 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
switch (p_flag) {
case WINDOW_FLAG_RESIZE_DISABLED: {
wd.resize_disabled = p_enabled;
- if (wd.fullscreen) { //fullscreen window should be resizable, style will be applied on exiting fs
+ if (wd.fullscreen) { // Fullscreen window should be resizable, style will be applied on exiting fullscreen.
return;
}
if (p_enabled) {
@@ -2744,7 +1767,7 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
}
} break;
case WINDOW_FLAG_BORDERLESS: {
- // OrderOut prevents a lose focus bug with the window
+ // OrderOut prevents a lose focus bug with the window.
if ([wd.window_object isVisible]) {
[wd.window_object orderOut:nil];
}
@@ -2752,15 +1775,14 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
if (p_enabled) {
[wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
} else {
- if (wd.layered_window)
- _set_window_per_pixel_transparency_enabled(false, p_window);
+ _set_window_per_pixel_transparency_enabled(false, p_window);
[wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
- // Force update of the window styles
+ // Force update of the window styles.
NSRect frameRect = [wd.window_object frame];
[wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
[wd.window_object setFrame:frameRect display:NO];
}
- _update_window(wd);
+ _update_window_style(wd);
if ([wd.window_object isVisible]) {
if (wd.no_focus) {
[wd.window_object orderFront:nil];
@@ -2781,9 +1803,8 @@ void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo
}
} break;
case WINDOW_FLAG_TRANSPARENT: {
- wd.layered_window = p_enabled;
if (p_enabled) {
- [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; // force borderless
+ [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; // Force borderless.
} else if (!wd.borderless) {
[wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
}
@@ -2886,28 +1907,103 @@ void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_
wd.im_position = p_pos;
}
-bool DisplayServerOSX::get_swap_cancel_ok() {
- return false;
+DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const {
+ Point2i position = p_position;
+ position.y *= -1;
+ position += _get_screens_origin();
+ position /= screen_get_max_scale();
+
+ NSInteger wnum = [NSWindow windowNumberAtPoint:NSMakePoint(position.x, position.y) belowWindowWithWindowNumber:0 /*topmost*/];
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if ([E->get().window_object windowNumber] == wnum) {
+ return E->key();
+ }
+ }
+ return INVALID_WINDOW_ID;
}
-void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
+int64_t DisplayServerOSX::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
+ ERR_FAIL_COND_V(!windows.has(p_window), 0);
+ switch (p_handle_type) {
+ case DISPLAY_HANDLE: {
+ return 0; // Not supported.
+ }
+ case WINDOW_HANDLE: {
+ return (int64_t)windows[p_window].window_object;
+ }
+ case WINDOW_VIEW: {
+ return (int64_t)windows[p_window].window_view;
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
+void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
_THREAD_SAFE_METHOD_
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].instance_id = p_instance;
+}
- if (cursor_shape == p_shape) {
- return;
+ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ return windows[p_window].instance_id;
+}
+
+void DisplayServerOSX::gl_window_make_current(DisplayServer::WindowID p_window_id) {
+#if defined(GLES3_ENABLED)
+ gl_manager->window_make_current(p_window_id);
+#endif
+}
+
+void DisplayServerOSX::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ gl_manager->set_use_vsync(p_vsync_mode);
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
}
+#endif
+}
- if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
- cursor_shape = p_shape;
- return;
+DisplayServer::VSyncMode DisplayServerOSX::window_get_vsync_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+#if defined(GLES3_ENABLED)
+ if (gl_manager) {
+ return (gl_manager->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED);
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ return context_vulkan->get_vsync_mode(p_window);
}
+#endif
+ return DisplayServer::VSYNC_ENABLED;
+}
+
+Point2i DisplayServerOSX::ime_get_selection() const {
+ return im_selection;
+}
- if (cursors[p_shape] != nullptr) {
- [cursors[p_shape] set];
+String DisplayServerOSX::ime_get_text() const {
+ return im_text;
+}
+
+void DisplayServerOSX::cursor_update_shape() {
+ _THREAD_SAFE_METHOD_
+
+ if (cursors[cursor_shape] != nullptr) {
+ [cursors[cursor_shape] set];
} else {
- switch (p_shape) {
+ switch (cursor_shape) {
case CURSOR_ARROW:
[[NSCursor arrowCursor] set];
break;
@@ -2936,16 +2032,16 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
[[NSCursor operationNotAllowedCursor] set];
break;
case CURSOR_VSIZE:
- [_cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set];
+ [_cursor_from_selector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set];
break;
case CURSOR_HSIZE:
- [_cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set];
+ [_cursor_from_selector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set];
break;
case CURSOR_BDIAGSIZE:
- [_cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set];
+ [_cursor_from_selector(@selector(_windowResizeNorthEastSouthWestCursor)) set];
break;
case CURSOR_FDIAGSIZE:
- [_cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set];
+ [_cursor_from_selector(@selector(_windowResizeNorthWestSouthEastCursor)) set];
break;
case CURSOR_MOVE:
[[NSCursor arrowCursor] set];
@@ -2957,14 +2053,30 @@ void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
[[NSCursor resizeLeftRightCursor] set];
break;
case CURSOR_HELP:
- [_cursorFromSelector(@selector(_helpCursor)) set];
+ [_cursor_from_selector(@selector(_helpCursor)) set];
break;
default: {
}
}
}
+}
+
+void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (cursor_shape == p_shape) {
+ return;
+ }
cursor_shape = p_shape;
+
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
+ return;
+ }
+
+ cursor_update_shape();
}
DisplayServerOSX::CursorShape DisplayServerOSX::cursor_get_shape() const {
@@ -3059,7 +2171,6 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
- [cursors[p_shape] release];
cursors[p_shape] = cursor;
Vector<Variant> params;
@@ -3072,94 +2183,32 @@ void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape
[cursor set];
}
}
-
- [imgrep release];
- [nsimage release];
} else {
- // Reset to default system cursor
+ // Reset to default system cursor.
if (cursors[p_shape] != nullptr) {
- [cursors[p_shape] release];
cursors[p_shape] = nullptr;
}
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- cursor_set_shape(c);
+ cursor_update_shape();
cursors_cache.erase(p_shape);
}
}
-struct LayoutInfo {
- String name;
- String code;
-};
-
-static Vector<LayoutInfo> kbd_layouts;
-static int current_layout = 0;
-static bool keyboard_layout_dirty = true;
-static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
- kbd_layouts.clear();
- current_layout = 0;
- keyboard_layout_dirty = true;
-}
-
-void _update_keyboard_layouts() {
- @autoreleasepool {
- TISInputSourceRef cur_source = TISCopyCurrentKeyboardInputSource();
- NSString *cur_name = (NSString *)TISGetInputSourceProperty(cur_source, kTISPropertyLocalizedName);
- CFRelease(cur_source);
-
- // Enum IME layouts
- NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
- NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
- for (NSUInteger i = 0; i < [list_ime count]; i++) {
- LayoutInfo ly;
- NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
- ly.name.parse_utf8([name UTF8String]);
-
- NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyInputSourceLanguages);
- ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
- kbd_layouts.push_back(ly);
-
- if ([name isEqualToString:cur_name]) {
- current_layout = kbd_layouts.size() - 1;
- }
- }
- [list_ime release];
-
- // Enum plain keyboard layouts
- NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
- NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
- for (NSUInteger i = 0; i < [list_kbd count]; i++) {
- LayoutInfo ly;
- NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
- ly.name.parse_utf8([name UTF8String]);
-
- NSArray *langs = (NSArray *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyInputSourceLanguages);
- ly.code.parse_utf8([(NSString *)[langs objectAtIndex:0] UTF8String]);
- kbd_layouts.push_back(ly);
-
- if ([name isEqualToString:cur_name]) {
- current_layout = kbd_layouts.size() - 1;
- }
- }
- [list_kbd release];
- }
-
- keyboard_layout_dirty = false;
+bool DisplayServerOSX::get_swap_cancel_ok() {
+ return false;
}
int DisplayServerOSX::keyboard_get_layout_count() const {
if (keyboard_layout_dirty) {
- _update_keyboard_layouts();
+ const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts();
}
return kbd_layouts.size();
}
void DisplayServerOSX::keyboard_set_current_layout(int p_index) {
if (keyboard_layout_dirty) {
- _update_keyboard_layouts();
+ const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts();
}
ERR_FAIL_INDEX(p_index, kbd_layouts.size());
@@ -3167,31 +2216,29 @@ void DisplayServerOSX::keyboard_set_current_layout(int p_index) {
NSString *cur_name = [NSString stringWithUTF8String:kbd_layouts[p_index].name.utf8().get_data()];
NSDictionary *filter_kbd = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardLayout };
- NSArray *list_kbd = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_kbd, false);
+ NSArray *list_kbd = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_kbd, false);
for (NSUInteger i = 0; i < [list_kbd count]; i++) {
- NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
+ NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i], kTISPropertyLocalizedName);
if ([name isEqualToString:cur_name]) {
- TISSelectInputSource((TISInputSourceRef)[list_kbd objectAtIndex:i]);
+ TISSelectInputSource((__bridge TISInputSourceRef)[list_kbd objectAtIndex:i]);
break;
}
}
- [list_kbd release];
NSDictionary *filter_ime = @{ (NSString *)kTISPropertyInputSourceType : (NSString *)kTISTypeKeyboardInputMode };
- NSArray *list_ime = (NSArray *)TISCreateInputSourceList((CFDictionaryRef)filter_ime, false);
+ NSArray *list_ime = (__bridge NSArray *)TISCreateInputSourceList((__bridge CFDictionaryRef)filter_ime, false);
for (NSUInteger i = 0; i < [list_ime count]; i++) {
- NSString *name = (NSString *)TISGetInputSourceProperty((TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
+ NSString *name = (__bridge NSString *)TISGetInputSourceProperty((__bridge TISInputSourceRef)[list_ime objectAtIndex:i], kTISPropertyLocalizedName);
if ([name isEqualToString:cur_name]) {
- TISSelectInputSource((TISInputSourceRef)[list_ime objectAtIndex:i]);
+ TISSelectInputSource((__bridge TISInputSourceRef)[list_ime objectAtIndex:i]);
break;
}
}
- [list_ime release];
}
int DisplayServerOSX::keyboard_get_current_layout() const {
if (keyboard_layout_dirty) {
- _update_keyboard_layouts();
+ const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts();
}
return current_layout;
@@ -3199,7 +2246,7 @@ int DisplayServerOSX::keyboard_get_current_layout() const {
String DisplayServerOSX::keyboard_get_layout_language(int p_index) const {
if (keyboard_layout_dirty) {
- _update_keyboard_layouts();
+ const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts();
}
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
@@ -3208,7 +2255,7 @@ String DisplayServerOSX::keyboard_get_layout_language(int p_index) const {
String DisplayServerOSX::keyboard_get_layout_name(int p_index) const {
if (keyboard_layout_dirty) {
- _update_keyboard_layouts();
+ const_cast<DisplayServerOSX *>(this)->_update_keyboard_layouts();
}
ERR_FAIL_INDEX_V(p_index, kbd_layouts.size(), "");
@@ -3222,124 +2269,8 @@ Key DisplayServerOSX::keyboard_get_keycode_from_physical(Key p_keycode) const {
Key modifiers = p_keycode & KeyModifierMask::MODIFIER_MASK;
Key keycode_no_mod = p_keycode & KeyModifierMask::CODE_MASK;
- unsigned int osx_keycode = unmapKey((Key)keycode_no_mod);
- return (Key)(remapKey(osx_keycode, 0) | modifiers);
-}
-
-void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
- Ref<InputEvent> ev = p_event;
- Input::get_singleton()->parse_input_event(ev);
-}
-
-void DisplayServerOSX::_release_pressed_events() {
- _THREAD_SAFE_METHOD_
- if (Input::get_singleton()) {
- Input::get_singleton()->release_pressed_events();
- }
-}
-
-NSMenu *DisplayServerOSX::_get_dock_menu() const {
- return dock_menu;
-}
-
-void DisplayServerOSX::_menu_callback(id p_sender) {
- if (![p_sender representedObject]) {
- return;
- }
-
- GlobalMenuItem *value = [p_sender representedObject];
-
- if (value) {
- if (value->checkable) {
- if ([p_sender state] == NSControlStateValueOff) {
- [p_sender setState:NSControlStateValueOn];
- } else {
- [p_sender setState:NSControlStateValueOff];
- }
- }
-
- if (value->callback != Callable()) {
- Variant tag = value->meta;
- Variant *tagp = &tag;
- Variant ret;
- Callable::CallError ce;
- value->callback.call((const Variant **)&tagp, 1, ret, ce);
- }
- }
-}
-
-void DisplayServerOSX::_send_event(NSEvent *p_event) {
- // special case handling of command-period, which is traditionally a special
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
- if ([p_event type] == NSEventTypeKeyDown) {
- if (([p_event modifierFlags] & NSEventModifierFlagCommand) && [p_event keyCode] == 0x2f) {
- Ref<InputEventKey> k;
- k.instantiate();
-
- _get_key_modifier_state([p_event modifierFlags], k);
- k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
- k->set_pressed(true);
- k->set_keycode(Key::PERIOD);
- k->set_physical_keycode(Key::PERIOD);
- k->set_echo([p_event isARepeat]);
-
- Input::get_singleton()->parse_input_event(k);
- }
- }
-}
-
-void DisplayServerOSX::_process_key_events() {
- Ref<InputEventKey> k;
- for (int i = 0; i < key_event_pos; i++) {
- const KeyEvent &ke = key_event_buffer[i];
- if (ke.raw) {
- // Non IME input - no composite characters, pass events as is
- k.instantiate();
-
- k->set_window_id(ke.window_id);
- _get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(ke.keycode);
- k->set_physical_keycode((Key)ke.physical_keycode);
- k->set_unicode(ke.unicode);
-
- _push_input(k);
- } else {
- // IME input
- if ((i == 0 && ke.keycode == Key::NONE) || (i > 0 && key_event_buffer[i - 1].keycode == Key::NONE)) {
- k.instantiate();
-
- k->set_window_id(ke.window_id);
- _get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(Key::NONE);
- k->set_physical_keycode(Key::NONE);
- k->set_unicode(ke.unicode);
-
- _push_input(k);
- }
- if (ke.keycode != Key::NONE) {
- k.instantiate();
-
- k->set_window_id(ke.window_id);
- _get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(ke.keycode);
- k->set_physical_keycode((Key)ke.physical_keycode);
-
- if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == Key::NONE) {
- k->set_unicode(key_event_buffer[i + 1].unicode);
- }
-
- _push_input(k);
- }
- }
- }
-
- key_event_pos = 0;
+ unsigned int osx_keycode = KeyMappingOSX::unmap_key((Key)keycode_no_mod);
+ return (Key)(KeyMappingOSX::remap_key(osx_keycode, 0) | modifiers);
}
void DisplayServerOSX::process_events() {
@@ -3367,8 +2298,8 @@ void DisplayServerOSX::process_events() {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
WindowData &wd = E->get();
if (wd.mpath.size() > 0) {
- const Vector2 mpos = _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
- if (Geometry2D::is_point_in_polygon(mpos, wd.mpath)) {
+ update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ if (Geometry2D::is_point_in_polygon(wd.mouse_pos, wd.mpath)) {
if ([wd.window_object ignoresMouseEvents]) {
[wd.window_object setIgnoresMouseEvents:NO];
}
@@ -3383,9 +2314,6 @@ void DisplayServerOSX::process_events() {
}
}
}
-
- [autoreleasePool drain];
- autoreleasePool = [[NSAutoreleasePool alloc] init];
}
void DisplayServerOSX::force_process_and_drop_events() {
@@ -3396,6 +2324,18 @@ void DisplayServerOSX::force_process_and_drop_events() {
drop_events = false;
}
+void DisplayServerOSX::release_rendering_thread() {
+}
+
+void DisplayServerOSX::make_rendering_thread() {
+}
+
+void DisplayServerOSX::swap_buffers() {
+#if defined(GLES3_ENABLED)
+ gl_manager->swap_buffers();
+#endif
+}
+
void DisplayServerOSX::set_native_icon(const String &p_filename) {
_THREAD_SAFE_METHOD_
@@ -3408,10 +2348,10 @@ void DisplayServerOSX::set_native_icon(const String &p_filename) {
f->get_buffer((uint8_t *)&data.write[0], len);
memdelete(f);
- NSData *icon_data = [[[NSData alloc] initWithBytes:&data.write[0] length:len] autorelease];
+ NSData *icon_data = [[NSData alloc] initWithBytes:&data.write[0] length:len];
ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
- NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
+ NSImage *icon = [[NSImage alloc] initWithData:icon_data];
ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
[NSApp setApplicationIconImage:icon];
@@ -3454,112 +2394,6 @@ void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
[nsimg addRepresentation:imgrep];
[NSApp setApplicationIconImage:nsimg];
-
- [imgrep release];
- [nsimg release];
-}
-
-void DisplayServerOSX::window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window) {
- _THREAD_SAFE_METHOD_
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->swap_buffers();
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- context_vulkan->set_vsync_mode(p_window, p_vsync_mode);
- }
-#endif
-}
-
-DisplayServer::VSyncMode DisplayServerOSX::window_get_vsync_mode(WindowID p_window) const {
- _THREAD_SAFE_METHOD_
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- return (gl_manager->is_using_vsync() ? DisplayServer::VSyncMode::VSYNC_ENABLED : DisplayServer::VSyncMode::VSYNC_DISABLED);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- return context_vulkan->get_vsync_mode(p_window);
- }
-#endif
- return DisplayServer::VSYNC_ENABLED;
-}
-
-Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
- Vector<String> drivers;
-
-#if defined(VULKAN_ENABLED)
- drivers.push_back("vulkan");
-#endif
-#if defined(GLES3_ENABLED)
- drivers.push_back("opengl3");
-#endif
-
- return drivers;
-}
-
-void DisplayServerOSX::gl_window_make_current(DisplayServer::WindowID p_window_id) {
-#if defined(GLES3_ENABLED)
- gl_manager->window_make_current(p_window_id);
-#endif
-}
-
-Point2i DisplayServerOSX::ime_get_selection() const {
- return im_selection;
-}
-
-String DisplayServerOSX::ime_get_text() const {
- return im_text;
-}
-
-DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const {
- Point2i position = p_position;
- position.y *= -1;
- position += _get_screens_origin();
- position /= screen_get_max_scale();
-
- NSInteger wnum = [NSWindow windowNumberAtPoint:NSMakePoint(position.x, position.y) belowWindowWithWindowNumber:0 /*topmost*/];
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- if ([E->get().window_object windowNumber] == wnum) {
- return E->key();
- }
- }
- return INVALID_WINDOW_ID;
-}
-
-int64_t DisplayServerOSX::window_get_native_handle(HandleType p_handle_type, WindowID p_window) const {
- ERR_FAIL_COND_V(!windows.has(p_window), 0);
- switch (p_handle_type) {
- case DISPLAY_HANDLE: {
- return 0; // Not supported.
- }
- case WINDOW_HANDLE: {
- return (int64_t)windows[p_window].window_object;
- }
- case WINDOW_VIEW: {
- return (int64_t)windows[p_window].window_view;
- }
- default: {
- return 0;
- }
- }
-}
-
-void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
- _THREAD_SAFE_METHOD_
-
- ERR_FAIL_COND(!windows.has(p_window));
- windows[p_window].instance_id = p_instance;
-}
-
-ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) const {
- _THREAD_SAFE_METHOD_
-
- ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
- return windows[p_window].instance_id;
}
DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
@@ -3570,179 +2404,48 @@ DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, W
return ds;
}
-DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, VSyncMode p_vsync_mode, const Rect2i &p_rect) {
- WindowID id;
- const float scale = screen_get_max_scale();
- {
- WindowData wd;
-
- wd.window_delegate = [[GodotWindowDelegate alloc] init];
- ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate");
- [wd.window_delegate setWindowID:window_id_counter];
-
- Point2i position = p_rect.position;
- // OS X native y-coordinate relative to _get_screens_origin() is negative,
- // Godot passes a positive value
- position.y *= -1;
- position += _get_screens_origin();
-
- // initWithContentRect uses bottom-left corner of the window’s frame as origin.
- wd.window_object = [[GodotWindow alloc]
- initWithContentRect:NSMakeRect(position.x / scale, (position.y - p_rect.size.height) / scale, p_rect.size.width / scale, p_rect.size.height / scale)
- styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
- backing:NSBackingStoreBuffered
- defer:NO];
- ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window");
-
- wd.window_view = [[GodotContentView alloc] init];
- ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view");
- [wd.window_view setWindowID:window_id_counter];
- [wd.window_view setWantsLayer:TRUE];
-
- [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- [wd.window_object setContentView:wd.window_view];
- [wd.window_object setDelegate:wd.window_delegate];
- [wd.window_object setAcceptsMouseMovedEvents:YES];
- [wd.window_object setRestorable:NO];
- [wd.window_object setColorSpace:[NSColorSpace sRGBColorSpace]];
-
- if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)]) {
- [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
- }
-
- CALayer *layer = [wd.window_view layer];
- if (layer) {
- layer.contentsScale = scale;
- }
+Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
+ Vector<String> drivers;
#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- Error err = context_vulkan->window_create(window_id_counter, p_vsync_mode, wd.window_view, p_rect.size.width, p_rect.size.height);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
- }
-#endif
-#if defined(GLES3_ENABLED)
- if (gl_manager) {
- Error err = gl_manager->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
- ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create an OpenGL context");
- }
+ drivers.push_back("vulkan");
#endif
- id = window_id_counter++;
- windows[id] = wd;
- }
-
- WindowData &wd = windows[id];
- window_set_mode(p_mode, id);
-
- const NSRect contentRect = [wd.window_view frame];
- wd.size.width = contentRect.size.width * scale;
- wd.size.height = contentRect.size.height * scale;
-
- CALayer *layer = [wd.window_view layer];
- if (layer) {
- layer.contentsScale = scale;
- }
-
#if defined(GLES3_ENABLED)
- if (gl_manager) {
- gl_manager->window_resize(id, wd.size.width, wd.size.height);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (context_vulkan) {
- context_vulkan->window_resize(id, wd.size.width, wd.size.height);
- }
+ drivers.push_back("opengl3");
#endif
- return id;
-}
-
-void DisplayServerOSX::_dispatch_input_events(const Ref<InputEvent> &p_event) {
- ((DisplayServerOSX *)(get_singleton()))->_dispatch_input_event(p_event);
-}
-
-void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- _THREAD_SAFE_METHOD_
- if (!in_dispatch_input_event) {
- in_dispatch_input_event = true;
-
- Variant ev = p_event;
- Variant *evp = &ev;
- Variant ret;
- Callable::CallError ce;
-
- Ref<InputEventFromWindow> event_from_window = p_event;
- if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
- //send to a window
- if (windows.has(event_from_window->get_window_id())) {
- Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
- if (callable.is_null()) {
- return;
- }
- callable.call((const Variant **)&evp, 1, ret, ce);
- }
- } else {
- //send to all windows
- for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
- Callable callable = E->get().input_event_callback;
- if (callable.is_null()) {
- continue;
- }
- callable.call((const Variant **)&evp, 1, ret, ce);
- }
- }
-
- in_dispatch_input_event = false;
- }
-}
-
-void DisplayServerOSX::release_rendering_thread() {
-}
-
-void DisplayServerOSX::make_rendering_thread() {
+ return drivers;
}
-void DisplayServerOSX::swap_buffers() {
-#if defined(GLES3_ENABLED)
- gl_manager->swap_buffers();
-#endif
+void DisplayServerOSX::register_osx_driver() {
+ register_create_function("osx", create_func, get_rendering_drivers_func);
}
DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
r_error = OK;
- drop_events = false;
memset(cursors, 0, sizeof(cursors));
- cursor_shape = CURSOR_ARROW;
-
- key_event_pos = 0;
- mouse_mode = MOUSE_MODE_VISIBLE;
-
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- ERR_FAIL_COND(!eventSource);
- CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
+ event_source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ ERR_FAIL_COND(!event_source);
- keyboard_layout_dirty = true;
- displays_arrangement_dirty = true;
+ CGEventSourceSetLocalEventsSuppressionInterval(event_source, 0.0);
int screen_count = get_screen_count();
for (int i = 0; i < screen_count; i++) {
display_max_scale = fmax(display_max_scale, screen_get_scale(i));
}
- // Register to be notified on keyboard layout changes
+ // Register to be notified on keyboard layout changes.
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- nullptr, keyboard_layout_changed,
+ nullptr, _keyboard_layout_changed,
kTISNotifySelectedKeyboardInputSourceChanged, nullptr,
CFNotificationSuspensionBehaviorDeliverImmediately);
- // Register to be notified on displays arrangement changes
- CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, nullptr);
+ // Register to be notified on displays arrangement changes.
+ CGDisplayRegisterReconfigurationCallback(_displays_arrangement_changed, nullptr);
NSMenuItem *menu_item;
NSString *title;
@@ -3752,11 +2455,11 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
nsappname = [[NSProcessInfo processInfo] processName];
}
- // Setup Dock menu
+ // Setup Dock menu.
dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
- // Setup Apple menu
- apple_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ // Setup Apple menu.
+ apple_menu = [[NSMenu alloc] initWithTitle:@""];
title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
@@ -3766,7 +2469,6 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
[apple_menu setSubmenu:services forItem:menu_item];
[NSApp setServicesMenu:services];
- [services release];
[apple_menu addItem:[NSMenuItem separatorItem]];
@@ -3783,7 +2485,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
[apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
- // Add items to the menu bar
+ // Add items to the menu bar.
NSMenu *main_menu = [NSApp mainMenu];
menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
[main_menu setSubmenu:apple_menu forItem:menu_item];
@@ -3845,15 +2547,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
}
DisplayServerOSX::~DisplayServerOSX() {
- if (dock_menu) {
- [dock_menu release];
- }
-
- for (Map<String, NSMenu *>::Element *E = submenu.front(); E; E = E->next()) {
- [E->get() release];
- }
-
- //destroy all windows
+ // Destroy all windows.
for (Map<WindowID, WindowData>::Element *E = windows.front(); E;) {
Map<WindowID, WindowData>::Element *F = E;
E = E->next();
@@ -3861,7 +2555,7 @@ DisplayServerOSX::~DisplayServerOSX() {
[F->get().window_object close];
}
- //destroy drivers
+ // Destroy drivers.
#if defined(GLES3_ENABLED)
if (gl_manager) {
memdelete(gl_manager);
@@ -3882,11 +2576,7 @@ DisplayServerOSX::~DisplayServerOSX() {
#endif
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), nullptr, kTISNotifySelectedKeyboardInputSourceChanged, nullptr);
- CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, nullptr);
+ CGDisplayRemoveReconfigurationCallback(_displays_arrangement_changed, nullptr);
cursors_cache.clear();
}
-
-void DisplayServerOSX::register_osx_driver() {
- register_create_function("osx", create_func, get_rendering_drivers_func);
-}
diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h
index 0c2ac90206..931ce7e41a 100644
--- a/platform/osx/export/export_plugin.h
+++ b/platform/osx/export/export_plugin.h
@@ -87,7 +87,7 @@ class EditorExportPlatformOSX : public EditorExportPlatform {
for (int i = 0; i < pname.length(); i++) {
char32_t c = pname[i];
- if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) {
+ if (!(is_ascii_alphanumeric_char(c) || c == '-' || c == '.')) {
if (r_error) {
*r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c));
}
diff --git a/platform/osx/gl_manager_osx.h b/platform/osx/gl_manager_osx_legacy.h
index 0229b672a2..b5a1b9dd98 100644
--- a/platform/osx/gl_manager_osx.h
+++ b/platform/osx/gl_manager_osx_legacy.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gl_manager_osx.h */
+/* gl_manager_osx_legacy.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GL_MANAGER_OSX_H
-#define GL_MANAGER_OSX_H
+#ifndef GL_MANAGER_OSX_LEGACY_H
+#define GL_MANAGER_OSX_LEGACY_H
#if defined(OSX_ENABLED) && defined(GLES3_ENABLED)
@@ -50,29 +50,21 @@ public:
private:
struct GLWindow {
- GLWindow() { in_use = false; }
- bool in_use;
+ int width = 0;
+ int height = 0;
- DisplayServer::WindowID window_id;
- int width;
- int height;
-
- id window_view;
- NSOpenGLContext *context;
+ id window_view = nullptr;
+ NSOpenGLContext *context = nullptr;
};
- LocalVector<GLWindow> _windows;
-
- NSOpenGLContext *_shared_context = nullptr;
- GLWindow *_current_window;
+ Map<DisplayServer::WindowID, GLWindow> windows;
- Error _create_context(GLWindow &win);
- void _internal_set_current_window(GLWindow *p_win);
+ NSOpenGLContext *shared_context = nullptr;
+ DisplayServer::WindowID current_window = DisplayServer::INVALID_WINDOW_ID;
- GLWindow &get_window(unsigned int id) { return _windows[id]; }
- const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
+ Error create_context(GLWindow &win);
- bool use_vsync;
+ bool use_vsync = false;
ContextType context_type;
public:
@@ -80,7 +72,6 @@ public:
void window_destroy(DisplayServer::WindowID p_window_id);
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
- // get directly from the cached GLWindow
int window_get_width(DisplayServer::WindowID p_window_id = 0);
int window_get_height(DisplayServer::WindowID p_window_id = 0);
@@ -91,6 +82,7 @@ public:
void window_make_current(DisplayServer::WindowID p_window_id);
void window_update(DisplayServer::WindowID p_window_id);
+ void window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled);
Error initialize();
@@ -101,6 +93,5 @@ public:
~GLManager_OSX();
};
-#endif // defined(OSX_ENABLED) && defined(GLES3_ENABLED)
-
-#endif // GL_MANAGER_OSX_H
+#endif // OSX_ENABLED && GLES3_ENABLED
+#endif // GL_MANAGER_OSX_LEGACY_H
diff --git a/platform/osx/gl_manager_osx.mm b/platform/osx/gl_manager_osx_legacy.mm
index 3e70de8523..fbe64e32a3 100644
--- a/platform/osx/gl_manager_osx.mm
+++ b/platform/osx/gl_manager_osx_legacy.mm
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* gl_manager_osx.mm */
+/* gl_manager_osx_legacy.mm */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gl_manager_osx.h"
+#include "gl_manager_osx_legacy.h"
#ifdef OSX_ENABLED
#ifdef GLES3_ENABLED
@@ -36,7 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
-Error GLManager_OSX::_create_context(GLWindow &win) {
+Error GLManager_OSX::create_context(GLWindow &win) {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAClosestPolicy,
@@ -50,10 +50,10 @@ Error GLManager_OSX::_create_context(GLWindow &win) {
NSOpenGLPixelFormat *pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
ERR_FAIL_COND_V(pixel_format == nil, ERR_CANT_CREATE);
- win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:_shared_context];
+ win.context = [[NSOpenGLContext alloc] initWithFormat:pixel_format shareContext:shared_context];
ERR_FAIL_COND_V(win.context == nil, ERR_CANT_CREATE);
- if (_shared_context == nullptr) {
- _shared_context = win.context;
+ if (shared_context == nullptr) {
+ shared_context = win.context;
}
[win.context setView:win.window_view];
@@ -63,40 +63,27 @@ Error GLManager_OSX::_create_context(GLWindow &win) {
}
Error GLManager_OSX::window_create(DisplayServer::WindowID p_window_id, id p_view, int p_width, int p_height) {
- if (p_window_id >= (int)_windows.size()) {
- _windows.resize(p_window_id + 1);
- }
-
- GLWindow &win = _windows[p_window_id];
- win.in_use = true;
- win.window_id = p_window_id;
+ GLWindow win;
win.width = p_width;
win.height = p_height;
win.window_view = p_view;
- if (_create_context(win) != OK) {
- _windows.remove_at(_windows.size() - 1);
+ if (create_context(win) != OK) {
return FAILED;
}
- window_make_current(_windows.size() - 1);
+ windows[p_window_id] = win;
+ window_make_current(p_window_id);
return OK;
}
-void GLManager_OSX::_internal_set_current_window(GLWindow *p_win) {
- _current_window = p_win;
-}
-
void GLManager_OSX::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
- if (p_window_id == -1) {
+ if (!windows.has(p_window_id)) {
return;
}
- GLWindow &win = _windows[p_window_id];
- if (!win.in_use) {
- return;
- }
+ GLWindow &win = windows[p_window_id];
win.width = p_width;
win.height = p_height;
@@ -116,24 +103,37 @@ void GLManager_OSX::window_resize(DisplayServer::WindowID p_window_id, int p_wid
}
int GLManager_OSX::window_get_width(DisplayServer::WindowID p_window_id) {
- return get_window(p_window_id).width;
+ if (!windows.has(p_window_id)) {
+ return 0;
+ }
+
+ GLWindow &win = windows[p_window_id];
+ return win.width;
}
int GLManager_OSX::window_get_height(DisplayServer::WindowID p_window_id) {
- return get_window(p_window_id).height;
+ if (!windows.has(p_window_id)) {
+ return 0;
+ }
+
+ GLWindow &win = windows[p_window_id];
+ return win.height;
}
void GLManager_OSX::window_destroy(DisplayServer::WindowID p_window_id) {
- GLWindow &win = get_window(p_window_id);
- win.in_use = false;
+ if (!windows.has(p_window_id)) {
+ return;
+ }
- if (_current_window == &win) {
- _current_window = nullptr;
+ if (current_window == p_window_id) {
+ current_window = DisplayServer::INVALID_WINDOW_ID;
}
+
+ windows.erase(p_window_id);
}
void GLManager_OSX::release_current() {
- if (!_current_window) {
+ if (current_window == DisplayServer::INVALID_WINDOW_ID) {
return;
}
@@ -141,63 +141,59 @@ void GLManager_OSX::release_current() {
}
void GLManager_OSX::window_make_current(DisplayServer::WindowID p_window_id) {
- if (p_window_id == -1) {
- return;
- }
-
- GLWindow &win = _windows[p_window_id];
- if (!win.in_use) {
+ if (current_window == p_window_id) {
return;
}
-
- if (&win == _current_window) {
+ if (!windows.has(p_window_id)) {
return;
}
+ GLWindow &win = windows[p_window_id];
[win.context makeCurrentContext];
- _internal_set_current_window(&win);
+ current_window = p_window_id;
}
void GLManager_OSX::make_current() {
- if (!_current_window) {
+ if (current_window == DisplayServer::INVALID_WINDOW_ID) {
return;
}
- if (!_current_window->in_use) {
- WARN_PRINT("current window not in use!");
+ if (!windows.has(current_window)) {
return;
}
- [_current_window->context makeCurrentContext];
+
+ GLWindow &win = windows[current_window];
+ [win.context makeCurrentContext];
}
void GLManager_OSX::swap_buffers() {
- // NO NEED TO CALL SWAP BUFFERS for each window...
- // see https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glXSwapBuffers.xml
-
- if (!_current_window) {
- return;
- }
- if (!_current_window->in_use) {
- WARN_PRINT("current window not in use!");
- return;
+ for (Map<DisplayServer::WindowID, GLWindow>::Element *E = windows.front(); E; E = E->next()) {
+ [E->get().context flushBuffer];
}
- [_current_window->context flushBuffer];
}
void GLManager_OSX::window_update(DisplayServer::WindowID p_window_id) {
- if (p_window_id == -1) {
+ if (!windows.has(p_window_id)) {
return;
}
- GLWindow &win = _windows[p_window_id];
- if (!win.in_use) {
- return;
- }
+ GLWindow &win = windows[p_window_id];
+ [win.context update];
+}
- if (&win == _current_window) {
+void GLManager_OSX::window_set_per_pixel_transparency_enabled(DisplayServer::WindowID p_window_id, bool p_enabled) {
+ if (!windows.has(p_window_id)) {
return;
}
+ GLWindow &win = windows[p_window_id];
+ if (p_enabled) {
+ GLint opacity = 0;
+ [win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
+ } else {
+ GLint opacity = 1;
+ [win.context setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
+ }
[win.context update];
}
@@ -207,6 +203,7 @@ Error GLManager_OSX::initialize() {
void GLManager_OSX::set_use_vsync(bool p_use) {
use_vsync = p_use;
+
CGLContextObj ctx = CGLGetCurrentContext();
if (ctx) {
GLint swapInterval = p_use ? 1 : 0;
@@ -221,8 +218,6 @@ bool GLManager_OSX::is_using_vsync() const {
GLManager_OSX::GLManager_OSX(ContextType p_context_type) {
context_type = p_context_type;
- use_vsync = false;
- _current_window = nullptr;
}
GLManager_OSX::~GLManager_OSX() {
diff --git a/platform/osx/godot_application.h b/platform/osx/godot_application.h
new file mode 100644
index 0000000000..8d48a659f3
--- /dev/null
+++ b/platform/osx/godot_application.h
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* godot_application.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_APPLICATION_H
+#define GODOT_APPLICATION_H
+
+#include "core/os/os.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@interface GodotApplication : NSApplication
+@end
+
+#endif // GODOT_APPLICATION_H
diff --git a/platform/osx/godot_application.mm b/platform/osx/godot_application.mm
new file mode 100644
index 0000000000..00a58700e8
--- /dev/null
+++ b/platform/osx/godot_application.mm
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* godot_application.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_application.h"
+
+#include "display_server_osx.h"
+
+@implementation GodotApplication
+
+- (void)sendEvent:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ ds->send_event(event);
+ }
+
+ // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
+ // This works around an AppKit bug, where key up events while holding
+ // down the command key don't get sent to the key window.
+ if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
+ [[self keyWindow] sendEvent:event];
+ } else {
+ [super sendEvent:event];
+ }
+}
+
+@end
diff --git a/platform/osx/godot_application_delegate.h b/platform/osx/godot_application_delegate.h
new file mode 100644
index 0000000000..8eec762d8f
--- /dev/null
+++ b/platform/osx/godot_application_delegate.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* godot_application_delegate.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_APPLICATION_DELEGATE_H
+#define GODOT_APPLICATION_DELEGATE_H
+
+#include "core/os/os.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@interface GodotApplicationDelegate : NSObject
+- (void)forceUnbundledWindowActivationHackStep1;
+- (void)forceUnbundledWindowActivationHackStep2;
+- (void)forceUnbundledWindowActivationHackStep3;
+@end
+
+#endif // GODOT_APPLICATION_DELEGATE_H
diff --git a/platform/osx/godot_application_delegate.mm b/platform/osx/godot_application_delegate.mm
new file mode 100644
index 0000000000..be284ba543
--- /dev/null
+++ b/platform/osx/godot_application_delegate.mm
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* godot_application_delegate.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_application_delegate.h"
+
+#include "display_server_osx.h"
+#include "os_osx.h"
+
+@implementation GodotApplicationDelegate
+
+- (void)forceUnbundledWindowActivationHackStep1 {
+ // Step 1: Switch focus to macOS SystemUIServer process.
+ // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
+ for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) {
+ [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+ break;
+ }
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep2)
+ withObject:nil
+ afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep2 {
+ // Step 2: Register app as foreground process.
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep3 {
+ // Step 3: Switch focus back to app window.
+ [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notice {
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
+ // If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
+ }
+}
+
+- (void)applicationDidResignActive:(NSNotification *)notification {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+}
+
+- (void)applicationDidBecomeActive:(NSNotification *)notification {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+}
+
+- (void)globalMenuCallback:(id)sender {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ return ds->menu_callback(sender);
+ }
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ return ds->get_dock_menu();
+ } else {
+ return nullptr;
+ }
+}
+
+- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
+ // Note: may be called called before main loop init!
+ OS_OSX *os = (OS_OSX *)OS::get_singleton();
+ if (os) {
+ os->set_open_with_filename(String::utf8([filename UTF8String]));
+ }
+
+#ifdef TOOLS_ENABLED
+ // Open new instance.
+ if (os && os->get_main_loop()) {
+ List<String> args;
+ args.push_back(os->get_open_with_filename());
+ String exec = os->get_executable_path();
+ os->create_process(exec, args);
+ }
+#endif
+ return YES;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (ds) {
+ ds->send_window_event(ds->get_window(DisplayServerOSX::MAIN_WINDOW_ID), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ return NSTerminateCancel;
+}
+
+- (void)showAbout:(id)sender {
+ OS_OSX *os = (OS_OSX *)OS::get_singleton();
+ if (os && os->get_main_loop()) {
+ os->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+ }
+}
+
+@end
diff --git a/platform/osx/godot_content_view.h b/platform/osx/godot_content_view.h
new file mode 100644
index 0000000000..7942d716dc
--- /dev/null
+++ b/platform/osx/godot_content_view.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* godot_content_view.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_CONTENT_VIEW_H
+#define GODOT_CONTENT_VIEW_H
+
+#include "servers/display_server.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+#if defined(GLES3_ENABLED)
+#import <AppKit/NSOpenGLView.h>
+#define RootView NSOpenGLView
+#else
+#define RootView NSView
+#endif
+
+#import <QuartzCore/CAMetalLayer.h>
+
+@interface GodotContentView : RootView <NSTextInputClient> {
+ DisplayServer::WindowID window_id;
+ NSTrackingArea *tracking_area;
+ NSMutableAttributedString *marked_text;
+ bool ime_input_event_in_progress;
+ bool mouse_down_control;
+ bool ignore_momentum_scroll;
+}
+
+- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor;
+- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy;
+- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed;
+- (void)setWindowID:(DisplayServer::WindowID)wid;
+- (void)cancelComposition;
+
+@end
+
+#endif // GODOT_CONTENT_VIEW_H
diff --git a/platform/osx/godot_content_view.mm b/platform/osx/godot_content_view.mm
new file mode 100644
index 0000000000..4e831e1ccc
--- /dev/null
+++ b/platform/osx/godot_content_view.mm
@@ -0,0 +1,760 @@
+/*************************************************************************/
+/* godot_content_view.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_content_view.h"
+
+#include "display_server_osx.h"
+#include "key_mapping_osx.h"
+
+@implementation GodotContentView
+
+- (id)init {
+ self = [super init];
+ window_id = DisplayServer::INVALID_WINDOW_ID;
+ tracking_area = nil;
+ ime_input_event_in_progress = false;
+ mouse_down_control = false;
+ ignore_momentum_scroll = false;
+ [self updateTrackingAreas];
+
+ if (@available(macOS 10.13, *)) {
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
+#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM.
+ } else {
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+#endif
+ }
+ marked_text = [[NSMutableAttributedString alloc] init];
+ return self;
+}
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
+// MARK: Backing Layer
+
+- (CALayer *)makeBackingLayer {
+ return [[CAMetalLayer class] layer];
+}
+
+- (void)updateLayer {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ ds->window_update(window_id);
+ [super updateLayer];
+}
+
+- (BOOL)wantsUpdateLayer {
+ return YES;
+}
+
+- (BOOL)isOpaque {
+ return YES;
+}
+
+// MARK: IME
+
+- (BOOL)hasMarkedText {
+ return (marked_text.length > 0);
+}
+
+- (NSRange)markedRange {
+ return NSMakeRange(0, marked_text.length);
+}
+
+- (NSRange)selectedRange {
+ static const NSRange kEmptyRange = { NSNotFound, 0 };
+ return kEmptyRange;
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ marked_text = [[NSMutableAttributedString alloc] initWithAttributedString:aString];
+ } else {
+ marked_text = [[NSMutableAttributedString alloc] initWithString:aString];
+ }
+ if (marked_text.length == 0) {
+ [self unmarkText];
+ return;
+ }
+
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ if (wd.im_active) {
+ ime_input_event_in_progress = true;
+ ds->update_im_text(Point2i(selectedRange.location, selectedRange.length), String::utf8([[marked_text mutableString] UTF8String]));
+ }
+}
+
+- (void)unmarkText {
+ ime_input_event_in_progress = false;
+ [[marked_text mutableString] setString:@""];
+
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ if (wd.im_active) {
+ ds->update_im_text(Point2i(), String());
+ }
+}
+
+- (NSArray *)validAttributesForMarkedText {
+ return [NSArray array];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ return nil;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
+ return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return NSMakeRect(0, 0, 0, 0);
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ const NSRect content_rect = [wd.window_view frame];
+ const float scale = ds->screen_get_max_scale();
+ NSRect point_in_window_rect = NSMakeRect(wd.im_position.x / scale, content_rect.size.height - (wd.im_position.y / scale) - 1, 0, 0);
+ NSPoint point_on_screen = [wd.window_object convertRectToScreen:point_in_window_rect].origin;
+
+ return NSMakeRect(point_on_screen.x, point_on_screen.y, 0, 0);
+}
+
+- (void)cancelComposition {
+ [self unmarkText];
+ [[NSTextInputContext currentInputContext] discardMarkedText];
+}
+
+- (void)insertText:(id)aString {
+ [self insertText:aString replacementRange:NSMakeRange(0, 0)];
+}
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
+ NSEvent *event = [NSApp currentEvent];
+
+ NSString *characters;
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ characters = [aString string];
+ } else {
+ characters = (NSString *)aString;
+ }
+
+ NSCharacterSet *ctrl_chars = [NSCharacterSet controlCharacterSet];
+ NSCharacterSet *wsnl_chars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ if ([characters rangeOfCharacterFromSet:ctrl_chars].length && [characters rangeOfCharacterFromSet:wsnl_chars].length == 0) {
+ [[NSTextInputContext currentInputContext] discardMarkedText];
+ [self cancelComposition];
+ return;
+ }
+
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ [self cancelComposition];
+ return;
+ }
+
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
+ if ((codepoint & 0xFF00) == 0xF700) {
+ continue;
+ }
+
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = false;
+ ke.raw = false; // IME input event.
+ ke.keycode = Key::NONE;
+ ke.physical_keycode = Key::NONE;
+ ke.unicode = codepoint;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+ [self cancelComposition];
+}
+
+// MARK: Drag and drop
+
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return NO;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ if (!wd.drop_files_callback.is_null()) {
+ Vector<String> files;
+ NSPasteboard *pboard = [sender draggingPasteboard];
+
+ if (@available(macOS 10.13, *)) {
+ NSArray *items = pboard.pasteboardItems;
+ for (NSPasteboardItem *item in items) {
+ NSString *url = [item stringForType:NSPasteboardTypeFileURL];
+ NSString *file = [NSURL URLWithString:url].path;
+ files.push_back(String::utf8([file UTF8String]));
+ }
+#if !defined(__aarch64__) // Do not build deprectead 10.13 code on ARM.
+ } else {
+ NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
+ for (NSString *file in filenames) {
+ files.push_back(String::utf8([file UTF8String]));
+ }
+#endif
+ }
+
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
+
+ return NO;
+}
+
+// MARK: Focus
+
+- (BOOL)canBecomeKeyView {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return YES;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ return !wd.no_focus;
+}
+
+- (BOOL)acceptsFirstResponder {
+ return YES;
+}
+
+// MARK: Mouse
+
+- (void)cursorUpdate:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds) {
+ return;
+ }
+
+ ds->cursor_update_shape();
+}
+
+- (void)processMouseEvent:(NSEvent *)event index:(MouseButton)index mask:(MouseButton)mask pressed:(bool)pressed {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ MouseButton last_button_state = ds->mouse_get_button_state();
+
+ if (pressed) {
+ last_button_state |= mask;
+ } else {
+ last_button_state &= (MouseButton)~mask;
+ }
+ ds->mouse_set_button_state(last_button_state);
+
+ Ref<InputEventMouseButton> mb;
+ mb.instantiate();
+ mb->set_window_id(window_id);
+ ds->update_mouse_pos(wd, [event locationInWindow]);
+ ds->get_key_modifier_state([event modifierFlags], mb);
+ mb->set_button_index(index);
+ mb->set_pressed(pressed);
+ mb->set_position(wd.mouse_pos);
+ mb->set_global_position(wd.mouse_pos);
+ mb->set_button_mask(last_button_state);
+ if (index == MouseButton::LEFT && pressed) {
+ mb->set_double_click([event clickCount] == 2);
+ }
+
+ Input::get_singleton()->parse_input_event(mb);
+}
+
+- (void)mouseDown:(NSEvent *)event {
+ if (([event modifierFlags] & NSEventModifierFlagControl)) {
+ mouse_down_control = true;
+ [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+ } else {
+ mouse_down_control = false;
+ [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:true];
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)mouseUp:(NSEvent *)event {
+ if (mouse_down_control) {
+ [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+ } else {
+ [self processMouseEvent:event index:MouseButton::LEFT mask:MouseButton::MASK_LEFT pressed:false];
+ }
+}
+
+- (void)mouseMoved:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
+ NSPoint mpos = [event locationInWindow];
+
+ if (ds->update_mouse_wrap(wd, delta, mpos, [event timestamp])) {
+ return;
+ }
+
+ Ref<InputEventMouseMotion> mm;
+ mm.instantiate();
+
+ mm->set_window_id(window_id);
+ mm->set_button_mask(ds->mouse_get_button_state());
+ ds->update_mouse_pos(wd, mpos);
+ mm->set_position(wd.mouse_pos);
+ mm->set_pressure([event pressure]);
+ if ([event subtype] == NSEventSubtypeTabletPoint) {
+ const NSPoint p = [event tilt];
+ mm->set_tilt(Vector2(p.x, p.y));
+ }
+ mm->set_global_position(wd.mouse_pos);
+ mm->set_velocity(Input::get_singleton()->get_last_mouse_velocity());
+ const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * ds->screen_get_max_scale();
+ mm->set_relative(relativeMotion);
+ ds->get_key_modifier_state([event modifierFlags], mm);
+
+ Input::get_singleton()->parse_input_event(mm);
+}
+
+- (void)rightMouseDown:(NSEvent *)event {
+ [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:true];
+}
+
+- (void)rightMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)rightMouseUp:(NSEvent *)event {
+ [self processMouseEvent:event index:MouseButton::RIGHT mask:MouseButton::MASK_RIGHT pressed:false];
+}
+
+- (void)otherMouseDown:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:true];
+ } else if ((int)[event buttonNumber] == 3) {
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:true];
+ } else if ((int)[event buttonNumber] == 4) {
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:true];
+ } else {
+ return;
+ }
+}
+
+- (void)otherMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)otherMouseUp:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ [self processMouseEvent:event index:MouseButton::MIDDLE mask:MouseButton::MASK_MIDDLE pressed:false];
+ } else if ((int)[event buttonNumber] == 3) {
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON1 mask:MouseButton::MASK_XBUTTON1 pressed:false];
+ } else if ((int)[event buttonNumber] == 4) {
+ [self processMouseEvent:event index:MouseButton::MB_XBUTTON2 mask:MouseButton::MASK_XBUTTON2 pressed:false];
+ } else {
+ return;
+ }
+}
+
+- (void)mouseExited:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT);
+ }
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ if (ds->mouse_get_mode() != DisplayServer::MOUSE_MODE_CAPTURED) {
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER);
+ }
+
+ ds->cursor_update_shape();
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ Ref<InputEventMagnifyGesture> ev;
+ ev.instantiate();
+ ev->set_window_id(window_id);
+ ds->get_key_modifier_state([event modifierFlags], ev);
+ ds->update_mouse_pos(wd, [event locationInWindow]);
+ ev->set_position(wd.mouse_pos);
+ ev->set_factor([event magnification] + 1.0);
+
+ Input::get_singleton()->parse_input_event(ev);
+}
+
+- (void)updateTrackingAreas {
+ if (tracking_area != nil) {
+ [self removeTrackingArea:tracking_area];
+ }
+
+ NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingCursorUpdate | NSTrackingInVisibleRect;
+ tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil];
+
+ [self addTrackingArea:tracking_area];
+ [super updateTrackingAreas];
+}
+
+// MARK: Keyboard
+
+- (void)keyDown:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ ignore_momentum_scroll = true;
+
+ // Ignore all input if IME input is in progress.
+ if (!ime_input_event_in_progress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) {
+ // Fallback unicode character handler used if IME is not active.
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
+
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
+ ke.raw = true;
+ ke.unicode = codepoint;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
+ ke.raw = false;
+ ke.unicode = 0;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+ }
+
+ // Pass events to IME handler
+ if (wd.im_active) {
+ [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+ }
+}
+
+- (void)flagsChanged:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+ ignore_momentum_scroll = true;
+
+ // Ignore all input if IME input is in progress
+ if (!ime_input_event_in_progress) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.echo = false;
+ ke.raw = true;
+
+ int key = [event keyCode];
+ int mod = [event modifierFlags];
+
+ if (key == 0x36 || key == 0x37) {
+ if (mod & NSEventModifierFlagCommand) {
+ mod &= ~NSEventModifierFlagCommand;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x38 || key == 0x3c) {
+ if (mod & NSEventModifierFlagShift) {
+ mod &= ~NSEventModifierFlagShift;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3a || key == 0x3d) {
+ if (mod & NSEventModifierFlagOption) {
+ mod &= ~NSEventModifierFlagOption;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3b || key == 0x3e) {
+ if (mod & NSEventModifierFlagControl) {
+ mod &= ~NSEventModifierFlagControl;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else {
+ return;
+ }
+
+ ke.osx_state = mod;
+ ke.keycode = KeyMappingOSX::remap_key(key, mod);
+ ke.physical_keycode = KeyMappingOSX::translate_key(key);
+ ke.unicode = 0;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+}
+
+- (void)keyUp:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ // Ignore all input if IME input is in progress.
+ if (!ime_input_event_in_progress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ // Fallback unicode character handler used if IME is not active.
+ if (!wd.im_active && length > 0 && keycode_has_unicode(KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]))) {
+ Char16String text;
+ text.resize([characters length] + 1);
+ [characters getCharacters:(unichar *)text.ptrw() range:NSMakeRange(0, [characters length])];
+
+ String u32text;
+ u32text.parse_utf16(text.ptr(), text.length());
+
+ for (int i = 0; i < u32text.length(); i++) {
+ const char32_t codepoint = u32text[i];
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
+ ke.raw = true;
+ ke.unicode = codepoint;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = KeyMappingOSX::remap_key([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = KeyMappingOSX::translate_key([event keyCode]);
+ ke.raw = true;
+ ke.unicode = 0;
+
+ ds->push_to_key_event_buffer(ke);
+ }
+ }
+}
+
+// MARK: Scroll and pan
+
+- (void)processScrollEvent:(NSEvent *)event button:(MouseButton)button factor:(double)factor {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ MouseButton mask = mouse_button_to_mask(button);
+
+ Ref<InputEventMouseButton> sc;
+ sc.instantiate();
+
+ sc->set_window_id(window_id);
+ ds->get_key_modifier_state([event modifierFlags], sc);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(true);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ MouseButton last_button_state = ds->mouse_get_button_state() | (MouseButton)mask;
+ sc->set_button_mask(last_button_state);
+ ds->mouse_set_button_state(last_button_state);
+
+ Input::get_singleton()->parse_input_event(sc);
+
+ sc.instantiate();
+ sc->set_window_id(window_id);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(false);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ last_button_state &= (MouseButton)~mask;
+ sc->set_button_mask(last_button_state);
+ ds->mouse_set_button_state(last_button_state);
+
+ Input::get_singleton()->parse_input_event(sc);
+}
+
+- (void)processPanEvent:(NSEvent *)event dx:(double)dx dy:(double)dy {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ Ref<InputEventPanGesture> pg;
+ pg.instantiate();
+
+ pg->set_window_id(window_id);
+ ds->get_key_modifier_state([event modifierFlags], pg);
+ pg->set_position(wd.mouse_pos);
+ pg->set_delta(Vector2(-dx, -dy));
+
+ Input::get_singleton()->parse_input_event(pg);
+}
+
+- (void)scrollWheel:(NSEvent *)event {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ ds->update_mouse_pos(wd, [event locationInWindow]);
+
+ double delta_x = [event scrollingDeltaX];
+ double delta_y = [event scrollingDeltaY];
+
+ if ([event hasPreciseScrollingDeltas]) {
+ delta_x *= 0.03;
+ delta_y *= 0.03;
+ }
+
+ if ([event momentumPhase] != NSEventPhaseNone) {
+ if (ignore_momentum_scroll) {
+ return;
+ }
+ } else {
+ ignore_momentum_scroll = false;
+ }
+
+ if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
+ [self processPanEvent:event dx:delta_x dy:delta_y];
+ } else {
+ if (fabs(delta_x)) {
+ [self processScrollEvent:event button:(0 > delta_x ? MouseButton::WHEEL_RIGHT : MouseButton::WHEEL_LEFT) factor:fabs(delta_x * 0.3)];
+ }
+ if (fabs(delta_y)) {
+ [self processScrollEvent:event button:(0 < delta_y ? MouseButton::WHEEL_UP : MouseButton::WHEEL_DOWN) factor:fabs(delta_y * 0.3)];
+ }
+ }
+}
+
+@end
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index 64a93f7292..7fabfaa1b7 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -37,7 +37,7 @@
int main(int argc, char **argv) {
#if defined(VULKAN_ENABLED)
- // MoltenVK - enable full component swizzling support
+ // MoltenVK - enable full component swizzling support.
setenv("MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE", "1", 1);
#endif
@@ -45,13 +45,14 @@ int main(int argc, char **argv) {
const char *dbg_arg = "-NSDocumentRevisionsDebugMode";
printf("arguments\n");
for (int i = 0; i < argc; i++) {
- if (strcmp(dbg_arg, argv[i]) == 0)
+ if (strcmp(dbg_arg, argv[i]) == 0) {
first_arg = i + 2;
+ }
printf("%i: %s\n", i, argv[i]);
};
#ifdef DEBUG_ENABLED
- // lets report the path we made current after all that
+ // Lets report the path we made current after all that.
char cwd[4096];
getcwd(cwd, 4096);
printf("Current path: %s\n", cwd);
@@ -60,23 +61,25 @@ int main(int argc, char **argv) {
OS_OSX os;
Error err;
- // We must override main when testing is enabled
+ // We must override main when testing is enabled.
TEST_MAIN_OVERRIDE
- if (os.open_with_filename != "") {
- char *argv_c = (char *)malloc(os.open_with_filename.utf8().size());
- memcpy(argv_c, os.open_with_filename.utf8().get_data(), os.open_with_filename.utf8().size());
+ if (os.get_open_with_filename() != "") {
+ char *argv_c = (char *)malloc(os.get_open_with_filename().utf8().size());
+ memcpy(argv_c, os.get_open_with_filename().utf8().get_data(), os.get_open_with_filename().utf8().size());
err = Main::setup(argv[0], 1, &argv_c);
free(argv_c);
} else {
err = Main::setup(argv[0], argc - first_arg, &argv[first_arg]);
}
- if (err != OK)
+ if (err != OK) {
return 255;
+ }
- if (Main::start())
- os.run(); // it is actually the OS that decides how to run
+ if (Main::start()) {
+ os.run(); // It is actually the OS that decides how to run.
+ }
Main::cleanup();
diff --git a/platform/osx/godot_menu_item.h b/platform/osx/godot_menu_item.h
new file mode 100644
index 0000000000..50c4709c18
--- /dev/null
+++ b/platform/osx/godot_menu_item.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* godot_menu_item.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_MENU_ITEM_H
+#define GODOT_MENU_ITEM_H
+
+#include "servers/display_server.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@interface GodotMenuItem : NSObject {
+@public
+ Callable callback;
+ Variant meta;
+ int id;
+ bool checkable;
+}
+
+@end
+
+@implementation GodotMenuItem
+@end
+
+#endif // GODOT_MENU_ITEM_H
diff --git a/platform/osx/godot_window.h b/platform/osx/godot_window.h
new file mode 100644
index 0000000000..16ff101142
--- /dev/null
+++ b/platform/osx/godot_window.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* godot_window.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_WINDOW_H
+#define GODOT_WINDOW_H
+
+#include "servers/display_server.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@interface GodotWindow : NSWindow {
+ DisplayServer::WindowID window_id;
+}
+
+- (void)setWindowID:(DisplayServer::WindowID)wid;
+
+@end
+
+#endif //GODOT_WINDOW_H
diff --git a/platform/osx/godot_window.mm b/platform/osx/godot_window.mm
new file mode 100644
index 0000000000..772a2ddb9f
--- /dev/null
+++ b/platform/osx/godot_window.mm
@@ -0,0 +1,69 @@
+/*************************************************************************/
+/* godot_window.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_window.h"
+
+#include "display_server_osx.h"
+
+@implementation GodotWindow
+
+- (id)init {
+ self = [super init];
+ window_id = DisplayServer::INVALID_WINDOW_ID;
+ return self;
+}
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
+- (BOOL)canBecomeKeyWindow {
+ // Required for NSWindowStyleMaskBorderless windows.
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return YES;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ return !wd.no_focus;
+}
+
+- (BOOL)canBecomeMainWindow {
+ // Required for NSWindowStyleMaskBorderless windows.
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return YES;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ return !wd.no_focus;
+}
+
+@end
diff --git a/platform/osx/godot_window_delegate.h b/platform/osx/godot_window_delegate.h
new file mode 100644
index 0000000000..8a1f681fcd
--- /dev/null
+++ b/platform/osx/godot_window_delegate.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* godot_window_delegate.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GODOT_WINDOW_DELEGATE_H
+#define GODOT_WINDOW_DELEGATE_H
+
+#include "servers/display_server.h"
+
+#import <AppKit/AppKit.h>
+#import <Foundation/Foundation.h>
+
+@interface GodotWindowDelegate : NSObject <NSWindowDelegate> {
+ DisplayServer::WindowID window_id;
+}
+
+- (void)setWindowID:(DisplayServer::WindowID)wid;
+
+@end
+
+#endif //GODOT_WINDOW_DELEGATE_H
diff --git a/platform/osx/godot_window_delegate.mm b/platform/osx/godot_window_delegate.mm
new file mode 100644
index 0000000000..1742be987d
--- /dev/null
+++ b/platform/osx/godot_window_delegate.mm
@@ -0,0 +1,254 @@
+/*************************************************************************/
+/* godot_window_delegate.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "godot_window_delegate.h"
+
+#include "display_server_osx.h"
+
+@implementation GodotWindowDelegate
+
+- (void)setWindowID:(DisplayServer::WindowID)wid {
+ window_id = wid;
+}
+
+- (BOOL)windowShouldClose:(id)sender {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return YES;
+ }
+
+ ds->send_window_event(ds->get_window(window_id), DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ while (wd.transient_children.size()) {
+ ds->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
+ ds->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
+ }
+
+ ds->window_destroy(window_id);
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ wd.fullscreen = true;
+ // Reset window size limits.
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+
+ // Force window resize event.
+ [self windowDidResize:notification];
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ wd.fullscreen = false;
+
+ // Set window size limits.
+ const float scale = ds->screen_get_max_scale();
+ if (wd.min_size != Size2i()) {
+ Size2i size = wd.min_size / scale;
+ [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ }
+ if (wd.max_size != Size2i()) {
+ Size2i size = wd.max_size / scale;
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+
+ // Restore resizability state.
+ if (wd.resize_disabled) {
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ }
+
+ // Restore on-top state.
+ if (wd.on_top) {
+ [wd.window_object setLevel:NSFloatingWindowLevel];
+ }
+
+ // Force window resize event.
+ [self windowDidResize:notification];
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ CGFloat new_scale_factor = [wd.window_object backingScaleFactor];
+ CGFloat old_scale_factor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
+
+ if (new_scale_factor != old_scale_factor) {
+ // Set new display scale and window size.
+ const float scale = ds->screen_get_max_scale();
+ const NSRect content_rect = [wd.window_view frame];
+
+ wd.size.width = content_rect.size.width * scale;
+ wd.size.height = content_rect.size.height * scale;
+
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE);
+
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
+ }
+
+ //Force window resize event
+ [self windowDidResize:notification];
+ }
+}
+
+- (void)windowDidResize:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ const NSRect content_rect = [wd.window_view frame];
+ const float scale = ds->screen_get_max_scale();
+ wd.size.width = content_rect.size.width * scale;
+ wd.size.height = content_rect.size.height * scale;
+
+ CALayer *layer = [wd.window_view layer];
+ if (layer) {
+ layer.contentsScale = scale;
+ }
+
+ ds->window_resize(window_id, wd.size.width, wd.size.height);
+
+ if (!wd.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id));
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+ ds->release_pressed_events();
+
+ if (!wd.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(ds->window_get_position(window_id), ds->window_get_size(window_id));
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ if (ds->mouse_get_mode() == DisplayServer::MOUSE_MODE_CAPTURED) {
+ const NSRect content_rect = [wd.window_view frame];
+ NSRect point_in_window_rect = NSMakeRect(content_rect.size.width / 2, content_rect.size.height / 2, 0, 0);
+ NSPoint point_on_screen = [[wd.window_view window] convertRectToScreen:point_in_window_rect].origin;
+ CGPoint mouse_warp_pos = { point_on_screen.x, CGDisplayBounds(CGMainDisplayID()).size.height - point_on_screen.y };
+ CGWarpMouseCursorPosition(mouse_warp_pos);
+ } else {
+ ds->update_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]);
+ }
+
+ ds->set_last_focused_window(window_id);
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+- (void)windowDidResignKey:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ ds->release_pressed_events();
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ ds->release_pressed_events();
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification {
+ DisplayServerOSX *ds = (DisplayServerOSX *)DisplayServer::get_singleton();
+ if (!ds || !ds->has_window(window_id)) {
+ return;
+ }
+
+ DisplayServerOSX::WindowData &wd = ds->get_window(window_id);
+
+ ds->set_last_focused_window(window_id);
+ ds->send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+@end
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index c2356f12cd..d518206f04 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -80,7 +80,7 @@ int joypad::get_hid_element_state(rec_element *p_element) const {
if (IOHIDDeviceGetValue(device_ref, p_element->ref, &valueRef) == kIOReturnSuccess) {
value = (SInt32)IOHIDValueGetIntegerValue(valueRef);
- /* record min and max for auto calibration */
+ // Record min and max for auto calibration.
if (value < p_element->min) {
p_element->min = value;
}
@@ -179,7 +179,7 @@ void joypad::add_hid_element(IOHIDElementRef p_element) {
break;
}
- if (list) { /* add to list */
+ if (list) { // Add to list.
rec_element element;
element.ref = p_element;
@@ -280,7 +280,7 @@ static String _hex_str(uint8_t p_byte) {
bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
p_joy->device_ref = p_device_ref;
- /* get device name */
+ // Get device name.
String name;
char c_name[256];
CFTypeRef refCF = IOHIDDeviceGetProperty(p_device_ref, CFSTR(kIOHIDProductKey));
@@ -319,7 +319,7 @@ bool JoypadOSX::configure_joypad(IOHIDDeviceRef p_device_ref, joypad *p_joy) {
sprintf(uid, "%08x%08x%08x%08x", OSSwapHostToBigInt32(3), OSSwapHostToBigInt32(vendor), OSSwapHostToBigInt32(product_id), OSSwapHostToBigInt32(version));
input->joy_connection_changed(id, true, name, uid);
} else {
- //bluetooth device
+ // Bluetooth device.
String guid = "05000000";
for (int i = 0; i < 12; i++) {
if (i < name.size())
@@ -445,7 +445,7 @@ static HatMask process_hat_value(int p_min, int p_max, int p_value, bool p_offse
void JoypadOSX::poll_joypads() const {
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
- /* no-op. Pending callbacks will fire. */
+ // No-op. Pending callbacks will fire.
}
}
@@ -568,7 +568,7 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
IOHIDManagerScheduleWithRunLoop(hid_manager, runloop, GODOT_JOY_LOOP_RUN_MODE);
while (CFRunLoopRunInMode(GODOT_JOY_LOOP_RUN_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
- /* no-op. Callback fires once per existing device. */
+ // No-op. Callback fires once per existing device.
}
}
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index 8ea1033a77..4ca7fb1698 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -66,7 +66,7 @@ struct joypad {
int id = 0;
bool offset_hat = false;
- io_service_t ffservice = 0; /* Interface for force feedback, 0 = no ff */
+ io_service_t ffservice = 0; // Interface for force feedback, 0 = no ff.
FFCONSTANTFORCE ff_constant_force;
FFDeviceObjectReference ff_device = nullptr;
FFEffectObjectReference ff_object = nullptr;
diff --git a/platform/osx/key_mapping_osx.h b/platform/osx/key_mapping_osx.h
new file mode 100644
index 0000000000..252cc907bb
--- /dev/null
+++ b/platform/osx/key_mapping_osx.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* key_mapping_osx.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef KEY_MAPPING_OSX_H
+#define KEY_MAPPING_OSX_H
+
+#include "core/os/keyboard.h"
+
+class KeyMappingOSX {
+ KeyMappingOSX() {}
+
+ static bool is_numpad_key(unsigned int key);
+
+public:
+ // Mappings input.
+ static Key translate_key(unsigned int key);
+ static unsigned int unmap_key(Key key);
+ static Key remap_key(unsigned int key, unsigned int state);
+
+ // Mapping for menu shortcuts.
+ static String keycode_get_native_string(Key p_keycode);
+ static unsigned int keycode_get_native_mask(Key p_keycode);
+};
+
+#endif // KEY_MAPPING_OSX_H
diff --git a/platform/osx/key_mapping_osx.mm b/platform/osx/key_mapping_osx.mm
new file mode 100644
index 0000000000..fde9206824
--- /dev/null
+++ b/platform/osx/key_mapping_osx.mm
@@ -0,0 +1,477 @@
+/*************************************************************************/
+/* key_mapping_osx.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "key_mapping_osx.h"
+
+#include <Carbon/Carbon.h>
+#include <Cocoa/Cocoa.h>
+
+bool KeyMappingOSX::is_numpad_key(unsigned int key) {
+ static const unsigned int table[] = {
+ 0x41, /* kVK_ANSI_KeypadDecimal */
+ 0x43, /* kVK_ANSI_KeypadMultiply */
+ 0x45, /* kVK_ANSI_KeypadPlus */
+ 0x47, /* kVK_ANSI_KeypadClear */
+ 0x4b, /* kVK_ANSI_KeypadDivide */
+ 0x4c, /* kVK_ANSI_KeypadEnter */
+ 0x4e, /* kVK_ANSI_KeypadMinus */
+ 0x51, /* kVK_ANSI_KeypadEquals */
+ 0x52, /* kVK_ANSI_Keypad0 */
+ 0x53, /* kVK_ANSI_Keypad1 */
+ 0x54, /* kVK_ANSI_Keypad2 */
+ 0x55, /* kVK_ANSI_Keypad3 */
+ 0x56, /* kVK_ANSI_Keypad4 */
+ 0x57, /* kVK_ANSI_Keypad5 */
+ 0x58, /* kVK_ANSI_Keypad6 */
+ 0x59, /* kVK_ANSI_Keypad7 */
+ 0x5b, /* kVK_ANSI_Keypad8 */
+ 0x5c, /* kVK_ANSI_Keypad9 */
+ 0x5f, /* kVK_JIS_KeypadComma */
+ 0x00
+ };
+ for (int i = 0; table[i] != 0; i++) {
+ if (key == table[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Keyboard symbol translation table.
+static const Key _osx_to_godot_table[128] = {
+ /* 00 */ Key::A,
+ /* 01 */ Key::S,
+ /* 02 */ Key::D,
+ /* 03 */ Key::F,
+ /* 04 */ Key::H,
+ /* 05 */ Key::G,
+ /* 06 */ Key::Z,
+ /* 07 */ Key::X,
+ /* 08 */ Key::C,
+ /* 09 */ Key::V,
+ /* 0a */ Key::SECTION, /* ISO Section */
+ /* 0b */ Key::B,
+ /* 0c */ Key::Q,
+ /* 0d */ Key::W,
+ /* 0e */ Key::E,
+ /* 0f */ Key::R,
+ /* 10 */ Key::Y,
+ /* 11 */ Key::T,
+ /* 12 */ Key::KEY_1,
+ /* 13 */ Key::KEY_2,
+ /* 14 */ Key::KEY_3,
+ /* 15 */ Key::KEY_4,
+ /* 16 */ Key::KEY_6,
+ /* 17 */ Key::KEY_5,
+ /* 18 */ Key::EQUAL,
+ /* 19 */ Key::KEY_9,
+ /* 1a */ Key::KEY_7,
+ /* 1b */ Key::MINUS,
+ /* 1c */ Key::KEY_8,
+ /* 1d */ Key::KEY_0,
+ /* 1e */ Key::BRACERIGHT,
+ /* 1f */ Key::O,
+ /* 20 */ Key::U,
+ /* 21 */ Key::BRACELEFT,
+ /* 22 */ Key::I,
+ /* 23 */ Key::P,
+ /* 24 */ Key::ENTER,
+ /* 25 */ Key::L,
+ /* 26 */ Key::J,
+ /* 27 */ Key::APOSTROPHE,
+ /* 28 */ Key::K,
+ /* 29 */ Key::SEMICOLON,
+ /* 2a */ Key::BACKSLASH,
+ /* 2b */ Key::COMMA,
+ /* 2c */ Key::SLASH,
+ /* 2d */ Key::N,
+ /* 2e */ Key::M,
+ /* 2f */ Key::PERIOD,
+ /* 30 */ Key::TAB,
+ /* 31 */ Key::SPACE,
+ /* 32 */ Key::QUOTELEFT,
+ /* 33 */ Key::BACKSPACE,
+ /* 34 */ Key::UNKNOWN,
+ /* 35 */ Key::ESCAPE,
+ /* 36 */ Key::META,
+ /* 37 */ Key::META,
+ /* 38 */ Key::SHIFT,
+ /* 39 */ Key::CAPSLOCK,
+ /* 3a */ Key::ALT,
+ /* 3b */ Key::CTRL,
+ /* 3c */ Key::SHIFT,
+ /* 3d */ Key::ALT,
+ /* 3e */ Key::CTRL,
+ /* 3f */ Key::UNKNOWN, /* Function */
+ /* 40 */ Key::UNKNOWN, /* F17 */
+ /* 41 */ Key::KP_PERIOD,
+ /* 42 */ Key::UNKNOWN,
+ /* 43 */ Key::KP_MULTIPLY,
+ /* 44 */ Key::UNKNOWN,
+ /* 45 */ Key::KP_ADD,
+ /* 46 */ Key::UNKNOWN,
+ /* 47 */ Key::NUMLOCK, /* Really KeypadClear... */
+ /* 48 */ Key::VOLUMEUP, /* VolumeUp */
+ /* 49 */ Key::VOLUMEDOWN, /* VolumeDown */
+ /* 4a */ Key::VOLUMEMUTE, /* Mute */
+ /* 4b */ Key::KP_DIVIDE,
+ /* 4c */ Key::KP_ENTER,
+ /* 4d */ Key::UNKNOWN,
+ /* 4e */ Key::KP_SUBTRACT,
+ /* 4f */ Key::UNKNOWN, /* F18 */
+ /* 50 */ Key::UNKNOWN, /* F19 */
+ /* 51 */ Key::EQUAL, /* KeypadEqual */
+ /* 52 */ Key::KP_0,
+ /* 53 */ Key::KP_1,
+ /* 54 */ Key::KP_2,
+ /* 55 */ Key::KP_3,
+ /* 56 */ Key::KP_4,
+ /* 57 */ Key::KP_5,
+ /* 58 */ Key::KP_6,
+ /* 59 */ Key::KP_7,
+ /* 5a */ Key::UNKNOWN, /* F20 */
+ /* 5b */ Key::KP_8,
+ /* 5c */ Key::KP_9,
+ /* 5d */ Key::YEN, /* JIS Yen */
+ /* 5e */ Key::UNDERSCORE, /* JIS Underscore */
+ /* 5f */ Key::COMMA, /* JIS KeypadComma */
+ /* 60 */ Key::F5,
+ /* 61 */ Key::F6,
+ /* 62 */ Key::F7,
+ /* 63 */ Key::F3,
+ /* 64 */ Key::F8,
+ /* 65 */ Key::F9,
+ /* 66 */ Key::UNKNOWN, /* JIS Eisu */
+ /* 67 */ Key::F11,
+ /* 68 */ Key::UNKNOWN, /* JIS Kana */
+ /* 69 */ Key::F13,
+ /* 6a */ Key::F16,
+ /* 6b */ Key::F14,
+ /* 6c */ Key::UNKNOWN,
+ /* 6d */ Key::F10,
+ /* 6e */ Key::MENU,
+ /* 6f */ Key::F12,
+ /* 70 */ Key::UNKNOWN,
+ /* 71 */ Key::F15,
+ /* 72 */ Key::INSERT, /* Really Help... */
+ /* 73 */ Key::HOME,
+ /* 74 */ Key::PAGEUP,
+ /* 75 */ Key::KEY_DELETE,
+ /* 76 */ Key::F4,
+ /* 77 */ Key::END,
+ /* 78 */ Key::F2,
+ /* 79 */ Key::PAGEDOWN,
+ /* 7a */ Key::F1,
+ /* 7b */ Key::LEFT,
+ /* 7c */ Key::RIGHT,
+ /* 7d */ Key::DOWN,
+ /* 7e */ Key::UP,
+ /* 7f */ Key::UNKNOWN,
+};
+
+// Translates a OS X keycode to a Godot keycode.
+Key KeyMappingOSX::translate_key(unsigned int key) {
+ if (key >= 128) {
+ return Key::UNKNOWN;
+ }
+
+ return _osx_to_godot_table[key];
+}
+
+// Translates a Godot keycode back to a OSX keycode.
+unsigned int KeyMappingOSX::unmap_key(Key key) {
+ for (int i = 0; i <= 126; i++) {
+ if (_osx_to_godot_table[i] == key) {
+ return i;
+ }
+ }
+ return 127;
+}
+
+struct _KeyCodeMap {
+ UniChar kchar;
+ Key kcode;
+};
+
+static const _KeyCodeMap _keycodes[55] = {
+ { '`', Key::QUOTELEFT },
+ { '~', Key::ASCIITILDE },
+ { '0', Key::KEY_0 },
+ { '1', Key::KEY_1 },
+ { '2', Key::KEY_2 },
+ { '3', Key::KEY_3 },
+ { '4', Key::KEY_4 },
+ { '5', Key::KEY_5 },
+ { '6', Key::KEY_6 },
+ { '7', Key::KEY_7 },
+ { '8', Key::KEY_8 },
+ { '9', Key::KEY_9 },
+ { '-', Key::MINUS },
+ { '_', Key::UNDERSCORE },
+ { '=', Key::EQUAL },
+ { '+', Key::PLUS },
+ { 'q', Key::Q },
+ { 'w', Key::W },
+ { 'e', Key::E },
+ { 'r', Key::R },
+ { 't', Key::T },
+ { 'y', Key::Y },
+ { 'u', Key::U },
+ { 'i', Key::I },
+ { 'o', Key::O },
+ { 'p', Key::P },
+ { '[', Key::BRACELEFT },
+ { ']', Key::BRACERIGHT },
+ { '{', Key::BRACELEFT },
+ { '}', Key::BRACERIGHT },
+ { 'a', Key::A },
+ { 's', Key::S },
+ { 'd', Key::D },
+ { 'f', Key::F },
+ { 'g', Key::G },
+ { 'h', Key::H },
+ { 'j', Key::J },
+ { 'k', Key::K },
+ { 'l', Key::L },
+ { ';', Key::SEMICOLON },
+ { ':', Key::COLON },
+ { '\'', Key::APOSTROPHE },
+ { '\"', Key::QUOTEDBL },
+ { '\\', Key::BACKSLASH },
+ { '#', Key::NUMBERSIGN },
+ { 'z', Key::Z },
+ { 'x', Key::X },
+ { 'c', Key::C },
+ { 'v', Key::V },
+ { 'b', Key::B },
+ { 'n', Key::N },
+ { 'm', Key::M },
+ { ',', Key::COMMA },
+ { '.', Key::PERIOD },
+ { '/', Key::SLASH }
+};
+
+// Remap key according to current keyboard layout.
+Key KeyMappingOSX::remap_key(unsigned int key, unsigned int state) {
+ if (is_numpad_key(key)) {
+ return translate_key(key);
+ }
+
+ TISInputSourceRef current_keyboard = TISCopyCurrentKeyboardInputSource();
+ if (!current_keyboard) {
+ return translate_key(key);
+ }
+
+ CFDataRef layout_data = (CFDataRef)TISGetInputSourceProperty(current_keyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layout_data) {
+ return translate_key(key);
+ }
+
+ const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data);
+
+ UInt32 keys_down = 0;
+ UniChar chars[4];
+ UniCharCount real_length;
+
+ OSStatus err = UCKeyTranslate(keyboard_layout,
+ key,
+ kUCKeyActionDisplay,
+ (state >> 8) & 0xFF,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keys_down,
+ sizeof(chars) / sizeof(chars[0]),
+ &real_length,
+ chars);
+
+ if (err != noErr) {
+ return translate_key(key);
+ }
+
+ for (unsigned int i = 0; i < 55; i++) {
+ if (_keycodes[i].kchar == chars[0]) {
+ return _keycodes[i].kcode;
+ }
+ }
+ return translate_key(key);
+}
+
+struct _KeyCodeText {
+ Key code;
+ char32_t text;
+};
+
+static const _KeyCodeText _native_keycodes[] = {
+ /* clang-format off */
+ {Key::ESCAPE ,0x001B},
+ {Key::TAB ,0x0009},
+ {Key::BACKTAB ,0x007F},
+ {Key::BACKSPACE ,0x0008},
+ {Key::ENTER ,0x000D},
+ {Key::INSERT ,NSInsertFunctionKey},
+ {Key::KEY_DELETE ,0x007F},
+ {Key::PAUSE ,NSPauseFunctionKey},
+ {Key::PRINT ,NSPrintScreenFunctionKey},
+ {Key::SYSREQ ,NSSysReqFunctionKey},
+ {Key::CLEAR ,NSClearLineFunctionKey},
+ {Key::HOME ,0x2196},
+ {Key::END ,0x2198},
+ {Key::LEFT ,0x001C},
+ {Key::UP ,0x001E},
+ {Key::RIGHT ,0x001D},
+ {Key::DOWN ,0x001F},
+ {Key::PAGEUP ,0x21DE},
+ {Key::PAGEDOWN ,0x21DF},
+ {Key::NUMLOCK ,NSClearLineFunctionKey},
+ {Key::SCROLLLOCK ,NSScrollLockFunctionKey},
+ {Key::F1 ,NSF1FunctionKey},
+ {Key::F2 ,NSF2FunctionKey},
+ {Key::F3 ,NSF3FunctionKey},
+ {Key::F4 ,NSF4FunctionKey},
+ {Key::F5 ,NSF5FunctionKey},
+ {Key::F6 ,NSF6FunctionKey},
+ {Key::F7 ,NSF7FunctionKey},
+ {Key::F8 ,NSF8FunctionKey},
+ {Key::F9 ,NSF9FunctionKey},
+ {Key::F10 ,NSF10FunctionKey},
+ {Key::F11 ,NSF11FunctionKey},
+ {Key::F12 ,NSF12FunctionKey},
+ {Key::F13 ,NSF13FunctionKey},
+ {Key::F14 ,NSF14FunctionKey},
+ {Key::F15 ,NSF15FunctionKey},
+ {Key::F16 ,NSF16FunctionKey}, //* ... NSF35FunctionKey */
+ {Key::MENU ,NSMenuFunctionKey},
+ {Key::HELP ,NSHelpFunctionKey},
+ {Key::STOP ,NSStopFunctionKey},
+ {Key::LAUNCH0 ,NSUserFunctionKey},
+ {Key::SPACE ,0x0020},
+ {Key::EXCLAM ,'!'},
+ {Key::QUOTEDBL ,'\"'},
+ {Key::NUMBERSIGN ,'#'},
+ {Key::DOLLAR ,'$'},
+ {Key::PERCENT ,'\%'},
+ {Key::AMPERSAND ,'&'},
+ {Key::APOSTROPHE ,'\''},
+ {Key::PARENLEFT ,'('},
+ {Key::PARENRIGHT ,')'},
+ {Key::ASTERISK ,'*'},
+ {Key::PLUS ,'+'},
+ {Key::COMMA ,','},
+ {Key::MINUS ,'-'},
+ {Key::PERIOD ,'.'},
+ {Key::SLASH ,'/'},
+ {Key::KEY_0 ,'0'},
+ {Key::KEY_1 ,'1'},
+ {Key::KEY_2 ,'2'},
+ {Key::KEY_3 ,'3'},
+ {Key::KEY_4 ,'4'},
+ {Key::KEY_5 ,'5'},
+ {Key::KEY_6 ,'6'},
+ {Key::KEY_7 ,'7'},
+ {Key::KEY_8 ,'8'},
+ {Key::KEY_9 ,'9'},
+ {Key::COLON ,':'},
+ {Key::SEMICOLON ,';'},
+ {Key::LESS ,'<'},
+ {Key::EQUAL ,'='},
+ {Key::GREATER ,'>'},
+ {Key::QUESTION ,'?'},
+ {Key::AT ,'@'},
+ {Key::A ,'a'},
+ {Key::B ,'b'},
+ {Key::C ,'c'},
+ {Key::D ,'d'},
+ {Key::E ,'e'},
+ {Key::F ,'f'},
+ {Key::G ,'g'},
+ {Key::H ,'h'},
+ {Key::I ,'i'},
+ {Key::J ,'j'},
+ {Key::K ,'k'},
+ {Key::L ,'l'},
+ {Key::M ,'m'},
+ {Key::N ,'n'},
+ {Key::O ,'o'},
+ {Key::P ,'p'},
+ {Key::Q ,'q'},
+ {Key::R ,'r'},
+ {Key::S ,'s'},
+ {Key::T ,'t'},
+ {Key::U ,'u'},
+ {Key::V ,'v'},
+ {Key::W ,'w'},
+ {Key::X ,'x'},
+ {Key::Y ,'y'},
+ {Key::Z ,'z'},
+ {Key::BRACKETLEFT ,'['},
+ {Key::BACKSLASH ,'\\'},
+ {Key::BRACKETRIGHT ,']'},
+ {Key::ASCIICIRCUM ,'^'},
+ {Key::UNDERSCORE ,'_'},
+ {Key::QUOTELEFT ,'`'},
+ {Key::BRACELEFT ,'{'},
+ {Key::BAR ,'|'},
+ {Key::BRACERIGHT ,'}'},
+ {Key::ASCIITILDE ,'~'},
+ {Key::NONE ,0x0000}
+ /* clang-format on */
+};
+
+String KeyMappingOSX::keycode_get_native_string(Key p_keycode) {
+ const _KeyCodeText *kct = &_native_keycodes[0];
+
+ while (kct->text) {
+ if (kct->code == p_keycode) {
+ return String::chr(kct->text);
+ }
+ kct++;
+ }
+ return String();
+}
+
+unsigned int KeyMappingOSX::keycode_get_native_mask(Key p_keycode) {
+ unsigned int mask = 0;
+ if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) {
+ mask |= NSEventModifierFlagControl;
+ }
+ if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) {
+ mask |= NSEventModifierFlagOption;
+ }
+ if ((p_keycode & KeyModifierMask::SHIFT) != Key::NONE) {
+ mask |= NSEventModifierFlagShift;
+ }
+ if ((p_keycode & KeyModifierMask::META) != Key::NONE) {
+ mask |= NSEventModifierFlagCommand;
+ }
+ if ((p_keycode & KeyModifierMask::KPAD) != Key::NONE) {
+ mask |= NSEventModifierFlagNumericPad;
+ }
+ return mask;
+}
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 60dec1fe3f..5bb5b3320e 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -40,9 +40,7 @@
#include "servers/audio_server.h"
class OS_OSX : public OS_Unix {
- virtual void delete_main_loop() override;
-
- bool force_quit;
+ bool force_quit = false;
JoypadOSX *joypad_osx = nullptr;
@@ -55,13 +53,15 @@ class OS_OSX : public OS_Unix {
CrashHandler crash_handler;
- MainLoop *main_loop;
+ CFRunLoopObserverRef pre_wait_observer;
- static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
+ MainLoop *main_loop = nullptr;
-public:
String open_with_filename;
+ static _FORCE_INLINE_ String get_framework_executable(const String &p_path);
+ static void pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context);
+
protected:
virtual void initialize_core() override;
virtual void initialize() override;
@@ -70,8 +70,12 @@ protected:
virtual void initialize_joypads() override;
virtual void set_main_loop(MainLoop *p_main_loop) override;
+ virtual void delete_main_loop() override;
public:
+ String get_open_with_filename() const;
+ void set_open_with_filename(const String &p_path);
+
virtual String get_name() const override;
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
@@ -89,26 +93,27 @@ public:
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
- Error shell_open(String p_uri) override;
+ virtual Error shell_open(String p_uri) override;
- String get_locale() const override;
+ virtual String get_locale() const override;
virtual String get_executable_path() const override;
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override;
- virtual String get_unique_id() const override; //++
+ virtual String get_unique_id() const override;
virtual bool _check_internal_feature_support(const String &p_feature) override;
- void run();
-
virtual void disable_crash_handler() override;
virtual bool is_disable_crash_handler() const override;
virtual Error move_to_trash(const String &p_path) override;
+ void run();
+
OS_OSX();
+ ~OS_OSX();
};
#endif
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 32d0e6dd94..9288e658cf 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -31,241 +31,45 @@
#include "os_osx.h"
#include "core/version_generated.gen.h"
+#include "main/main.h"
#include "dir_access_osx.h"
#include "display_server_osx.h"
-#include "main/main.h"
+#include "godot_application.h"
+#include "godot_application_delegate.h"
+#include "osx_terminal_logger.h"
#include <dlfcn.h>
#include <libproc.h>
#include <mach-o/dyld.h>
-#include <os/log.h>
-
-#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
-
-/*************************************************************************/
-/* GodotApplication */
-/*************************************************************************/
-
-@interface GodotApplication : NSApplication
-@end
-
-@implementation GodotApplication
-
-- (void)sendEvent:(NSEvent *)event {
- if (DS_OSX) {
- DS_OSX->_send_event(event);
- }
-
- // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
- // This works around an AppKit bug, where key up events while holding
- // down the command key don't get sent to the key window.
- if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand)) {
- [[self keyWindow] sendEvent:event];
- } else {
- [super sendEvent:event];
- }
-}
-
-@end
-
-/*************************************************************************/
-/* GodotApplicationDelegate */
-/*************************************************************************/
-
-@interface GodotApplicationDelegate : NSObject
-- (void)forceUnbundledWindowActivationHackStep1;
-- (void)forceUnbundledWindowActivationHackStep2;
-- (void)forceUnbundledWindowActivationHackStep3;
-@end
-
-@implementation GodotApplicationDelegate
-
-- (void)forceUnbundledWindowActivationHackStep1 {
- // Step 1: Switch focus to macOS SystemUIServer process.
- // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
- for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.systemuiserver"]) {
- [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
- break;
- }
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep2)
- withObject:nil
- afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep2 {
- // Step 2: Register app as foreground process.
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep3 {
- // Step 3: Switch focus back to app window.
- [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
-}
-
-- (void)applicationDidFinishLaunching:(NSNotification *)notice {
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
- // If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
- }
-}
-
-- (void)applicationDidResignActive:(NSNotification *)notification {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
- }
-}
-- (void)applicationDidBecomeActive:(NSNotification *)notification {
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
- }
-}
-
-- (void)globalMenuCallback:(id)sender {
- if (DS_OSX) {
- return DS_OSX->_menu_callback(sender);
- }
-}
-
-- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
- if (DS_OSX) {
- return DS_OSX->_get_dock_menu();
+_FORCE_INLINE_ String OS_OSX::get_framework_executable(const String &p_path) {
+ // Append framework executable name, or return as is if p_path is not a framework.
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
+ return p_path.plus_file(p_path.get_file().get_basename());
} else {
- return nullptr;
- }
-}
-
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
- // Note: may be called called before main loop init!
- char *utfs = strdup([filename UTF8String]);
- ((OS_OSX *)OS_OSX::get_singleton())->open_with_filename.parse_utf8(utfs);
- free(utfs);
-
-#ifdef TOOLS_ENABLED
- // Open new instance
- if (OS_OSX::get_singleton()->get_main_loop()) {
- List<String> args;
- args.push_back(((OS_OSX *)OS_OSX::get_singleton())->open_with_filename);
- String exec = OS_OSX::get_singleton()->get_executable_path();
- OS_OSX::get_singleton()->create_process(exec, args);
- }
-#endif
- return YES;
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- if (DS_OSX) {
- DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
- }
- return NSTerminateCancel;
-}
-
-- (void)showAbout:(id)sender {
- if (OS_OSX::get_singleton()->get_main_loop()) {
- OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+ return p_path;
}
}
-@end
-
-/*************************************************************************/
-/* OSXTerminalLogger */
-/*************************************************************************/
-
-class OSXTerminalLogger : public StdLogger {
-public:
- virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) {
- if (!should_log(true)) {
- return;
- }
-
- const char *err_details;
- if (p_rationale && p_rationale[0])
- err_details = p_rationale;
- else
- err_details = p_code;
-
- switch (p_type) {
- case ERR_WARNING:
- os_log_info(OS_LOG_DEFAULT,
- "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_SCRIPT:
- os_log_error(OS_LOG_DEFAULT,
- "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_SHADER:
- os_log_error(OS_LOG_DEFAULT,
- "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- case ERR_ERROR:
- default:
- os_log_error(OS_LOG_DEFAULT,
- "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
- err_details, p_function, p_file, p_line);
- logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
- logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
- break;
- }
- }
-};
-
-/*************************************************************************/
-/* OS_OSX */
-/*************************************************************************/
-
-String OS_OSX::get_unique_id() const {
- static String serial_number;
-
- if (serial_number.is_empty()) {
- io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
- CFStringRef serialNumberAsCFString = nullptr;
- if (platformExpert) {
- serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
- IOObjectRelease(platformExpert);
- }
+void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
+ // Prevent main loop from sleeping and redraw window during resize / modal popups.
- NSString *serialNumberAsNSString = nil;
- if (serialNumberAsCFString) {
- serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
- CFRelease(serialNumberAsCFString);
+ if (get_singleton()->get_main_loop()) {
+ Main::force_redraw();
+ if (!Main::is_iterating()) { // Avoid cyclic loop.
+ Main::iteration();
}
-
- serial_number = [serialNumberAsNSString UTF8String];
}
- return serial_number;
+ CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
}
-void OS_OSX::alert(const String &p_alert, const String &p_title) {
- NSAlert *window = [[NSAlert alloc] init];
- NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
- NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
-
- [window addButtonWithTitle:@"OK"];
- [window setMessageText:ns_title];
- [window setInformativeText:ns_alert];
- [window setAlertStyle:NSAlertStyleWarning];
+void OS_OSX::initialize() {
+ crash_handler.initialize();
- id key_window = [[NSApplication sharedApplication] keyWindow];
- [window runModal];
- [window release];
- if (key_window) {
- [key_window makeKeyAndOrderFront:nil];
- }
+ initialize_core();
}
void OS_OSX::initialize_core() {
@@ -276,17 +80,6 @@ void OS_OSX::initialize_core() {
DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
-void OS_OSX::initialize_joypads() {
- joypad_osx = memnew(JoypadOSX(Input::get_singleton()));
-}
-
-void OS_OSX::initialize() {
- crash_handler.initialize();
-
- initialize_core();
- //ensure_user_data_dir();
-}
-
void OS_OSX::finalize() {
#ifdef COREMIDI_ENABLED
midi_driver.close();
@@ -299,42 +92,63 @@ void OS_OSX::finalize() {
}
}
+void OS_OSX::initialize_joypads() {
+ joypad_osx = memnew(JoypadOSX(Input::get_singleton()));
+}
+
void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
main_loop = p_main_loop;
}
void OS_OSX::delete_main_loop() {
- if (!main_loop)
+ if (!main_loop) {
return;
+ }
+
memdelete(main_loop);
main_loop = nullptr;
}
+String OS_OSX::get_open_with_filename() const {
+ return open_with_filename;
+}
+
+void OS_OSX::set_open_with_filename(const String &p_path) {
+ open_with_filename = p_path;
+}
+
String OS_OSX::get_name() const {
return "macOS";
}
-_FORCE_INLINE_ String _get_framework_executable(const String p_path) {
- // Append framework executable name, or return as is if p_path is not a framework.
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- if (da->dir_exists(p_path) && da->file_exists(p_path.plus_file(p_path.get_file().get_basename()))) {
- return p_path.plus_file(p_path.get_file().get_basename());
- } else {
- return p_path;
+void OS_OSX::alert(const String &p_alert, const String &p_title) {
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_alert];
+ [window setAlertStyle:NSAlertStyleWarning];
+
+ id key_window = [[NSApplication sharedApplication] keyWindow];
+ [window runModal];
+ if (key_window) {
+ [key_window makeKeyAndOrderFront:nil];
}
}
Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
- String path = _get_framework_executable(p_path);
+ String path = get_framework_executable(p_path);
if (!FileAccess::exists(path)) {
- // This code exists so gdnative can load .dylib files from within the executable path.
- path = _get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
+ // Load .dylib or framework from within the executable path.
+ path = get_framework_executable(get_executable_path().get_base_dir().plus_file(p_path.get_file()));
}
if (!FileAccess::exists(path)) {
- // This code exists so gdnative can load .dylib files from a standard macOS location.
- path = _get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
+ // Load .dylib or framework from a standard macOS location.
+ path = get_framework_executable(get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file()));
}
p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
@@ -393,8 +207,8 @@ String OS_OSX::get_bundle_resource_dir() const {
NSBundle *main = [NSBundle mainBundle];
if (main) {
- NSString *resourcePath = [main resourcePath];
- ret.parse_utf8([resourcePath UTF8String]);
+ NSString *resource_path = [main resourcePath];
+ ret.parse_utf8([resource_path UTF8String]);
}
return ret;
}
@@ -404,9 +218,9 @@ String OS_OSX::get_bundle_icon_path() const {
NSBundle *main = [NSBundle mainBundle];
if (main) {
- NSString *iconPath = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
- if (iconPath) {
- ret.parse_utf8([iconPath UTF8String]);
+ NSString *icon_path = [[main infoDictionary] objectForKey:@"CFBundleIconFile"];
+ if (icon_path) {
+ ret.parse_utf8([icon_path UTF8String]);
}
}
return ret;
@@ -449,9 +263,7 @@ String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
if (found) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(id, NSUserDomainMask, YES);
if (paths && [paths count] >= 1) {
- char *utfs = strdup([[paths firstObject] UTF8String]);
- ret.parse_utf8(utfs);
- free(utfs);
+ ret.parse_utf8([[paths firstObject] UTF8String]);
}
}
@@ -475,12 +287,9 @@ String OS_OSX::get_locale() const {
}
String OS_OSX::get_executable_path() const {
- int ret;
- pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
-
- pid = getpid();
- ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
+ int pid = getpid();
+ pid_t ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
if (ret <= 0) {
return OS::get_executable_path();
} else {
@@ -491,18 +300,6 @@ String OS_OSX::get_executable_path() const {
}
}
-Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
- // If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly.
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname != nil) {
- String path;
- path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
- return create_process(path, p_arguments, r_child_id, false);
- } else {
- return create_process(get_executable_path(), p_arguments, r_child_id, false);
- }
-}
-
Error OS_OSX::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
if (@available(macOS 10.15, *)) {
// Use NSWorkspace if path is an .app bundle.
@@ -532,7 +329,6 @@ Error OS_OSX::create_process(const String &p_path, const List<String> &p_argumen
dispatch_semaphore_signal(lock);
}];
dispatch_semaphore_wait(lock, dispatch_time(DISPATCH_TIME_NOW, 20000000000)); // 20 sec timeout, wait for app to launch.
- dispatch_release(lock);
if (err == OK) {
if (r_child_id) {
@@ -549,17 +345,66 @@ Error OS_OSX::create_process(const String &p_path, const List<String> &p_argumen
}
}
-void OS_OSX::pre_wait_observer_cb(CFRunLoopObserverRef p_observer, CFRunLoopActivity p_activiy, void *p_context) {
- // Prevent main loop from sleeping and redraw window during resize / modal popups.
+Error OS_OSX::create_instance(const List<String> &p_arguments, ProcessID *r_child_id) {
+ // If executable is bundled, always execute editor instances as an app bundle to ensure app window is registered and activated correctly.
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname != nil) {
+ String path;
+ path.parse_utf8([[[NSBundle mainBundle] bundlePath] UTF8String]);
+ return create_process(path, p_arguments, r_child_id, false);
+ } else {
+ return create_process(get_executable_path(), p_arguments, r_child_id, false);
+ }
+}
- if (get_singleton()->get_main_loop()) {
- Main::force_redraw();
- if (!Main::is_iterating()) { // Avoid cyclic loop.
- Main::iteration();
+String OS_OSX::get_unique_id() const {
+ static String serial_number;
+
+ if (serial_number.is_empty()) {
+ io_service_t platform_expert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
+ CFStringRef serial_number_cf_string = nullptr;
+ if (platform_expert) {
+ serial_number_cf_string = (CFStringRef)IORegistryEntryCreateCFProperty(platform_expert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
+ IOObjectRelease(platform_expert);
+ }
+
+ NSString *serial_number_ns_string = nil;
+ if (serial_number_cf_string) {
+ serial_number_ns_string = [NSString stringWithString:(__bridge NSString *)serial_number_cf_string];
+ CFRelease(serial_number_cf_string);
+ }
+
+ if (serial_number_ns_string) {
+ serial_number.parse_utf8([serial_number_ns_string UTF8String]);
}
}
- CFRunLoopWakeUp(CFRunLoopGetCurrent()); // Prevent main loop from sleeping.
+ return serial_number;
+}
+
+bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
+ return p_feature == "pc";
+}
+
+void OS_OSX::disable_crash_handler() {
+ crash_handler.disable();
+}
+
+bool OS_OSX::is_disable_crash_handler() const {
+ return crash_handler.is_disabled();
+}
+
+Error OS_OSX::move_to_trash(const String &p_path) {
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
+ NSError *err;
+
+ if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) {
+ ERR_PRINT("trashItemAtURL error: " + String::utf8(err.localizedDescription.UTF8String));
+ return FAILED;
+ }
+
+ return OK;
}
void OS_OSX::run() {
@@ -571,14 +416,11 @@ void OS_OSX::run() {
main_loop->initialize();
- CFRunLoopObserverRef pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
- CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
-
bool quit = false;
while (!force_quit && !quit) {
@try {
if (DisplayServer::get_singleton()) {
- DisplayServer::get_singleton()->process_events(); // get rid of pending events
+ DisplayServer::get_singleton()->process_events(); // Get rid of pending events.
}
joypad_osx->process_joypads();
@@ -590,25 +432,9 @@ void OS_OSX::run() {
}
};
- CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
- CFRelease(pre_wait_observer);
-
main_loop->finalize();
}
-Error OS_OSX::move_to_trash(const String &p_path) {
- NSFileManager *fm = [NSFileManager defaultManager];
- NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
- NSError *err;
-
- if (![fm trashItemAtURL:url resultingItemURL:nil error:&err]) {
- ERR_PRINT("trashItemAtURL error: " + String::utf8(err.localizedDescription.UTF8String));
- return FAILED;
- }
-
- return OK;
-}
-
OS_OSX::OS_OSX() {
main_loop = nullptr;
force_quit = false;
@@ -623,17 +449,17 @@ OS_OSX::OS_OSX() {
DisplayServerOSX::register_osx_driver();
- // Implicitly create shared NSApplication instance
+ // Implicitly create shared NSApplication instance.
[GodotApplication sharedApplication];
- // In case we are unbundled, make us a proper UI application
+ // In case we are unbundled, make us a proper UI application.
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
// Menu bar setup must go between sharedApplication above and
// finishLaunching below, in order to properly emulate the behavior
- // of NSApplicationMain
+ // of NSApplicationMain.
- NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
[NSApp setMainMenu:main_menu];
[NSApp finishLaunching];
@@ -641,7 +467,10 @@ OS_OSX::OS_OSX() {
ERR_FAIL_COND(!delegate);
[NSApp setDelegate:delegate];
- //process application:openFile: event
+ pre_wait_observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, true, 0, &pre_wait_observer_cb, nullptr);
+ CFRunLoopAddObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
+
+ // Process application:openFile: event.
while (true) {
NSEvent *event = [NSApp
nextEventMatchingMask:NSEventMaskAny
@@ -659,14 +488,7 @@ OS_OSX::OS_OSX() {
[NSApp activateIgnoringOtherApps:YES];
}
-bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
- return p_feature == "pc";
-}
-
-void OS_OSX::disable_crash_handler() {
- crash_handler.disable();
-}
-
-bool OS_OSX::is_disable_crash_handler() const {
- return crash_handler.is_disabled();
+OS_OSX::~OS_OSX() {
+ CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), pre_wait_observer, kCFRunLoopCommonModes);
+ CFRelease(pre_wait_observer);
}
diff --git a/platform/osx/osx_terminal_logger.h b/platform/osx/osx_terminal_logger.h
new file mode 100644
index 0000000000..8413509c4b
--- /dev/null
+++ b/platform/osx/osx_terminal_logger.h
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* osx_terminal_logger.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OSX_TERMINAL_LOGGER_H
+#define OSX_TERMINAL_LOGGER_H
+
+#ifdef OSX_ENABLED
+
+#include "core/io/logger.h"
+
+class OSXTerminalLogger : public StdLogger {
+public:
+ virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR) override;
+};
+
+#endif // OSX_ENABLED
+#endif // OSX_TERMINAL_LOGGER_H
diff --git a/platform/osx/osx_terminal_logger.mm b/platform/osx/osx_terminal_logger.mm
new file mode 100644
index 0000000000..c1dca111a7
--- /dev/null
+++ b/platform/osx/osx_terminal_logger.mm
@@ -0,0 +1,81 @@
+/*************************************************************************/
+/* osx_terminal_logger.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "osx_terminal_logger.h"
+
+#ifdef OSX_ENABLED
+
+#include <os/log.h>
+
+void OSXTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type) {
+ if (!should_log(true)) {
+ return;
+ }
+
+ const char *err_details;
+ if (p_rationale && p_rationale[0])
+ err_details = p_rationale;
+ else
+ err_details = p_code;
+
+ switch (p_type) {
+ case ERR_WARNING:
+ os_log_info(OS_LOG_DEFAULT,
+ "WARNING: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;33mWARNING:\E[0;93m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_SCRIPT:
+ os_log_error(OS_LOG_DEFAULT,
+ "SCRIPT ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;35mSCRIPT ERROR:\E[0;95m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_SHADER:
+ os_log_error(OS_LOG_DEFAULT,
+ "SHADER ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;36mSHADER ERROR:\E[0;96m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ case ERR_ERROR:
+ default:
+ os_log_error(OS_LOG_DEFAULT,
+ "ERROR: %{public}s\nat: %{public}s (%{public}s:%i)",
+ err_details, p_function, p_file, p_line);
+ logf_error("\E[1;31mERROR:\E[0;91m %s\n", err_details);
+ logf_error("\E[0;90m at: %s (%s:%i)\E[0m\n", p_function, p_file, p_line);
+ break;
+ }
+}
+
+#endif // OSX_ENABLED
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index f32fab1eee..bdabc24c28 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -44,7 +44,7 @@ Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, Displ
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = nullptr;
createInfo.flags = 0;
- createInfo.pView = p_window;
+ createInfo.pView = (__bridge const void *)p_window;
VkSurfaceKHR surface;
VkResult err = vkCreateMacOSSurfaceMVK(get_instance(), &createInfo, nullptr, &surface);
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index 71e9d9acbd..5064f6b97f 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -33,7 +33,6 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "core/version.h"
-#include "core/version_hash.gen.h"
#include "main/main.h"
#ifdef CRASH_HANDLER_EXCEPTION
@@ -179,10 +178,10 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
}
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
- if (String(VERSION_HASH).length() != 0) {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME " (" VERSION_HASH ")\n");
+ if (String(VERSION_HASH).is_empty()) {
+ fprintf(stderr, "Engine version: %s\n", VERSION_FULL_NAME);
} else {
- fprintf(stderr, "Engine version: " VERSION_FULL_NAME "\n");
+ fprintf(stderr, "Engine version: %s (%s)\n", VERSION_FULL_NAME, VERSION_HASH);
}
fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data());
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index d288c27016..20268b3f6a 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -326,6 +326,12 @@ typedef struct {
Rect2i rect;
} EnumRectData;
+typedef struct {
+ int count;
+ int screen;
+ float rate;
+} EnumRefreshRateData;
+
static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
EnumSizeData *data = (EnumSizeData *)dwData;
if (data->count == data->screen) {
@@ -363,6 +369,26 @@ static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonito
return TRUE;
}
+static BOOL CALLBACK _MonitorEnumProcRefreshRate(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+ EnumRefreshRateData *data = (EnumRefreshRateData *)dwData;
+ if (data->count == data->screen) {
+ MONITORINFOEXW minfo;
+ memset(&minfo, 0, sizeof(minfo));
+ minfo.cbSize = sizeof(minfo);
+ GetMonitorInfoW(hMonitor, &minfo);
+
+ DEVMODEW dm;
+ memset(&dm, 0, sizeof(dm));
+ dm.dmSize = sizeof(dm);
+ EnumDisplaySettingsW(minfo.szDevice, ENUM_CURRENT_SETTINGS, &dm);
+
+ data->rate = dm.dmDisplayFrequency;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {
_THREAD_SAFE_METHOD_
@@ -446,6 +472,13 @@ int DisplayServerWindows::screen_get_dpi(int p_screen) const {
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcDpi, (LPARAM)&data);
return data.dpi;
}
+float DisplayServerWindows::screen_get_refresh_rate(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumRefreshRateData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, SCREEN_REFRESH_RATE_FALLBACK };
+ EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcRefreshRate, (LPARAM)&data);
+ return data.rate;
+}
bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
#ifndef _MSC_VER
@@ -506,13 +539,22 @@ DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mod
if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
wd.borderless = true;
}
- if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN) {
+ if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.always_on_top = true;
}
if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
wd.no_focus = true;
}
+ // Inherit icons from MAIN_WINDOW for all sub windows.
+ HICON mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_SMALL, 0);
+ if (mainwindow_icon) {
+ SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);
+ }
+ mainwindow_icon = (HICON)SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_GETICON, ICON_BIG, 0);
+ if (mainwindow_icon) {
+ SendMessage(windows[window_id].hWnd, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);
+ }
return window_id;
}
@@ -946,7 +988,7 @@ Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const {
return Size2();
}
-void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
+void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
// Windows docs for window styles:
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
@@ -959,6 +1001,9 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscre
if (p_fullscreen || p_borderless) {
r_style |= WS_POPUP; // p_borderless was WS_EX_TOOLWINDOW in the past.
+ if (p_fullscreen && p_multiwindow_fs) {
+ r_style |= WS_BORDER; // Allows child windows to be displayed on top of full screen.
+ }
} else {
if (p_resizable) {
if (p_maximized) {
@@ -989,7 +1034,7 @@ void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repain
DWORD style = 0;
DWORD style_ex = 0;
- _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex);
+ _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.multiwindow_fs, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex);
SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
@@ -1009,10 +1054,11 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
ERR_FAIL_COND(!windows.has(p_window));
WindowData &wd = windows[p_window];
- if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN) {
+ if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
RECT rect;
wd.fullscreen = false;
+ wd.multiwindow_fs = false;
wd.maximized = wd.was_maximized;
if (wd.pre_fs_valid) {
@@ -1051,7 +1097,15 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
wd.minimized = true;
}
- if (p_mode == WINDOW_MODE_FULLSCREEN && !wd.fullscreen) {
+ if (p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
+ wd.multiwindow_fs = false;
+ _update_window_style(false);
+ } else {
+ wd.multiwindow_fs = true;
+ _update_window_style(false);
+ }
+
+ if ((p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) && !wd.fullscreen) {
if (wd.minimized) {
ShowWindow(wd.hWnd, SW_RESTORE);
}
@@ -1098,7 +1152,11 @@ DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_windo
const WindowData &wd = windows[p_window];
if (wd.fullscreen) {
- return WINDOW_MODE_FULLSCREEN;
+ if (wd.multiwindow_fs) {
+ return WINDOW_MODE_FULLSCREEN;
+ } else {
+ return WINDOW_MODE_EXCLUSIVE_FULLSCREEN;
+ }
} else if (wd.minimized) {
return WINDOW_MODE_MINIMIZED;
} else if (wd.maximized) {
@@ -2649,98 +2707,77 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
} break;
- case WM_MOVE: {
- if (!IsIconic(windows[window_id].hWnd)) {
- int x = int16_t(LOWORD(lParam));
- int y = int16_t(HIWORD(lParam));
- windows[window_id].last_pos = Point2(x, y);
-
- if (!windows[window_id].rect_changed_callback.is_null()) {
- Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
- Variant *sizep = &size;
- Variant ret;
- Callable::CallError ce;
- windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+
+ case WM_WINDOWPOSCHANGED: {
+ Rect2i window_client_rect;
+ Rect2i window_rect;
+ {
+ RECT rect;
+ GetClientRect(hWnd, &rect);
+ ClientToScreen(hWnd, (POINT *)&rect.left);
+ ClientToScreen(hWnd, (POINT *)&rect.right);
+ window_client_rect = Rect2i(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
+
+ RECT wrect;
+ GetWindowRect(hWnd, &wrect);
+ window_rect = Rect2i(wrect.left, wrect.top, wrect.right - wrect.left, wrect.bottom - wrect.top);
+ }
+
+ WINDOWPOS *window_pos_params = (WINDOWPOS *)lParam;
+ WindowData &window = windows[window_id];
+
+ bool rect_changed = false;
+ if (!(window_pos_params->flags & SWP_NOSIZE) || window_pos_params->flags & SWP_FRAMECHANGED) {
+ int screen_id = window_get_current_screen(window_id);
+ Size2i screen_size = screen_get_size(screen_id);
+ Point2i screen_position = screen_get_position(screen_id);
+
+ window.maximized = false;
+ window.minimized = false;
+ window.fullscreen = false;
+
+ if (IsIconic(hWnd)) {
+ window.minimized = true;
+ } else if (IsZoomed(hWnd)) {
+ window.maximized = true;
+ } else if (window_rect.position == screen_position && window_rect.size == screen_size) {
+ window.fullscreen = true;
}
- }
- } break;
- case WM_SIZE: {
- // Ignore window size change when a SIZE_MINIMIZED event is triggered.
- if (wParam != SIZE_MINIMIZED) {
- // The new width and height of the client area.
- int window_w = LOWORD(lParam);
- int window_h = HIWORD(lParam);
-
- // Set new value to the size if it isn't preserved.
- if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) {
- windows[window_id].width = window_w;
- windows[window_id].height = window_h;
+
+ if (!window.minimized) {
+ window.width = window_client_rect.size.width;
+ window.height = window_client_rect.size.height;
#if defined(VULKAN_ENABLED)
if (context_vulkan && window_created) {
- context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height);
+ context_vulkan->window_resize(window_id, window.width, window.height);
}
#endif
+ rect_changed = true;
+ }
+ }
- } else { // If the size is preserved.
- windows[window_id].preserve_window_size = false;
+ if (!window.minimized && (!(window_pos_params->flags & SWP_NOMOVE) || window_pos_params->flags & SWP_FRAMECHANGED)) {
+ window.last_pos = window_client_rect.position;
+ rect_changed = true;
+ }
- // Restore the old size.
- window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id);
+ if (rect_changed) {
+ if (!window.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(window.last_pos.x, window.last_pos.y, window.width, window.height);
+ const Variant *args[] = { &size };
+ Variant ret;
+ Callable::CallError ce;
+ window.rect_changed_callback.call(args, 1, ret, ce);
}
- } else { // When the window has been minimized, preserve its size.
- windows[window_id].preserve_window_size = true;
}
- // Call windows rect change callback.
- if (!windows[window_id].rect_changed_callback.is_null()) {
- Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
- Variant *size_ptr = &size;
- Variant ret;
- Callable::CallError ce;
- windows[window_id].rect_changed_callback.call((const Variant **)&size_ptr, 1, ret, ce);
- }
-
- // The window has been maximized.
- if (wParam == SIZE_MAXIMIZED) {
- windows[window_id].maximized = true;
- windows[window_id].minimized = false;
- }
- // The window has been minimized.
- else if (wParam == SIZE_MINIMIZED) {
- windows[window_id].maximized = false;
- windows[window_id].minimized = true;
- windows[window_id].preserve_window_size = false;
- }
- // The window has been resized, but neither the SIZE_MINIMIZED nor SIZE_MAXIMIZED value applies.
- else if (wParam == SIZE_RESTORED) {
- windows[window_id].maximized = false;
- windows[window_id].minimized = false;
- }
-#if 0
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2i(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, nullptr, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
- }
-#endif
+ // Return here to prevent WM_MOVE and WM_SIZE from being sent
+ // See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks
+ return 0;
+
} break;
+
case WM_ENTERSIZEMOVE: {
Input::get_singleton()->release_pressed_events();
windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr);
@@ -3088,7 +3125,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
DWORD dwExStyle;
DWORD dwStyle;
- _get_window_style(window_id_counter == MAIN_WINDOW_ID, p_mode == WINDOW_MODE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle);
+ _get_window_style(window_id_counter == MAIN_WINDOW_ID, (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN), p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle);
RECT WindowRect;
@@ -3097,7 +3134,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
WindowRect.top = p_rect.position.y;
WindowRect.bottom = p_rect.position.y + p_rect.size.y;
- if (p_mode == WINDOW_MODE_FULLSCREEN) {
+ if (p_mode == WINDOW_MODE_FULLSCREEN || p_mode == WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
int nearest_area = 0;
Rect2i screen_rect;
for (int i = 0; i < get_screen_count(); i++) {
@@ -3140,7 +3177,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
windows.erase(id);
return INVALID_WINDOW_ID;
}
- if (p_mode != WINDOW_MODE_FULLSCREEN) {
+ if (p_mode != WINDOW_MODE_FULLSCREEN && p_mode != WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
wd.pre_fs_valid = true;
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 803c2d4836..d36ca97ebe 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -326,12 +326,12 @@ class DisplayServerWindows : public DisplayServer {
Vector<Vector2> mpath;
- bool preserve_window_size = false;
bool pre_fs_valid = false;
RECT pre_fs_rect;
bool maximized = false;
bool minimized = false;
bool fullscreen = false;
+ bool multiwindow_fs = false;
bool borderless = false;
bool resizable = true;
bool window_focused = false;
@@ -401,7 +401,7 @@ class DisplayServerWindows : public DisplayServer {
WNDPROC user_proc = nullptr;
void _send_window_event(const WindowData &wd, WindowEvent p_event);
- void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
+ void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
MouseMode mouse_mode;
int restore_mouse_trails = 0;
@@ -458,6 +458,7 @@ public:
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 4bead978f1..e685ad8f67 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -104,8 +104,8 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array
}
}
-Array NavigationPolygon::_get_polygons() const {
- Array ret;
+TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
+ TypedArray<Vector<int32_t>> ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
ret[i] = polygons[i].indices;
@@ -122,8 +122,8 @@ void NavigationPolygon::_set_outlines(const TypedArray<Vector<Vector2>> &p_array
rect_cache_dirty = true;
}
-Array NavigationPolygon::_get_outlines() const {
- Array ret;
+TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
+ TypedArray<Vector<Vector2>> ret;
ret.resize(outlines.size());
for (int i = 0; i < ret.size(); i++) {
ret[i] = outlines[i];
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index 012debb584..487a578401 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -55,10 +55,10 @@ protected:
static void _bind_methods();
void _set_polygons(const TypedArray<Vector<int32_t>> &p_array);
- Array _get_polygons() const;
+ TypedArray<Vector<int32_t>> _get_polygons() const;
void _set_outlines(const TypedArray<Vector<Vector2>> &p_array);
- Array _get_outlines() const;
+ TypedArray<Vector<Vector2>> _get_outlines() const;
public:
#ifdef TOOLS_ENABLED
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 9521667854..51b3e676f9 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -279,6 +279,13 @@ void RayCast2D::remove_exception(const CollisionObject2D *p_node) {
void RayCast2D::clear_exceptions() {
exclude.clear();
+
+ if (exclude_parent_body && is_inside_tree()) {
+ CollisionObject2D *parent = Object::cast_to<CollisionObject2D>(get_parent());
+ if (parent) {
+ exclude.insert(parent->get_rid());
+ }
+ }
}
void RayCast2D::set_collide_with_areas(bool p_enabled) {
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index e0e2eae4a5..0277171922 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -30,41 +30,24 @@
#include "occluder_instance_3d.h"
#include "core/core_string_names.h"
+#include "core/math/geometry_2d.h"
+#include "core/math/triangulate.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/resources/importer_mesh.h"
+#include "scene/resources/surface_tool.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/editor_node.h"
+#endif
RID Occluder3D::get_rid() const {
- if (!occluder.is_valid()) {
- occluder = RS::get_singleton()->occluder_create();
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
return occluder;
}
-void Occluder3D::set_vertices(PackedVector3Array p_vertices) {
- vertices = p_vertices;
- if (occluder.is_valid()) {
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
- _update_changes();
-}
-
-PackedVector3Array Occluder3D::get_vertices() const {
- return vertices;
-}
-
-void Occluder3D::set_indices(PackedInt32Array p_indices) {
- indices = p_indices;
- if (occluder.is_valid()) {
- RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
- }
- _update_changes();
-}
+void Occluder3D::_update() {
+ _update_arrays(vertices, indices);
-PackedInt32Array Occluder3D::get_indices() const {
- return indices;
-}
-
-void Occluder3D::_update_changes() {
aabb = AABB();
const Vector3 *ptr = vertices.ptr();
@@ -75,9 +58,18 @@ void Occluder3D::_update_changes() {
debug_lines.clear();
debug_mesh.unref();
+ RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices);
emit_changed();
}
+PackedVector3Array Occluder3D::get_vertices() const {
+ return vertices;
+}
+
+PackedInt32Array Occluder3D::get_indices() const {
+ return indices;
+}
+
Vector<Vector3> Occluder3D::get_debug_lines() const {
if (!debug_lines.is_empty()) {
return debug_lines;
@@ -87,14 +79,18 @@ Vector<Vector3> Occluder3D::get_debug_lines() const {
return Vector<Vector3>();
}
+ const Vector3 *vertices_ptr = vertices.ptr();
+ debug_lines.resize(indices.size() / 3 * 6);
+ Vector3 *line_ptr = debug_lines.ptrw();
+ int line_i = 0;
for (int i = 0; i < indices.size() / 3; i++) {
for (int j = 0; j < 3; j++) {
int a = indices[i * 3 + j];
int b = indices[i * 3 + (j + 1) % 3];
ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range.");
- debug_lines.push_back(vertices[a]);
- debug_lines.push_back(vertices[b]);
+ line_ptr[line_i++] = vertices_ptr[a];
+ line_ptr[line_i++] = vertices_ptr[b];
}
}
return debug_lines;
@@ -105,7 +101,7 @@ Ref<ArrayMesh> Occluder3D::get_debug_mesh() const {
return debug_mesh;
}
- if (indices.size() % 3 != 0) {
+ if (vertices.is_empty() || indices.is_empty() || indices.size() % 3 != 0) {
return debug_mesh;
}
@@ -123,18 +119,17 @@ AABB Occluder3D::get_aabb() const {
return aabb;
}
-void Occluder3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &Occluder3D::set_vertices);
- ClassDB::bind_method(D_METHOD("get_vertices"), &Occluder3D::get_vertices);
-
- ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices);
- ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices);
+void Occluder3D::_notification(int p_what) {
+ if (p_what == NOTIFICATION_POSTINITIALIZE) {
+ _update();
+ }
+}
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
+void Occluder3D::_bind_methods() {
}
Occluder3D::Occluder3D() {
+ occluder = RS::get_singleton()->occluder_create();
}
Occluder3D::~Occluder3D() {
@@ -142,6 +137,291 @@ Occluder3D::~Occluder3D() {
RS::get_singleton()->free(occluder);
}
}
+
+/////////////////////////////////////////////////
+
+void ArrayOccluder3D::set_arrays(PackedVector3Array p_vertices, PackedInt32Array p_indices) {
+ vertices = p_vertices;
+ indices = p_indices;
+ _update();
+}
+
+void ArrayOccluder3D::set_vertices(PackedVector3Array p_vertices) {
+ vertices = p_vertices;
+ _update();
+}
+
+void ArrayOccluder3D::set_indices(PackedInt32Array p_indices) {
+ indices = p_indices;
+ _update();
+}
+
+void ArrayOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ r_vertices = vertices;
+ r_indices = indices;
+}
+
+void ArrayOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_arrays", "vertices", "indices"), &ArrayOccluder3D::set_arrays);
+
+ ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &ArrayOccluder3D::set_vertices);
+ ClassDB::bind_method(D_METHOD("get_vertices"), &ArrayOccluder3D::get_vertices);
+
+ ClassDB::bind_method(D_METHOD("set_indices", "indices"), &ArrayOccluder3D::set_indices);
+ ClassDB::bind_method(D_METHOD("get_indices"), &ArrayOccluder3D::get_indices);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_vertices", "get_vertices");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_indices", "get_indices");
+}
+
+ArrayOccluder3D::ArrayOccluder3D() {
+}
+
+ArrayOccluder3D::~ArrayOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void QuadOccluder3D::set_size(const Vector2 &p_size) {
+ if (size == p_size) {
+ return;
+ }
+
+ size = p_size.max(Vector2());
+ ;
+ _update();
+}
+
+Vector2 QuadOccluder3D::get_size() const {
+ return size;
+}
+
+void QuadOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ Vector2 _size = Vector2(size.x / 2.0f, size.y / 2.0f);
+
+ r_vertices = {
+ Vector3(-_size.x, -_size.y, 0),
+ Vector3(-_size.x, _size.y, 0),
+ Vector3(_size.x, _size.y, 0),
+ Vector3(_size.x, -_size.y, 0),
+ };
+
+ r_indices = {
+ 0, 1, 2,
+ 0, 2, 3
+ };
+}
+
+void QuadOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &QuadOccluder3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &QuadOccluder3D::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
+}
+
+QuadOccluder3D::QuadOccluder3D() {
+}
+
+QuadOccluder3D::~QuadOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void BoxOccluder3D::set_size(const Vector3 &p_size) {
+ if (size == p_size) {
+ return;
+ }
+
+ size = Vector3(MAX(p_size.x, 0.0f), MAX(p_size.y, 0.0f), MAX(p_size.z, 0.0f));
+ ;
+ _update();
+}
+
+Vector3 BoxOccluder3D::get_size() const {
+ return size;
+}
+
+void BoxOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ Vector3 _size = Vector3(size.x / 2.0f, size.y / 2.0f, size.z / 2.0f);
+
+ r_vertices = {
+ // front
+ Vector3(-_size.x, -_size.y, _size.z),
+ Vector3(_size.x, -_size.y, _size.z),
+ Vector3(_size.x, _size.y, _size.z),
+ Vector3(-_size.x, _size.y, _size.z),
+ // back
+ Vector3(-_size.x, -_size.y, -_size.z),
+ Vector3(_size.x, -_size.y, -_size.z),
+ Vector3(_size.x, _size.y, -_size.z),
+ Vector3(-_size.x, _size.y, -_size.z),
+ };
+
+ r_indices = {
+ // front
+ 0, 1, 2,
+ 2, 3, 0,
+ // right
+ 1, 5, 6,
+ 6, 2, 1,
+ // back
+ 7, 6, 5,
+ 5, 4, 7,
+ // left
+ 4, 0, 3,
+ 3, 7, 4,
+ // bottom
+ 4, 5, 1,
+ 1, 0, 4,
+ // top
+ 3, 2, 6,
+ 6, 7, 3
+ };
+}
+
+void BoxOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &BoxOccluder3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &BoxOccluder3D::get_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+}
+
+BoxOccluder3D::BoxOccluder3D() {
+}
+
+BoxOccluder3D::~BoxOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void SphereOccluder3D::set_radius(float p_radius) {
+ if (radius == p_radius) {
+ return;
+ }
+
+ radius = MAX(p_radius, 0.0f);
+ _update();
+}
+
+float SphereOccluder3D::get_radius() const {
+ return radius;
+}
+
+void SphereOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ r_vertices.resize((RINGS + 2) * (RADIAL_SEGMENTS + 1));
+ int vertex_i = 0;
+ Vector3 *vertex_ptr = r_vertices.ptrw();
+
+ r_indices.resize((RINGS + 1) * RADIAL_SEGMENTS * 6);
+ int idx_i = 0;
+ int *idx_ptr = r_indices.ptrw();
+
+ int current_row = 0;
+ int previous_row = 0;
+ int point = 0;
+ for (int j = 0; j <= (RINGS + 1); j++) {
+ float v = j / float(RINGS + 1);
+ float w = Math::sin(Math_PI * v);
+ float y = Math::cos(Math_PI * v);
+ for (int i = 0; i <= RADIAL_SEGMENTS; i++) {
+ float u = i / float(RADIAL_SEGMENTS);
+
+ float x = Math::cos(u * Math_TAU);
+ float z = Math::sin(u * Math_TAU);
+ vertex_ptr[vertex_i++] = Vector3(x * w, y, z * w) * radius;
+
+ if (i > 0 && j > 0) {
+ idx_ptr[idx_i++] = previous_row + i - 1;
+ idx_ptr[idx_i++] = previous_row + i;
+ idx_ptr[idx_i++] = current_row + i - 1;
+
+ idx_ptr[idx_i++] = previous_row + i;
+ idx_ptr[idx_i++] = current_row + i;
+ idx_ptr[idx_i++] = current_row + i - 1;
+ }
+
+ point++;
+ }
+
+ previous_row = current_row;
+ current_row = point;
+ }
+}
+
+void SphereOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &SphereOccluder3D::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &SphereOccluder3D::get_radius);
+
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius"), "set_radius", "get_radius");
+}
+
+SphereOccluder3D::SphereOccluder3D() {
+}
+
+SphereOccluder3D::~SphereOccluder3D() {
+}
+
+/////////////////////////////////////////////////
+
+void PolygonOccluder3D::set_polygon(const Vector<Vector2> &p_polygon) {
+ polygon = p_polygon;
+ _update();
+}
+
+Vector<Vector2> PolygonOccluder3D::get_polygon() const {
+ return polygon;
+}
+
+void PolygonOccluder3D::_update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ if (polygon.size() < 3) {
+ r_vertices.clear();
+ r_indices.clear();
+ return;
+ }
+
+ Vector<Point2> occluder_polygon = polygon;
+ if (Triangulate::get_area(occluder_polygon) > 0) {
+ occluder_polygon.reverse();
+ }
+
+ Vector<int> occluder_indices = Geometry2D::triangulate_polygon(occluder_polygon);
+
+ if (occluder_indices.size() < 3) {
+ r_vertices.clear();
+ r_indices.clear();
+ ERR_FAIL_MSG("Failed to triangulate PolygonOccluder3D. Make sure the polygon doesn't have any intersecting edges.");
+ }
+
+ r_vertices.resize(occluder_polygon.size());
+ Vector3 *vertex_ptr = r_vertices.ptrw();
+ const Vector2 *polygon_ptr = occluder_polygon.ptr();
+ for (int i = 0; i < occluder_polygon.size(); i++) {
+ vertex_ptr[i] = Vector3(polygon_ptr[i].x, polygon_ptr[i].y, 0.0);
+ }
+
+ r_indices.resize(occluder_indices.size());
+ memcpy(r_indices.ptrw(), occluder_indices.ptr(), occluder_indices.size() * sizeof(int));
+}
+
+bool PolygonOccluder3D::_has_editable_3d_polygon_no_depth() const {
+ return false;
+}
+
+void PolygonOccluder3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &PolygonOccluder3D::set_polygon);
+ ClassDB::bind_method(D_METHOD("get_polygon"), &PolygonOccluder3D::get_polygon);
+
+ ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &PolygonOccluder3D::_has_editable_3d_polygon_no_depth);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
+}
+
+PolygonOccluder3D::PolygonOccluder3D() {
+}
+
+PolygonOccluder3D::~PolygonOccluder3D() {
+}
+
/////////////////////////////////////////////////
AABB OccluderInstance3D::get_aabb() const {
@@ -175,6 +455,13 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {
update_gizmos();
update_configuration_warnings();
+
+#ifdef TOOLS_ENABLED
+ // PolygonOccluder3D is edited via an editor plugin, this ensures the plugin is shown/hidden when necessary
+ if (Engine::get_singleton()->is_editor_hint()) {
+ EditorNode::get_singleton()->call_deferred(SNAME("edit_current"));
+ }
+#endif
}
void OccluderInstance3D::_occluder_changed() {
@@ -195,6 +482,14 @@ uint32_t OccluderInstance3D::get_bake_mask() const {
return bake_mask;
}
+void OccluderInstance3D::set_bake_simplification_distance(float p_dist) {
+ bake_simplification_dist = MAX(p_dist, 0.0f);
+}
+
+float OccluderInstance3D::get_bake_simplification_distance() const {
+ return bake_simplification_dist;
+}
+
void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) {
ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive.");
ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive.");
@@ -221,6 +516,47 @@ bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) {
return true;
}
+void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_surface_arrays, Ref<Material> p_material, float p_simplification_dist, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ if (!_bake_material_check(p_material)) {
+ return;
+ }
+ ERR_FAIL_COND_MSG(p_surface_arrays.size() != Mesh::ARRAY_MAX, "Invalid surface array.");
+
+ PackedVector3Array vertices = p_surface_arrays[Mesh::ARRAY_VERTEX];
+ PackedInt32Array indices = p_surface_arrays[Mesh::ARRAY_INDEX];
+
+ if (vertices.size() == 0 || indices.size() == 0) {
+ return;
+ }
+
+ Vector3 *vertices_ptr = vertices.ptrw();
+ for (int j = 0; j < vertices.size(); j++) {
+ vertices_ptr[j] = p_transform.xform(vertices_ptr[j]);
+ }
+
+ if (!Math::is_zero_approx(p_simplification_dist) && SurfaceTool::simplify_func) {
+ float error_scale = SurfaceTool::simplify_scale_func((float *)vertices.ptr(), vertices.size(), sizeof(Vector3));
+ float target_error = p_simplification_dist / error_scale;
+ float error = -1.0f;
+ int target_index_count = MIN(indices.size(), 36);
+ uint32_t index_count = SurfaceTool::simplify_func((unsigned int *)indices.ptrw(), (unsigned int *)indices.ptr(), indices.size(), (float *)vertices.ptr(), vertices.size(), sizeof(Vector3), target_index_count, target_error, &error);
+ indices.resize(index_count);
+ }
+
+ SurfaceTool::strip_mesh_arrays(vertices, indices);
+
+ int vertex_offset = r_vertices.size();
+ r_vertices.resize(vertex_offset + vertices.size());
+ memcpy(r_vertices.ptrw() + vertex_offset, vertices.ptr(), vertices.size() * sizeof(Vector3));
+
+ int index_offset = r_indices.size();
+ r_indices.resize(index_offset + indices.size());
+ int *idx_ptr = r_indices.ptrw();
+ for (int j = 0; j < indices.size(); j++) {
+ idx_ptr[index_offset + j] = vertex_offset + indices[j];
+ }
+}
+
void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
if (mi && mi->is_visible_in_tree()) {
@@ -243,58 +579,76 @@ void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices
Transform3D global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform();
for (int i = 0; i < mesh->get_surface_count(); i++) {
- if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
- continue;
- }
+ _bake_surface(global_to_local, mesh->surface_get_arrays(i), mi->get_active_material(i), bake_simplification_dist, r_vertices, r_indices);
+ }
+ }
+ }
- if (mi->get_surface_override_material(i).is_valid()) {
- if (!_bake_material_check(mi->get_surface_override_material(i))) {
- continue;
- }
- } else {
- if (!_bake_material_check(mesh->surface_get_material(i))) {
- continue;
- }
- }
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ Node *child = p_node->get_child(i);
+ if (!child->get_owner()) {
+ continue; // may be a helper
+ }
- Array arrays = mesh->surface_get_arrays(i);
+ _bake_node(child, r_vertices, r_indices);
+ }
+}
- int vertex_offset = r_vertices.size();
- PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX];
- r_vertices.resize(r_vertices.size() + vertices.size());
+void OccluderInstance3D::bake_single_node(const Node3D *p_node, float p_simplification_distance, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ ERR_FAIL_COND(!p_node);
- Vector3 *vtx_ptr = r_vertices.ptrw();
- for (int j = 0; j < vertices.size(); j++) {
- vtx_ptr[vertex_offset + j] = global_to_local.xform(vertices[j]);
- }
+ Transform3D xform = p_node->is_inside_tree() ? p_node->get_global_transform() : p_node->get_transform();
- int index_offset = r_indices.size();
- PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX];
- r_indices.resize(r_indices.size() + indices.size());
+ const MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ if (mi) {
+ Ref<Mesh> mesh = mi->get_mesh();
+ bool valid = true;
- int *idx_ptr = r_indices.ptrw();
- for (int j = 0; j < indices.size(); j++) {
- idx_ptr[index_offset + j] = vertex_offset + indices[j];
- }
+ if (mesh.is_null()) {
+ valid = false;
+ }
+
+ if (valid && !_bake_material_check(mi->get_material_override())) {
+ valid = false;
+ }
+
+ if (valid) {
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ _bake_surface(xform, mesh->surface_get_arrays(i), mi->get_active_material(i), p_simplification_distance, r_vertices, r_indices);
}
}
}
- for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *child = p_node->get_child(i);
- if (!child->get_owner()) {
- continue; //maybe a helper
+ const ImporterMeshInstance3D *imi = Object::cast_to<ImporterMeshInstance3D>(p_node);
+ if (imi) {
+ Ref<ImporterMesh> mesh = imi->get_mesh();
+ bool valid = true;
+
+ if (mesh.is_null()) {
+ valid = false;
}
- _bake_node(child, r_vertices, r_indices);
+ if (valid) {
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ Ref<Material> material = imi->get_surface_material(i);
+ if (material.is_null()) {
+ material = mesh->get_surface_material(i);
+ }
+ _bake_surface(xform, mesh->get_surface_arrays(i), material, p_simplification_distance, r_vertices, r_indices);
+ }
+ }
}
}
-OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String p_occluder_path) {
+OccluderInstance3D::BakeError OccluderInstance3D::bake_scene(Node *p_from_node, String p_occluder_path) {
if (p_occluder_path.is_empty()) {
if (get_occluder().is_null()) {
return BAKE_ERROR_NO_SAVE_PATH;
}
+ p_occluder_path = get_occluder()->get_path();
+ if (!p_occluder_path.is_resource_file()) {
+ return BAKE_ERROR_NO_SAVE_PATH;
+ }
}
PackedVector3Array vertices;
@@ -306,16 +660,25 @@ OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String
return BAKE_ERROR_NO_MESHES;
}
- Ref<Occluder3D> occ;
+ Ref<ArrayOccluder3D> occ;
if (get_occluder().is_valid()) {
occ = get_occluder();
- } else {
+ set_occluder(Ref<Occluder3D>()); // clear
+ }
+
+ if (occ.is_null()) {
occ.instantiate();
- occ->set_path(p_occluder_path);
}
- occ->set_vertices(vertices);
- occ->set_indices(indices);
+ occ->set_arrays(vertices, indices);
+
+ Error err = ResourceSaver::save(p_occluder_path, occ);
+
+ if (err != OK) {
+ return BAKE_ERROR_CANT_SAVE;
+ }
+
+ occ->set_path(p_occluder_path);
set_occluder(occ);
return BAKE_ERROR_OK;
@@ -333,28 +696,49 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const {
}
if (occluder.is_null()) {
- warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
- } else if (occluder->get_vertices().size() < 3) {
- // Using the "New Occluder" dropdown button won't result in a correct occluder,
- // so warn the user about this.
- warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
+ warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, set the Occluder property to one of the primitive occluder types or bake the scene meshes by selecting the OccluderInstance3D and pressing the Bake Occluders button at the top of the 3D editor viewport."));
+ } else {
+ Ref<ArrayOccluder3D> arr_occluder = occluder;
+ if (arr_occluder.is_valid() && arr_occluder->get_indices().size() < 3) {
+ // Setting a new ArrayOccluder3D from the inspector will create an empty occluder,
+ // so warn the user about this.
+ warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport."));
+ }
+ Ref<PolygonOccluder3D> poly_occluder = occluder;
+ if (poly_occluder.is_valid() && poly_occluder->get_polygon().size() < 3) {
+ warnings.push_back(TTR("The polygon occluder has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nVertices can be added in the inspector or using the polygon editing tools at the top of the 3D editor viewport."));
+ }
}
return warnings;
}
+bool OccluderInstance3D::_is_editable_3d_polygon() const {
+ return Ref<PolygonOccluder3D>(occluder).is_valid();
+}
+
+Ref<Resource> OccluderInstance3D::_get_editable_3d_polygon_resource() const {
+ return occluder;
+}
+
void OccluderInstance3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask);
ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask);
ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value);
ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value);
+ ClassDB::bind_method(D_METHOD("set_bake_simplification_distance", "simplification_distance"), &OccluderInstance3D::set_bake_simplification_distance);
+ ClassDB::bind_method(D_METHOD("get_bake_simplification_distance"), &OccluderInstance3D::get_bake_simplification_distance);
ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder);
ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder);
+ ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &OccluderInstance3D::_is_editable_3d_polygon);
+ ClassDB::bind_method(D_METHOD("_get_editable_3d_polygon_resource"), &OccluderInstance3D::_get_editable_3d_polygon_resource);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder");
ADD_GROUP("Bake", "bake_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_bake_simplification_distance", "get_bake_simplification_distance");
}
OccluderInstance3D::OccluderInstance3D() {
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index 8a8d4c9af4..669ddba775 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -37,24 +37,23 @@ class Occluder3D : public Resource {
GDCLASS(Occluder3D, Resource);
RES_BASE_EXTENSION("occ");
- mutable RID occluder;
- mutable Ref<ArrayMesh> debug_mesh;
- mutable Vector<Vector3> debug_lines;
- AABB aabb;
-
+ RID occluder;
PackedVector3Array vertices;
PackedInt32Array indices;
+ AABB aabb;
- void _update_changes();
+ mutable Ref<ArrayMesh> debug_mesh;
+ mutable Vector<Vector3> debug_lines;
protected:
+ void _update();
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0;
+
static void _bind_methods();
+ void _notification(int p_what);
public:
- void set_vertices(PackedVector3Array p_vertices);
PackedVector3Array get_vertices() const;
-
- void set_indices(PackedInt32Array p_indices);
PackedInt32Array get_indices() const;
Vector<Vector3> get_debug_lines() const;
@@ -63,7 +62,102 @@ public:
virtual RID get_rid() const override;
Occluder3D();
- ~Occluder3D();
+ virtual ~Occluder3D();
+};
+
+class ArrayOccluder3D : public Occluder3D {
+ GDCLASS(ArrayOccluder3D, Occluder3D);
+
+ PackedVector3Array vertices;
+ PackedInt32Array indices;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ void set_arrays(PackedVector3Array p_vertices, PackedInt32Array p_indices);
+ void set_vertices(PackedVector3Array p_vertices);
+ void set_indices(PackedInt32Array p_indices);
+
+ ArrayOccluder3D();
+ ~ArrayOccluder3D();
+};
+
+class QuadOccluder3D : public Occluder3D {
+ GDCLASS(QuadOccluder3D, Occluder3D);
+
+private:
+ Vector2 size = Vector2(1.0f, 1.0f);
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ Vector2 get_size() const;
+ void set_size(const Vector2 &p_size);
+
+ QuadOccluder3D();
+ ~QuadOccluder3D();
+};
+
+class BoxOccluder3D : public Occluder3D {
+ GDCLASS(BoxOccluder3D, Occluder3D);
+
+private:
+ Vector3 size = Vector3(1.0f, 1.0f, 1.0f);
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ Vector3 get_size() const;
+ void set_size(const Vector3 &p_size);
+
+ BoxOccluder3D();
+ ~BoxOccluder3D();
+};
+
+class SphereOccluder3D : public Occluder3D {
+ GDCLASS(SphereOccluder3D, Occluder3D);
+
+private:
+ static constexpr int RINGS = 7;
+ static constexpr int RADIAL_SEGMENTS = 7;
+ float radius = 1.0f;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ float get_radius() const;
+ void set_radius(float p_radius);
+
+ SphereOccluder3D();
+ ~SphereOccluder3D();
+};
+
+class PolygonOccluder3D : public Occluder3D {
+ GDCLASS(PolygonOccluder3D, Occluder3D);
+
+private:
+ Vector<Vector2> polygon;
+
+ bool _has_editable_3d_polygon_no_depth() const;
+
+protected:
+ virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) override;
+ static void _bind_methods();
+
+public:
+ void set_polygon(const Vector<Vector2> &p_polygon);
+ Vector<Vector2> get_polygon() const;
+
+ PolygonOccluder3D();
+ ~PolygonOccluder3D();
};
class OccluderInstance3D : public VisualInstance3D {
@@ -72,12 +166,17 @@ class OccluderInstance3D : public VisualInstance3D {
private:
Ref<Occluder3D> occluder;
uint32_t bake_mask = 0xFFFFFFFF;
+ float bake_simplification_dist = 0.1f;
void _occluder_changed();
- bool _bake_material_check(Ref<Material> p_material);
+ static bool _bake_material_check(Ref<Material> p_material);
+ static void _bake_surface(const Transform3D &p_transform, Array p_surface_arrays, Ref<Material> p_material, float p_simplification_dist, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
void _bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
+ bool _is_editable_3d_polygon() const;
+ Ref<Resource> _get_editable_3d_polygon_resource() const;
+
protected:
static void _bind_methods();
@@ -88,6 +187,7 @@ public:
BAKE_ERROR_OK,
BAKE_ERROR_NO_SAVE_PATH,
BAKE_ERROR_NO_MESHES,
+ BAKE_ERROR_CANT_SAVE,
};
void set_occluder(const Ref<Occluder3D> &p_occluder);
@@ -99,10 +199,14 @@ public:
void set_bake_mask(uint32_t p_mask);
uint32_t get_bake_mask() const;
+ void set_bake_simplification_distance(float p_dist);
+ float get_bake_simplification_distance() const;
+
void set_bake_mask_value(int p_layer_number, bool p_enable);
bool get_bake_mask_value(int p_layer_number) const;
- BakeError bake(Node *p_from_node, String p_occluder_path = "");
+ BakeError bake_scene(Node *p_from_node, String p_occluder_path = "");
+ static void bake_single_node(const Node3D *p_node, float p_simplification_distance, PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
OccluderInstance3D();
~OccluderInstance3D();
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index b71c54dcf9..b251aa38ba 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -259,6 +259,13 @@ void RayCast3D::remove_exception(const CollisionObject3D *p_node) {
void RayCast3D::clear_exceptions() {
exclude.clear();
+
+ if (exclude_parent_body && is_inside_tree()) {
+ CollisionObject3D *parent = Object::cast_to<CollisionObject3D>(get_parent());
+ if (parent) {
+ exclude.insert(parent->get_rid());
+ }
+ }
}
void RayCast3D::set_collide_with_areas(bool p_enabled) {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 3957a1653f..b6b5920f69 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -506,7 +506,7 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
// Skeleton creation api
void Skeleton3D::add_bone(const String &p_name) {
- ERR_FAIL_COND(p_name.is_empty() || p_name.find(":") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.is_empty() || p_name.contains(":") || p_name.contains("/"));
for (int i = 0; i < bones.size(); i++) {
ERR_FAIL_COND(bones[i].name == p_name);
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index a054f35d2e..66d1b97056 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -482,22 +482,22 @@ void XRController3D::_unbind_tracker() {
void XRController3D::_button_pressed(const String &p_name) {
// just pass it on...
- emit_signal("button_pressed", p_name);
+ emit_signal(SNAME("button_pressed"), p_name);
}
void XRController3D::_button_released(const String &p_name) {
// just pass it on...
- emit_signal("button_released", p_name);
+ emit_signal(SNAME("button_released"), p_name);
}
void XRController3D::_input_value_changed(const String &p_name, float p_value) {
// just pass it on...
- emit_signal("input_value_changed", p_name, p_value);
+ emit_signal(SNAME("input_value_changed"), p_name, p_value);
}
void XRController3D::_input_axis_changed(const String &p_name, Vector2 p_value) {
// just pass it on...
- emit_signal("input_axis_changed", p_name, p_value);
+ emit_signal(SNAME("input_axis_changed"), p_name, p_value);
}
bool XRController3D::is_button_pressed(const StringName &p_name) const {
diff --git a/scene/SCsub b/scene/SCsub
index 92288211bb..a7b23af598 100644
--- a/scene/SCsub
+++ b/scene/SCsub
@@ -9,6 +9,7 @@ env.add_source_files(env.scene_sources, "*.cpp")
# Chain load SCsubs
SConscript("main/SCsub")
+SConscript("multiplayer/SCsub")
SConscript("gui/SCsub")
if not env["disable_3d"]:
SConscript("3d/SCsub")
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index 3b55dd4a42..3d0ac291b8 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -857,7 +857,7 @@ void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNod
ERR_FAIL_COND(nodes.has(p_name));
ERR_FAIL_COND(p_node.is_null());
ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output);
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
Node n;
n.node = p_node;
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index a23e1b689c..a68f7e037d 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -50,7 +50,7 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const {
void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
String cs = p_condition;
- ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1);
+ ERR_FAIL_COND(cs.contains("/") || cs.contains(":"));
advance_condition = p_condition;
if (!cs.is_empty()) {
advance_condition_name = "conditions/" + cs;
@@ -536,7 +536,7 @@ Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName
void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) {
ERR_FAIL_COND(states.has(p_name));
ERR_FAIL_COND(p_node.is_null());
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
State state;
state.node = p_node;
@@ -553,7 +553,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
void AnimationNodeStateMachine::replace_node(const StringName &p_name, Ref<AnimationNode> p_node) {
ERR_FAIL_COND(states.has(p_name) == false);
ERR_FAIL_COND(p_node.is_null());
- ERR_FAIL_COND(String(p_name).find("/") != -1);
+ ERR_FAIL_COND(String(p_name).contains("/"));
{
Ref<AnimationNode> node = states[p_name].node;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 128c6cae8b..4a3fd72080 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1171,7 +1171,7 @@ void AnimationPlayer::_animation_process(double p_delta) {
Error AnimationPlayer::add_animation(const StringName &p_name, const Ref<Animation> &p_animation) {
#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(String(p_name).find("/") != -1 || String(p_name).find(":") != -1 || String(p_name).find(",") != -1 || String(p_name).find("[") != -1, ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
+ ERR_FAIL_COND_V_MSG(String(p_name).contains("/") || String(p_name).contains(":") || String(p_name).contains(",") || String(p_name).contains("["), ERR_INVALID_PARAMETER, "Invalid animation name: " + String(p_name) + ".");
#endif
ERR_FAIL_COND_V(p_animation.is_null(), ERR_INVALID_PARAMETER);
@@ -1213,7 +1213,7 @@ void AnimationPlayer::_unref_anim(const Ref<Animation> &p_anim) {
void AnimationPlayer::rename_animation(const StringName &p_name, const StringName &p_new_name) {
ERR_FAIL_COND(!animation_set.has(p_name));
- ERR_FAIL_COND(String(p_new_name).find("/") != -1 || String(p_new_name).find(":") != -1);
+ ERR_FAIL_COND(String(p_new_name).contains("/") || String(p_new_name).contains(":"));
ERR_FAIL_COND(animation_set.has(p_new_name));
stop();
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index d153b882f9..f2f69fc439 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -314,7 +314,7 @@ void AnimationNode::add_input(const String &p_name) {
//root nodes can't add inputs
ERR_FAIL_COND(Object::cast_to<AnimationRootNode>(this) != nullptr);
Input input;
- ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
input.name = p_name;
inputs.push_back(input);
emit_changed();
@@ -322,7 +322,7 @@ void AnimationNode::add_input(const String &p_name) {
void AnimationNode::set_input_name(int p_input, const String &p_name) {
ERR_FAIL_INDEX(p_input, inputs.size());
- ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1);
+ ERR_FAIL_COND(p_name.contains(".") || p_name.contains("/"));
inputs.write[p_input].name = p_name;
emit_changed();
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 22def607ed..8cb8a78e8d 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -34,14 +34,6 @@
#include "core/string/string_builder.h"
#include "core/string/ustring.h"
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return !is_symbol(c);
-}
-
void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
@@ -232,7 +224,7 @@ void CodeEdit::_notification(int p_what) {
int begin = 0;
int end = 0;
- if (line.find(String::chr(0xFFFF)) != -1) {
+ if (line.contains(String::chr(0xFFFF))) {
begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x;
end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x;
}
@@ -607,9 +599,9 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
- if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
insert_text_at_caret(chr);
- } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
+ } else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
insert_text_at_caret(chr);
} else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
@@ -1001,7 +993,7 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
}
/* Make sure this is the last char, trailing whitespace or comments are okay. */
- if (should_indent && (!_is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
+ if (should_indent && (!is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
should_indent = false;
}
}
@@ -1817,7 +1809,7 @@ void CodeEdit::request_code_completion(bool p_force) {
String line = get_line(get_caret_line());
int ofs = CLAMP(get_caret_column(), 0, line.length());
- if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
+ if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || !is_symbol(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
emit_signal(SNAME("code_completion_requested"));
} else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
emit_signal(SNAME("code_completion_requested"));
@@ -1926,7 +1918,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
if (merge_text) {
for (; caret_col < line.length(); caret_col++) {
- if (!_is_char(line[caret_col])) {
+ if (is_symbol(line[caret_col])) {
break;
}
}
@@ -2562,7 +2554,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
region = E->value();
in_region = true;
for (int i = E->key() - 2; i >= 0; i--) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2581,7 +2573,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
}
for (int i = end_col; i < line.length(); i++) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2797,11 +2789,11 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
while (ofs > 0 && line[ofs] == ' ') {
ofs--;
}
- prev_is_word = _is_char(line[ofs]);
+ prev_is_word = !is_symbol(line[ofs]);
/* Otherwise get current word and set cofs to the start. */
} else {
int start_cofs = cofs;
- while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || _is_char(line[cofs - 1]))) {
+ while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || !is_symbol(line[cofs - 1]))) {
cofs--;
}
string_to_complete = line.substr(cofs, start_cofs - cofs);
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index a985a9d031..94fa5d81d8 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -136,6 +136,9 @@ int MenuButton::get_item_count() const {
void MenuButton::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ } break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
popup->hide();
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 9984ab240a..ad2434cd8b 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -92,7 +92,10 @@ void OptionButton::_notification(int p_what) {
arrow->draw(ci, ofs, clr);
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ [[fallthrough]];
+ }
case NOTIFICATION_THEME_CHANGED: {
if (has_theme_icon(SNAME("arrow"))) {
if (is_layout_rtl()) {
@@ -115,6 +118,11 @@ void OptionButton::_notification(int p_what) {
bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
bool valid;
popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
@@ -133,6 +141,11 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
bool valid;
r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
return valid;
@@ -148,14 +161,6 @@ void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
- pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
- p_list->push_back(pi);
-
- pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i));
- pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
- p_list->push_back(pi);
-
pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
@@ -183,10 +188,13 @@ void OptionButton::pressed() {
popup->set_size(Size2(size.width, 0));
// If not triggered by the mouse, start the popup with the checked item selected.
- if (popup->get_item_count() > 0 &&
- ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
- (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) {
- popup->set_current_index(current > -1 ? current : 0);
+ if (popup->get_item_count() > 0) {
+ if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) {
+ popup->set_current_index(current > -1 ? current : 0);
+ } else {
+ popup->scroll_to_item(current > -1 ? current : 0);
+ }
}
popup->popup();
@@ -264,7 +272,20 @@ bool OptionButton::is_item_disabled(int p_idx) const {
void OptionButton::set_item_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
+
+ int count_old = get_item_count();
+ if (p_count == count_old) {
+ return;
+ }
+
popup->set_item_count(p_count);
+
+ if (p_count > count_old) {
+ for (int i = count_old; i < p_count; i++) {
+ popup->set_item_as_radio_checkable(i, true);
+ }
+ }
+
notify_property_list_changed();
}
@@ -294,7 +315,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
current = NONE_SELECTED;
set_text("");
- set_icon(NULL);
+ set_icon(nullptr);
} else {
ERR_FAIL_INDEX(p_which, popup->get_item_count());
diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp
index 86858fdc78..c88e4ae2f2 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -30,35 +30,14 @@
#include "panel.h"
-#include "core/string/print_string.h"
-
void Panel::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
- Ref<StyleBox> style = mode == MODE_BACKGROUND ? get_theme_stylebox(SNAME("panel")) : get_theme_stylebox(SNAME("panel_fg"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
style->draw(ci, Rect2(Point2(), get_size()));
}
}
-void Panel::set_mode(Mode p_mode) {
- mode = p_mode;
- update();
-}
-
-Panel::Mode Panel::get_mode() const {
- return mode;
-}
-
-void Panel::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Panel::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &Panel::get_mode);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Background,Foreground"), "set_mode", "get_mode");
-
- BIND_ENUM_CONSTANT(MODE_BACKGROUND);
- BIND_ENUM_CONSTANT(MODE_FOREGROUND);
-}
-
Panel::Panel() {
// Has visible stylebox, so stop by default.
set_mouse_filter(MOUSE_FILTER_STOP);
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index 37f14c250c..5d2e912680 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -36,26 +36,11 @@
class Panel : public Control {
GDCLASS(Panel, Control);
-public:
- enum Mode {
- MODE_BACKGROUND,
- MODE_FOREGROUND,
- };
-
-private:
- Mode mode = MODE_BACKGROUND;
-
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
- void set_mode(Mode p_mode);
- Mode get_mode() const;
-
Panel();
};
-VARIANT_ENUM_CAST(Panel::Mode)
-
#endif // PANEL_H
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 812339dc19..61a5fb999c 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -67,7 +67,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
has_check = true;
}
@@ -108,10 +108,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int PopupMenu::_get_item_height(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), 0);
- ERR_FAIL_COND_V(p_item < 0, 0);
int icon_height = items[p_item].get_icon_size().height;
- if (items[p_item].checkable_type) {
+ if (items[p_item].checkable_type && !items[p_item].separator) {
icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height()));
}
@@ -141,23 +140,6 @@ int PopupMenu::_get_items_total_height() const {
return items_total_height - vsep;
}
-void PopupMenu::_scroll_to_item(int p_item) {
- ERR_FAIL_INDEX(p_item, items.size());
- ERR_FAIL_COND(p_item < 0);
-
- // Scroll item into view (upwards)
- if (items[p_item]._ofs_cache < -control->get_position().y) {
- int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-
- // Scroll item into view (downwards)
- if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
- int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-}
-
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
if (p_over.x < 0 || p_over.x >= get_size().width) {
return -1;
@@ -203,15 +185,17 @@ void PopupMenu::_activate_submenu(int p_over) {
float scroll_offset = control->get_position().y;
- Point2 submenu_pos;
+ submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
Size2 submenu_size = submenu_popup->get_size();
+
+ Point2 submenu_pos;
if (control->is_layout_rtl()) {
submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset);
} else {
submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset);
}
- // Fix pos if going outside parent rect
+ // Fix pos if going outside parent rect.
if (submenu_pos.x < get_parent_rect().position.x) {
submenu_pos.x = this_pos.x + submenu_size.width;
}
@@ -222,7 +206,6 @@ void PopupMenu::_activate_submenu(int p_over) {
submenu_popup->set_close_on_parent_focus(false);
submenu_popup->set_position(submenu_pos);
- submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
if (!submenu_pum) {
@@ -275,7 +258,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -289,7 +272,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -307,7 +290,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -321,7 +304,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -471,7 +454,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (items[i].text.findn(search_string) == 0) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -495,7 +478,7 @@ void PopupMenu::_draw_items() {
bool rtl = control->is_layout_rtl();
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover"));
- // In Item::checkable_type enum order (less the non-checkable member)
+ // In Item::checkable_type enum order (less the non-checkable member).
Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) };
Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) };
Ref<Texture2D> submenu;
@@ -524,6 +507,10 @@ void PopupMenu::_draw_items() {
float icon_ofs = 0.0;
bool has_check = false;
for (int i = 0; i < items.size(); i++) {
+ if (items[i].separator) {
+ continue;
+ }
+
icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
if (items[i].checkable_type) {
@@ -567,29 +554,33 @@ void PopupMenu::_draw_items() {
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
int sep_ofs = Math::floor((h - sep_h) / 2.0);
- if (!text.is_empty()) {
- int text_size = items[i].text_buf->get_size().width;
- int text_center = display_width / 2;
- int text_left = text_center - text_size / 2;
- int text_right = text_center + text_size / 2;
- if (text_left > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
+ if (!text.is_empty() || !items[i].icon.is_null()) {
+ int content_size = items[i].text_buf->get_size().width;
+ if (!items[i].icon.is_null()) {
+ content_size += icon_size.width + hseparation;
}
- if (text_right < display_width) {
- labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h)));
+
+ int content_center = display_width / 2;
+ int content_left = content_center - content_size / 2;
+ int content_right = content_center + content_size / 2;
+ if (content_left > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h)));
+ }
+ if (content_right < display_width) {
+ labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h)));
}
} else {
separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h)));
}
}
- Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1);
// For non-separator items, add some padding for the content.
item_ofs.x += item_start_padding;
// Checkboxes
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
if (rtl) {
icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
@@ -598,16 +589,28 @@ void PopupMenu::_draw_items() {
}
}
+ int separator_ofs = (display_width - items[i].text_buf->get_size().width) / 2;
+
// Icon
if (!items[i].icon.is_null()) {
- if (rtl) {
- items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (items[i].separator) {
+ separator_ofs -= (icon_size.width + hseparation) / 2;
+
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
} else {
- items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
}
}
- // Submenu arrow on right hand side
+ // Submenu arrow on right hand side.
if (!items[i].submenu.is_empty()) {
if (rtl) {
submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
@@ -621,8 +624,11 @@ void PopupMenu::_draw_items() {
int outline_size = get_theme_constant(SNAME("outline_size"));
if (items[i].separator) {
if (!text.is_empty()) {
- int center = (display_width - items[i].text_buf->get_size().width) / 2;
- Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (!rtl && !items[i].icon.is_null()) {
+ text_pos.x += icon_size.width + hseparation;
+ }
+
if (outline_size > 0 && font_outline_color.a > 0) {
items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
}
@@ -659,7 +665,7 @@ void PopupMenu::_draw_items() {
items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color);
}
- // Cache the item vertical offset from the first item and the height
+ // Cache the item vertical offset from the first item and the height.
items.write[i]._ofs_cache = ofs.y;
items.write[i]._height_cache = h;
@@ -724,7 +730,7 @@ void PopupMenu::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
if (pm) {
- // Inherit submenu's popup delay time from parent menu
+ // Inherit submenu's popup delay time from parent menu.
float pm_delay = pm->get_submenu_popup_delay();
set_submenu_popup_delay(pm_delay);
}
@@ -819,7 +825,7 @@ void PopupMenu::_notification(int p_what) {
#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \
item.text = p_label; \
item.xl_text = atr(p_label); \
- item.id = p_id == -1 ? items.size() - 1 : p_id; \
+ item.id = p_id == -1 ? items.size() : p_id; \
item.accel = p_accel;
void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
@@ -901,7 +907,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = atr(item.text); \
- item.id = p_id == -1 ? items.size() - 1 : p_id; \
+ item.id = p_id == -1 ? items.size() : p_id; \
item.shortcut = p_shortcut; \
item.shortcut_is_global = p_global;
@@ -970,7 +976,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
Item item;
item.text = p_label;
item.xl_text = atr(p_label);
- item.id = p_id == -1 ? items.size() - 1 : p_id;
+ item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu;
items.push_back(item);
_shape_item(items.size() - 1);
@@ -1281,7 +1287,7 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
void PopupMenu::set_current_index(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
mouse_over = p_idx;
- _scroll_to_item(mouse_over);
+ scroll_to_item(mouse_over);
control->update();
}
@@ -1309,6 +1315,20 @@ int PopupMenu::get_item_count() const {
return items.size();
}
+void PopupMenu::scroll_to_item(int p_item) {
+ ERR_FAIL_INDEX(p_item, items.size());
+
+ // Scroll item into view (upwards).
+ if (items[p_item]._ofs_cache - scroll_container->get_v_scroll() < -control->get_position().y) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + control->get_position().y);
+ }
+
+ // Scroll item into view (downwards).
+ if (items[p_item]._ofs_cache + items[p_item]._height_cache - scroll_container->get_v_scroll() > -control->get_position().y + scroll_container->get_size().height) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y);
+ }
+}
+
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
Key code = Key::NONE;
Ref<InputEventKey> k = p_event;
@@ -1582,7 +1602,7 @@ bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
} else if (property == "id") {
set_item_id(item_index, p_value);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
set_item_disabled(item_index, p_value);
return true;
} else if (property == "separator") {
@@ -1655,7 +1675,7 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
} else if (property == "id") {
r_ret = get_item_id(item_index);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
r_ret = is_item_disabled(item_index);
return true;
} else if (property == "separator") {
@@ -1761,6 +1781,8 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "index"), &PopupMenu::scroll_to_item);
+
ClassDB::bind_method(D_METHOD("remove_item", "index"), &PopupMenu::remove_item);
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 7c2212d82d..5ce55209d4 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -103,7 +103,6 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const;
int _get_items_total_height() const;
- void _scroll_to_item(int p_item);
void _shape_item(int p_item);
@@ -218,6 +217,8 @@ public:
void set_item_count(int p_count);
int get_item_count() const;
+ void scroll_to_item(int p_item);
+
bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);
void activate_item(int p_item);
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 5a551ec5a5..b1baacd887 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -761,6 +761,8 @@ void TabBar::_update_hover() {
return;
}
+ ERR_FAIL_COND(tabs.is_empty());
+
const Point2 &pos = get_local_mouse_position();
// Test hovering to display right or close button.
int hover_now = -1;
@@ -1436,7 +1438,6 @@ void TabBar::_get_property_list(List<PropertyInfo> *p_list) const {
}
void TabBar::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_update_hover"), &TabBar::_update_hover);
ClassDB::bind_method(D_METHOD("set_tab_count", "count"), &TabBar::set_tab_count);
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabBar::set_current_tab);
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index c3fc08731e..818431a6a0 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -704,7 +704,7 @@ void TabContainer::add_child_notify(Node *p_child) {
}
_refresh_texts();
- call_deferred("_repaint");
+ call_deferred(SNAME("_repaint"));
update();
bool first = (_get_tabs().size() == 1);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 0ee4a6af4e..bb259843b8 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -43,18 +43,6 @@
#include "scene/main/window.h"
-static bool _is_text_char(char32_t c) {
- return !is_symbol(c);
-}
-
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
///////////////////////////////////////////////////////////////////////////////
/// TEXT ///
///////////////////////////////////////////////////////////////////////////////
@@ -820,8 +808,8 @@ void TextEdit::_notification(int p_what) {
int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs;
bool out_of_bounds = (xpos >= xmargin_end + minimap_width);
- bool is_whitespace = _is_whitespace(str[j]);
- if (!is_whitespace) {
+ bool whitespace = is_whitespace(str[j]);
+ if (!whitespace) {
characters++;
if (j < str.length() - 1 && color == previous_color && !out_of_bounds) {
@@ -843,7 +831,7 @@ void TextEdit::_notification(int p_what) {
if (characters > 0) {
previous_color.a *= 0.6;
// take one for zero indexing, and if we hit whitespace / the end of a word.
- int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1;
+ int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1;
int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
@@ -1144,7 +1132,7 @@ void TextEdit::_notification(int p_what) {
}
if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word
- if (_is_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') {
+ if (is_ascii_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') {
int highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
while (highlighted_word_col != -1) {
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + lookup_symbol_word.length() + start);
@@ -3037,7 +3025,7 @@ int TextEdit::get_first_non_whitespace_column(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
int col = 0;
- while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) {
+ while (col < text[p_line].length() && is_whitespace(text[p_line][col])) {
col++;
}
return col;
@@ -3622,9 +3610,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
// Validate for whole words.
- if (pos > 0 && _is_text_char(text_line[pos - 1])) {
+ if (pos > 0 && !is_symbol(text_line[pos - 1])) {
is_match = false;
- } else if (pos + p_key.length() < text_line.length() && _is_text_char(text_line[pos + p_key.length()])) {
+ } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
is_match = false;
}
}
@@ -5779,9 +5767,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
- if (col > 0 && _is_text_char(p_search[col - 1])) {
+ if (col > 0 && !is_symbol(p_search[col - 1])) {
col = -1;
- } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) {
+ } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
col = -1;
}
}
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 89a17ae854..26acfaaa70 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -37,7 +37,7 @@
Size2 TextureButton::get_minimum_size() const {
Size2 rscale = Control::get_minimum_size();
- if (!expand) {
+ if (!ignore_texture_size) {
if (normal.is_null()) {
if (pressed.is_null()) {
if (hover.is_null()) {
@@ -182,50 +182,48 @@ void TextureButton::_notification(int p_what) {
size = texdraw->get_size();
_texture_region = Rect2(Point2(), texdraw->get_size());
_tile = false;
- if (expand) {
- switch (stretch_mode) {
- case STRETCH_KEEP:
- size = texdraw->get_size();
- break;
- case STRETCH_SCALE:
- size = get_size();
- break;
- case STRETCH_TILE:
- size = get_size();
- _tile = true;
- break;
- case STRETCH_KEEP_CENTERED:
- ofs = (get_size() - texdraw->get_size()) / 2;
- size = texdraw->get_size();
- break;
- case STRETCH_KEEP_ASPECT_CENTERED:
- case STRETCH_KEEP_ASPECT: {
- Size2 _size = get_size();
- float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
- float tex_height = _size.height;
-
- if (tex_width > _size.width) {
- tex_width = _size.width;
- tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
- }
+ switch (stretch_mode) {
+ case STRETCH_KEEP:
+ size = texdraw->get_size();
+ break;
+ case STRETCH_SCALE:
+ size = get_size();
+ break;
+ case STRETCH_TILE:
+ size = get_size();
+ _tile = true;
+ break;
+ case STRETCH_KEEP_CENTERED:
+ ofs = (get_size() - texdraw->get_size()) / 2;
+ size = texdraw->get_size();
+ break;
+ case STRETCH_KEEP_ASPECT_CENTERED:
+ case STRETCH_KEEP_ASPECT: {
+ Size2 _size = get_size();
+ float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
+ float tex_height = _size.height;
+
+ if (tex_width > _size.width) {
+ tex_width = _size.width;
+ tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
+ }
- if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- ofs.x = (_size.width - tex_width) / 2;
- ofs.y = (_size.height - tex_height) / 2;
- }
- size.width = tex_width;
- size.height = tex_height;
- } break;
- case STRETCH_KEEP_ASPECT_COVERED: {
- size = get_size();
- Size2 tex_size = texdraw->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
- Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
- _texture_region = Rect2(ofs2, size / scale);
- } break;
- }
+ if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
+ ofs.x = (_size.width - tex_width) / 2;
+ ofs.y = (_size.height - tex_height) / 2;
+ }
+ size.width = tex_width;
+ size.height = tex_height;
+ } break;
+ case STRETCH_KEEP_ASPECT_COVERED: {
+ size = get_size();
+ Size2 tex_size = texdraw->get_size();
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+ Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
+ _texture_region = Rect2(ofs2, size / scale);
+ } break;
}
_position_rect = Rect2(ofs, size);
@@ -258,7 +256,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture);
ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture);
ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
- ClassDB::bind_method(D_METHOD("set_expand", "expand"), &TextureButton::set_expand);
+ ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureButton::set_ignore_texture_size);
ClassDB::bind_method(D_METHOD("set_stretch_mode", "mode"), &TextureButton::set_stretch_mode);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
@@ -271,7 +269,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_disabled_texture"), &TextureButton::get_disabled_texture);
ClassDB::bind_method(D_METHOD("get_focused_texture"), &TextureButton::get_focused_texture);
ClassDB::bind_method(D_METHOD("get_click_mask"), &TextureButton::get_click_mask);
- ClassDB::bind_method(D_METHOD("get_expand"), &TextureButton::get_expand);
+ ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureButton::get_ignore_texture_size);
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode);
ADD_GROUP("Textures", "texture_");
@@ -281,7 +279,7 @@ void TextureButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_disabled_texture", "get_disabled_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_focused_texture", "get_focused_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_ignore_texture_size", "get_ignore_texture_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_v", "is_flipped_v");
@@ -352,12 +350,12 @@ void TextureButton::set_focused_texture(const Ref<Texture2D> &p_focused) {
focused = p_focused;
};
-bool TextureButton::get_expand() const {
- return expand;
+bool TextureButton::get_ignore_texture_size() const {
+ return ignore_texture_size;
}
-void TextureButton::set_expand(bool p_expand) {
- expand = p_expand;
+void TextureButton::set_ignore_texture_size(bool p_ignore) {
+ ignore_texture_size = p_ignore;
update_minimum_size();
update();
}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 1428a79a1d..5762949acd 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -54,8 +54,8 @@ private:
Ref<Texture2D> disabled;
Ref<Texture2D> focused;
Ref<BitMap> click_mask;
- bool expand = false;
- StretchMode stretch_mode = STRETCH_SCALE;
+ bool ignore_texture_size = false;
+ StretchMode stretch_mode = STRETCH_KEEP;
Rect2 _texture_region;
Rect2 _position_rect;
@@ -85,8 +85,8 @@ public:
Ref<Texture2D> get_focused_texture() const;
Ref<BitMap> get_click_mask() const;
- bool get_expand() const;
- void set_expand(bool p_expand);
+ bool get_ignore_texture_size() const;
+ void set_ignore_texture_size(bool p_ignore);
void set_stretch_mode(StretchMode p_stretch_mode);
StretchMode get_stretch_mode() const;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 1b32884880..9a6c87276f 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -202,7 +202,7 @@ void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
bool ch = cells[p_column].checked;
if (p_emit_signal) {
- tree->emit_signal("check_propagated_to_item", this, p_column);
+ tree->emit_signal(SNAME("check_propagated_to_item"), this, p_column);
}
_propagate_check_through_children(p_column, ch, p_emit_signal);
_propagate_check_through_parents(p_column, p_emit_signal);
@@ -213,7 +213,7 @@ void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, b
while (current) {
current->set_checked(p_column, p_checked);
if (p_emit_signal) {
- current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
}
current->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
current = current->get_next();
@@ -252,7 +252,7 @@ void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal
}
if (p_emit_signal) {
- current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
}
current->_propagate_check_through_parents(p_column, p_emit_signal);
}
@@ -2490,7 +2490,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
/* process selection */
if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
-
+ // Emits the "item_activated" signal.
propagate_mouse_activated = true;
incr_search.clear();
@@ -4409,21 +4409,29 @@ Point2 Tree::get_scroll() const {
return ofs;
}
-void Tree::scroll_to_item(TreeItem *p_item) {
+void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
if (!is_visible_in_tree()) {
- // hack to work around crash in get_item_rect() if Tree is not in tree.
- return;
+ return; // Hack to work around crash in get_item_rect() if Tree is not in tree.
}
- // make sure the scrollbar min and max are up to date with latest changes.
update_scrollbars();
- const Rect2 r = get_item_rect(p_item);
+ const real_t tree_height = get_size().y;
+ const Rect2 item_rect = get_item_rect(p_item);
+ const real_t item_y = item_rect.position.y;
+ const real_t item_height = item_rect.size.y + cache.vseparation;
- if (r.position.y <= v_scroll->get_value()) {
- v_scroll->set_value(r.position.y);
- } else if (r.position.y + r.size.y + 2 * cache.vseparation > v_scroll->get_value() + get_size().y) {
- v_scroll->set_value(r.position.y + r.size.y + 2 * cache.vseparation - get_size().y);
+ if (p_center_on_item) {
+ v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f);
+ } else {
+ if (item_y < v_scroll->get_value()) {
+ v_scroll->set_value(item_y);
+ } else {
+ const real_t new_position = item_y + item_height - tree_height;
+ if (new_position > v_scroll->get_value()) {
+ v_scroll->set_value(new_position);
+ }
+ }
}
}
@@ -4868,7 +4876,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
- ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::scroll_to_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item", "center_on_item"), &Tree::scroll_to_item, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c24763a0e4..255a4f0576 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -682,7 +682,7 @@ public:
TreeItem *get_item_with_text(const String &p_find) const;
Point2 get_scroll() const;
- void scroll_to_item(TreeItem *p_item);
+ void scroll_to_item(TreeItem *p_item, bool p_center_on_item = false);
void set_h_scroll_enabled(bool p_enable);
bool is_h_scroll_enabled() const;
void set_v_scroll_enabled(bool p_enable);
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index a0916c6291..b794bbbc57 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -56,34 +56,19 @@ Transform2D CanvasItem::_edit_get_transform() const {
#endif
bool CanvasItem::is_visible_in_tree() const {
- if (!is_inside_tree()) {
- return false;
- }
-
- const CanvasItem *p = this;
-
- while (p) {
- if (!p->visible) {
- return false;
- }
- if (p->window && !p->window->is_visible()) {
- return false;
- }
- p = p->get_parent_item();
- }
-
- return true;
+ return visible && visible_in_tree;
}
-void CanvasItem::_propagate_visibility_changed(bool p_visible) {
+void CanvasItem::_propagate_visibility_changed(bool p_visible, bool p_was_visible) {
if (p_visible && first_draw) { //avoid propagating it twice
first_draw = false;
}
+ visible_in_tree = p_visible;
notification(NOTIFICATION_VISIBILITY_CHANGED);
- if (p_visible) {
- update(); //todo optimize
- } else {
+ if (visible && p_visible) {
+ update();
+ } else if (!p_visible && (visible || p_was_visible)) {
emit_signal(SceneStringNames::get_singleton()->hidden);
}
_block();
@@ -111,7 +96,7 @@ void CanvasItem::set_visible(bool p_visible) {
return;
}
- _propagate_visibility_changed(p_visible);
+ _propagate_visibility_changed(p_visible, !p_visible);
}
void CanvasItem::show() {
@@ -139,7 +124,7 @@ void CanvasItem::_update_callback() {
RenderingServer::get_singleton()->canvas_item_clear(get_canvas_item());
//todo updating = true - only allow drawing here
- if (is_visible_in_tree()) { //todo optimize this!!
+ if (is_visible_in_tree()) {
if (first_draw) {
notification(NOTIFICATION_VISIBILITY_CHANGED);
first_draw = false;
@@ -273,32 +258,44 @@ void CanvasItem::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
ERR_FAIL_COND(!is_inside_tree());
first_draw = true;
+
Node *parent = get_parent();
if (parent) {
CanvasItem *ci = Object::cast_to<CanvasItem>(parent);
+
if (ci) {
+ visible_in_tree = ci->is_visible_in_tree();
C = ci->children_items.push_back(this);
- }
- if (!ci) {
- //look for a window
- Viewport *viewport = nullptr;
-
- while (parent) {
- viewport = Object::cast_to<Viewport>(parent);
- if (viewport) {
- break;
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(parent);
+
+ if (cl) {
+ visible_in_tree = cl->is_visible();
+ } else {
+ // Look for a window.
+ Viewport *viewport = nullptr;
+
+ while (parent) {
+ viewport = Object::cast_to<Viewport>(parent);
+ if (viewport) {
+ break;
+ }
+ parent = parent->get_parent();
}
- parent = parent->get_parent();
- }
- ERR_FAIL_COND(!viewport);
+ ERR_FAIL_COND(!viewport);
- window = Object::cast_to<Window>(viewport);
- if (window) {
- window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
+ window = Object::cast_to<Window>(viewport);
+ if (window) {
+ window->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
+ visible_in_tree = window->is_visible();
+ } else {
+ visible_in_tree = true;
+ }
}
}
}
+
_enter_canvas();
_update_texture_filter_changed(false);
@@ -335,6 +332,7 @@ void CanvasItem::_notification(int p_what) {
window->disconnect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &CanvasItem::_window_visibility_changed));
}
global_invalid = true;
+ visible_in_tree = false;
} break;
case NOTIFICATION_DRAW:
case NOTIFICATION_TRANSFORM_CHANGED: {
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 3d49d89746..a368a10ad0 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -46,6 +46,8 @@ class World2D;
class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
+ friend class CanvasLayer;
+
public:
enum TextureFilter {
TEXTURE_FILTER_PARENT_NODE,
@@ -85,6 +87,7 @@ private:
Window *window = nullptr;
bool first_draw = false;
bool visible = true;
+ bool visible_in_tree = false;
bool clip_children = false;
bool pending_update = false;
bool top_level = false;
@@ -107,7 +110,7 @@ private:
void _top_level_raise_self();
- void _propagate_visibility_changed(bool p_visible);
+ void _propagate_visibility_changed(bool p_visible, bool p_was_visible = false);
void _update_callback();
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 282ab6b497..3f3e72357b 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "canvas_layer.h"
+#include "canvas_item.h"
#include "viewport.h"
void CanvasLayer::set_layer(int p_xform) {
@@ -42,6 +43,32 @@ int CanvasLayer::get_layer() const {
return layer;
}
+void CanvasLayer::set_visible(bool p_visible) {
+ if (p_visible == visible) {
+ return;
+ }
+
+ visible = p_visible;
+ emit_signal(SNAME("visibility_changed"));
+
+ for (int i = 0; i < get_child_count(); i++) {
+ CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
+ if (c) {
+ RenderingServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible());
+
+ if (c->is_visible()) {
+ c->_propagate_visibility_changed(p_visible);
+ } else {
+ c->notification(CanvasItem::NOTIFICATION_VISIBILITY_CHANGED);
+ }
+ }
+ }
+}
+
+bool CanvasLayer::is_visible() const {
+ return visible;
+}
+
void CanvasLayer::set_transform(const Transform2D &p_xform) {
transform = p_xform;
locrotscale_dirty = true;
@@ -264,6 +291,9 @@ void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer);
+ ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasLayer::set_visible);
+ ClassDB::bind_method(D_METHOD("is_visible"), &CanvasLayer::is_visible);
+
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform);
@@ -289,6 +319,7 @@ void CanvasLayer::_bind_methods() {
ADD_GROUP("Layer", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
@@ -299,6 +330,8 @@ void CanvasLayer::_bind_methods() {
ADD_GROUP("Follow Viewport", "follow_viewport");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enable"), "set_follow_viewport", "is_following_viewport");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_lesser"), "set_follow_viewport_scale", "get_follow_viewport_scale");
+
+ ADD_SIGNAL(MethodInfo("visibility_changed"));
}
CanvasLayer::CanvasLayer() {
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index 93a0152787..b7bd793440 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -52,6 +52,7 @@ class CanvasLayer : public Node {
Viewport *vp = nullptr;
int sort_index = 0;
+ bool visible = true;
bool follow_viewport = false;
float follow_viewport_scale = 1.0;
@@ -69,6 +70,9 @@ public:
void set_layer(int p_xform);
int get_layer() const;
+ void set_visible(bool p_visible);
+ bool is_visible() const;
+
void set_transform(const Transform2D &p_xform);
Transform2D get_transform() const;
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index a2415442f8..6b9d8ab211 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -32,6 +32,7 @@
#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
+#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/string/print_string.h"
#include "instance_placeholder.h"
@@ -110,9 +111,6 @@ void Node::_notification(int p_notification) {
memdelete(data.path_cache);
data.path_cache = nullptr;
}
- if (data.scene_file_path.length()) {
- get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, false);
- }
} break;
case NOTIFICATION_PATH_RENAMED: {
if (data.path_cache) {
@@ -141,12 +139,6 @@ void Node::_notification(int p_notification) {
}
GDVIRTUAL_CALL(_ready);
-
- if (data.scene_file_path.length()) {
- ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, true);
- }
-
} break;
case NOTIFICATION_POSTINITIALIZE: {
data.in_constructor = false;
@@ -211,6 +203,12 @@ void Node::_propagate_enter_tree() {
data.tree->node_added(this);
+ if (data.parent) {
+ Variant c = this;
+ const Variant *cptr = &c;
+ data.parent->emit_signal(SNAME("child_entered_tree"), &cptr, 1);
+ }
+
data.blocked++;
//block while adding children
@@ -281,6 +279,12 @@ void Node::_propagate_exit_tree() {
data.tree->node_removed(this);
}
+ if (data.parent) {
+ Variant c = this;
+ const Variant *cptr = &c;
+ data.parent->emit_signal(SNAME("child_exited_tree"), &cptr, 1);
+ }
+
// exit groups
for (KeyValue<StringName, GroupData> &E : data.grouped) {
@@ -1049,7 +1053,7 @@ void Node::_generate_serial_child_name(const Node *p_child, StringName &name) co
String nums;
for (int i = name_string.length() - 1; i >= 0; i--) {
char32_t n = name_string[i];
- if (n >= '0' && n <= '9') {
+ if (is_digit(n)) {
nums = String::chr(name_string[i]) + nums;
} else {
break;
@@ -2865,6 +2869,8 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_entered"));
ADD_SIGNAL(MethodInfo("tree_exiting"));
ADD_SIGNAL(MethodInfo("tree_exited"));
+ ADD_SIGNAL(MethodInfo("child_entered_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
+ ADD_SIGNAL(MethodInfo("child_exited_tree", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT, "Node")));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
diff --git a/scene/main/node.h b/scene/main/node.h
index a1fc672a15..0ac10f4381 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -212,7 +212,6 @@ protected:
static String _get_name_num_separator();
friend class SceneState;
- friend class MultiplayerReplicator;
void _add_child_nocheck(Node *p_child, const StringName &p_name);
void _set_owner_nocheck(Node *p_owner);
@@ -467,7 +466,7 @@ public:
bool is_displayed_folded() const;
/* NETWORK */
- void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
+ virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 45f04b28b9..69d781cbfc 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -36,6 +36,7 @@
#include "core/io/dir_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_loader.h"
+#include "core/multiplayer/multiplayer_api.h"
#include "core/object/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -1164,7 +1165,7 @@ void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
ERR_FAIL_COND(!p_multiplayer.is_valid());
multiplayer = p_multiplayer;
- multiplayer->set_root_node(root);
+ multiplayer->set_root_path("/" + root->get_name());
}
void SceneTree::_bind_methods() {
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 1dff1dab4f..a5cd52b4ca 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -31,7 +31,6 @@
#ifndef SCENE_TREE_H
#define SCENE_TREE_H
-#include "core/multiplayer/multiplayer_api.h"
#include "core/os/main_loop.h"
#include "core/os/thread_safe.h"
#include "core/templates/self_list.h"
@@ -46,6 +45,7 @@ class Node;
class Window;
class Material;
class Mesh;
+class MultiplayerAPI;
class SceneDebugger;
class Tween;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 09880ad6cf..522997cdf5 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1231,7 +1231,7 @@ void Viewport::_gui_show_tooltip() {
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- panel->set_transient(false);
+ panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
panel->set_wrap_controls(true);
panel->add_child(base_tooltip);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index fbc0bc5301..f2ebe50fa3 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1614,6 +1614,7 @@ void Window::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_MINIMIZED);
BIND_ENUM_CONSTANT(MODE_MAXIMIZED);
BIND_ENUM_CONSTANT(MODE_FULLSCREEN);
+ BIND_ENUM_CONSTANT(MODE_EXCLUSIVE_FULLSCREEN);
BIND_ENUM_CONSTANT(FLAG_RESIZE_DISABLED);
BIND_ENUM_CONSTANT(FLAG_BORDERLESS);
diff --git a/scene/main/window.h b/scene/main/window.h
index 2dd1dd6601..f37689f905 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -46,6 +46,7 @@ public:
MODE_MINIMIZED = DisplayServer::WINDOW_MODE_MINIMIZED,
MODE_MAXIMIZED = DisplayServer::WINDOW_MODE_MAXIMIZED,
MODE_FULLSCREEN = DisplayServer::WINDOW_MODE_FULLSCREEN,
+ MODE_EXCLUSIVE_FULLSCREEN = DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
};
enum Flags {
diff --git a/scene/multiplayer/SCsub b/scene/multiplayer/SCsub
new file mode 100644
index 0000000000..fc61250247
--- /dev/null
+++ b/scene/multiplayer/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.scene_sources, "*.cpp")
diff --git a/scene/multiplayer/multiplayer_spawner.cpp b/scene/multiplayer/multiplayer_spawner.cpp
new file mode 100644
index 0000000000..4f2a9d9e83
--- /dev/null
+++ b/scene/multiplayer/multiplayer_spawner.cpp
@@ -0,0 +1,227 @@
+/*************************************************************************/
+/* multiplayer_spawner.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_spawner.h"
+
+#include "core/io/marshalls.h"
+#include "core/multiplayer/multiplayer_api.h"
+#include "scene/main/window.h"
+#include "scene/scene_string_names.h"
+
+void MultiplayerSpawner::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("spawn", "data"), &MultiplayerSpawner::spawn, DEFVAL(Variant()));
+
+ ClassDB::bind_method(D_METHOD("get_spawnable_scenes"), &MultiplayerSpawner::get_spawnable_scenes);
+ ClassDB::bind_method(D_METHOD("set_spawnable_scenes", "scenes"), &MultiplayerSpawner::set_spawnable_scenes);
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "replication", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_spawnable_scenes", "get_spawnable_scenes");
+
+ ClassDB::bind_method(D_METHOD("get_spawn_path"), &MultiplayerSpawner::get_spawn_path);
+ ClassDB::bind_method(D_METHOD("set_spawn_path", "path"), &MultiplayerSpawner::set_spawn_path);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "spawn_path", PROPERTY_HINT_NONE, ""), "set_spawn_path", "get_spawn_path");
+
+ ClassDB::bind_method(D_METHOD("get_spawn_limit"), &MultiplayerSpawner::get_spawn_limit);
+ ClassDB::bind_method(D_METHOD("set_spawn_limit", "limit"), &MultiplayerSpawner::set_spawn_limit);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "spawn_limit", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_spawn_limit", "get_spawn_limit");
+
+ ClassDB::bind_method(D_METHOD("set_auto_spawning", "enabled"), &MultiplayerSpawner::set_auto_spawning);
+ ClassDB::bind_method(D_METHOD("is_auto_spawning"), &MultiplayerSpawner::is_auto_spawning);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_spawn"), "set_auto_spawning", "is_auto_spawning");
+
+ GDVIRTUAL_BIND(_spawn_custom, "data");
+
+ ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+ ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
+}
+
+void MultiplayerSpawner::_update_spawn_node() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ if (spawn_node.is_valid()) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(spawn_node));
+ if (node && node->is_connected("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added))) {
+ node->disconnect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
+ }
+ Node *node = spawn_path.is_empty() && is_inside_tree() ? nullptr : get_node_or_null(spawn_path);
+ if (node) {
+ spawn_node = node->get_instance_id();
+ if (auto_spawn) {
+ node->connect("child_entered_tree", callable_mp(this, &MultiplayerSpawner::_node_added));
+ }
+ } else {
+ spawn_node = ObjectID();
+ }
+}
+
+void MultiplayerSpawner::_notification(int p_what) {
+ if (p_what == NOTIFICATION_POST_ENTER_TREE) {
+ _update_spawn_node();
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ _update_spawn_node();
+ const ObjectID *oid = nullptr;
+ while ((oid = tracked_nodes.next(oid))) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(*oid));
+ ERR_CONTINUE(!node);
+ node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit));
+ // This is unlikely, but might still crash the engine.
+ if (node->is_connected(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready))) {
+ node->disconnect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready));
+ }
+ get_multiplayer()->despawn(node, this);
+ }
+ tracked_nodes.clear();
+ }
+}
+
+void MultiplayerSpawner::_node_added(Node *p_node) {
+ if (!get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority()) {
+ return;
+ }
+ if (tracked_nodes.has(p_node->get_instance_id())) {
+ return;
+ }
+ const Node *parent = get_spawn_node();
+ if (!parent || p_node->get_parent() != parent) {
+ return;
+ }
+ int id = get_scene_id(p_node->get_scene_file_path());
+ if (id == INVALID_ID) {
+ return;
+ }
+ const String name = p_node->get_name();
+ ERR_FAIL_COND_MSG(name.validate_node_name() != name, vformat("Unable to auto-spawn node with reserved name: %s. Make sure to add your replicated scenes via 'add_child(node, true)' to produce valid names.", name));
+ _track(p_node, Variant(), id);
+}
+
+void MultiplayerSpawner::set_auto_spawning(bool p_enabled) {
+ auto_spawn = p_enabled;
+ _update_spawn_node();
+}
+
+bool MultiplayerSpawner::is_auto_spawning() const {
+ return auto_spawn;
+}
+
+TypedArray<PackedScene> MultiplayerSpawner::get_spawnable_scenes() {
+ return spawnable_scenes;
+}
+
+void MultiplayerSpawner::set_spawnable_scenes(TypedArray<PackedScene> p_scenes) {
+ spawnable_scenes = p_scenes;
+}
+
+NodePath MultiplayerSpawner::get_spawn_path() const {
+ return spawn_path;
+}
+
+void MultiplayerSpawner::set_spawn_path(const NodePath &p_path) {
+ spawn_path = p_path;
+ _update_spawn_node();
+}
+
+void MultiplayerSpawner::_track(Node *p_node, const Variant &p_argument, int p_scene_id) {
+ ObjectID oid = p_node->get_instance_id();
+ if (!tracked_nodes.has(oid)) {
+ tracked_nodes[oid] = SpawnInfo(p_argument.duplicate(true), p_scene_id);
+ p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &MultiplayerSpawner::_node_exit), varray(p_node->get_instance_id()), CONNECT_ONESHOT);
+ p_node->connect(SceneStringNames::get_singleton()->ready, callable_mp(this, &MultiplayerSpawner::_node_ready), varray(p_node->get_instance_id()), CONNECT_ONESHOT);
+ }
+}
+
+void MultiplayerSpawner::_node_ready(ObjectID p_id) {
+ get_multiplayer()->spawn(ObjectDB::get_instance(p_id), this);
+}
+
+void MultiplayerSpawner::_node_exit(ObjectID p_id) {
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
+ ERR_FAIL_COND(!node);
+ if (tracked_nodes.has(p_id)) {
+ tracked_nodes.erase(p_id);
+ get_multiplayer()->despawn(node, this);
+ }
+}
+
+int MultiplayerSpawner::get_scene_id(const String &p_scene) const {
+ for (int i = 0; i < spawnable_scenes.size(); i++) {
+ Ref<PackedScene> ps = spawnable_scenes[i];
+ ERR_CONTINUE(ps.is_null());
+ if (ps->get_path() == p_scene) {
+ return i;
+ }
+ }
+ return INVALID_ID;
+}
+
+int MultiplayerSpawner::get_spawn_id(const ObjectID &p_id) const {
+ const SpawnInfo *info = tracked_nodes.getptr(p_id);
+ return info ? info->id : INVALID_ID;
+}
+
+const Variant MultiplayerSpawner::get_spawn_argument(const ObjectID &p_id) const {
+ const SpawnInfo *info = tracked_nodes.getptr(p_id);
+ return info ? info->args : Variant();
+}
+
+Node *MultiplayerSpawner::instantiate_scene(int p_id) {
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ ERR_FAIL_INDEX_V(p_id, spawnable_scenes.size(), nullptr);
+ Ref<PackedScene> scene = spawnable_scenes[p_id];
+ ERR_FAIL_COND_V(scene.is_null(), nullptr);
+ return scene->instantiate();
+}
+
+Node *MultiplayerSpawner::instantiate_custom(const Variant &p_data) {
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ Object *obj = nullptr;
+ Node *node = nullptr;
+ if (GDVIRTUAL_CALL(_spawn_custom, p_data, obj)) {
+ node = Object::cast_to<Node>(obj);
+ }
+ return node;
+}
+
+Node *MultiplayerSpawner::spawn(const Variant &p_data) {
+ ERR_FAIL_COND_V(!is_inside_tree() || !get_multiplayer()->has_multiplayer_peer() || !is_multiplayer_authority(), nullptr);
+ ERR_FAIL_COND_V_MSG(spawn_limit && spawn_limit <= tracked_nodes.size(), nullptr, "Spawn limit reached!");
+ ERR_FAIL_COND_V_MSG(!GDVIRTUAL_IS_OVERRIDDEN(_spawn_custom), nullptr, "Custom spawn requires the '_spawn_custom' virtual method to be implemented via script.");
+
+ Node *parent = get_spawn_node();
+ ERR_FAIL_COND_V_MSG(!parent, nullptr, "Cannot find spawn node.");
+
+ Node *node = instantiate_custom(p_data);
+ ERR_FAIL_COND_V_MSG(!node, nullptr, "The '_spawn_custom' implementation must return a valid Node.");
+
+ _track(node, p_data);
+ parent->add_child(node, true);
+ return node;
+}
diff --git a/scene/multiplayer/multiplayer_spawner.h b/scene/multiplayer/multiplayer_spawner.h
new file mode 100644
index 0000000000..63948e39a5
--- /dev/null
+++ b/scene/multiplayer/multiplayer_spawner.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* multiplayer_spawner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_SPAWNER_H
+#define MULTIPLAYER_SPAWNER_H
+
+#include "scene/main/node.h"
+
+#include "core/variant/typed_array.h"
+#include "scene/resources/packed_scene.h"
+#include "scene/resources/scene_replication_config.h"
+
+class MultiplayerSpawner : public Node {
+ GDCLASS(MultiplayerSpawner, Node);
+
+public:
+ enum {
+ INVALID_ID = 0xFF,
+ };
+
+private:
+ TypedArray<PackedScene> spawnable_scenes;
+ Set<ResourceUID::ID> spawnable_ids;
+ NodePath spawn_path;
+
+ struct SpawnInfo {
+ Variant args;
+ int id = INVALID_ID;
+ SpawnInfo(Variant p_args, int p_id) {
+ id = p_id;
+ args = p_args;
+ }
+ SpawnInfo() {}
+ };
+
+ ObjectID spawn_node;
+ HashMap<ObjectID, SpawnInfo> tracked_nodes;
+ bool auto_spawn = false;
+ uint32_t spawn_limit = 0;
+
+ void _update_spawn_node();
+ void _track(Node *p_node, const Variant &p_argument, int p_scene_id = INVALID_ID);
+ void _node_added(Node *p_node);
+ void _node_exit(ObjectID p_id);
+ void _node_ready(ObjectID p_id);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ Node *get_spawn_node() const { return spawn_node.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(spawn_node)) : nullptr; }
+ TypedArray<PackedScene> get_spawnable_scenes();
+ void set_spawnable_scenes(TypedArray<PackedScene> p_scenes);
+ NodePath get_spawn_path() const;
+ void set_spawn_path(const NodePath &p_path);
+ uint32_t get_spawn_limit() const { return spawn_limit; }
+ void set_spawn_limit(uint32_t p_limit) { spawn_limit = p_limit; }
+ bool is_auto_spawning() const;
+ void set_auto_spawning(bool p_enabled);
+
+ const Variant get_spawn_argument(const ObjectID &p_id) const;
+ int get_spawn_id(const ObjectID &p_id) const;
+ int get_scene_id(const String &p_path) const;
+ Node *spawn(const Variant &p_data = Variant());
+ Node *instantiate_custom(const Variant &p_data);
+ Node *instantiate_scene(int p_idx);
+
+ GDVIRTUAL1R(Object *, _spawn_custom, const Variant &);
+
+ MultiplayerSpawner() {}
+};
+
+#endif // MULTIPLAYER_SPAWNER_H
diff --git a/scene/multiplayer/multiplayer_synchronizer.cpp b/scene/multiplayer/multiplayer_synchronizer.cpp
new file mode 100644
index 0000000000..fbe1b99cc9
--- /dev/null
+++ b/scene/multiplayer/multiplayer_synchronizer.cpp
@@ -0,0 +1,158 @@
+/*************************************************************************/
+/* multiplayer_synchronizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "multiplayer_synchronizer.h"
+
+#include "core/config/engine.h"
+#include "core/multiplayer/multiplayer_api.h"
+
+Object *MultiplayerSynchronizer::_get_prop_target(Object *p_obj, const NodePath &p_path) {
+ if (p_path.get_name_count() == 0) {
+ return p_obj;
+ }
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V_MSG(!node || !node->has_node(p_path), nullptr, vformat("Node '%s' not found.", p_path));
+ return node->get_node(p_path);
+}
+
+void MultiplayerSynchronizer::_stop() {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->replication_stop(node, this);
+ }
+}
+
+void MultiplayerSynchronizer::_start() {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (node) {
+ get_multiplayer()->replication_start(node, this);
+ }
+}
+
+Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs) {
+ ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ r_variant.resize(p_properties.size());
+ r_variant_ptrs.resize(r_variant.size());
+ int i = 0;
+ for (const NodePath &prop : p_properties) {
+ bool valid = false;
+ const Object *obj = _get_prop_target(p_obj, prop);
+ ERR_FAIL_COND_V(!obj, FAILED);
+ r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid);
+ r_variant_ptrs.write[i] = &r_variant[i];
+ ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop));
+ i++;
+ }
+ return OK;
+}
+
+Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state) {
+ ERR_FAIL_COND_V(!p_obj, ERR_INVALID_PARAMETER);
+ int i = 0;
+ for (const NodePath &prop : p_properties) {
+ Object *obj = _get_prop_target(p_obj, prop);
+ ERR_FAIL_COND_V(!obj, FAILED);
+ obj->set(prop.get_concatenated_subnames(), p_state[i]);
+ i += 1;
+ }
+ return OK;
+}
+
+void MultiplayerSynchronizer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_path", "path"), &MultiplayerSynchronizer::set_root_path);
+ ClassDB::bind_method(D_METHOD("get_root_path"), &MultiplayerSynchronizer::get_root_path);
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path");
+
+ ClassDB::bind_method(D_METHOD("set_replication_interval", "milliseconds"), &MultiplayerSynchronizer::set_replication_interval);
+ ClassDB::bind_method(D_METHOD("get_replication_interval"), &MultiplayerSynchronizer::get_replication_interval);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "replication_interval", PROPERTY_HINT_RANGE, "0,5,0.001"), "set_replication_interval", "get_replication_interval");
+
+ ClassDB::bind_method(D_METHOD("set_replication_config", "config"), &MultiplayerSynchronizer::set_replication_config);
+ ClassDB::bind_method(D_METHOD("get_replication_config"), &MultiplayerSynchronizer::get_replication_config);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "SceneReplicationConfig"), "set_replication_config", "get_replication_config");
+}
+
+void MultiplayerSynchronizer::_notification(int p_what) {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ return;
+ }
+#endif
+ if (root_path.is_empty()) {
+ return;
+ }
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ _start();
+ } else if (p_what == NOTIFICATION_EXIT_TREE) {
+ _stop();
+ }
+}
+
+void MultiplayerSynchronizer::set_replication_interval(double p_interval) {
+ ERR_FAIL_COND_MSG(p_interval < 0, "Interval must be greater or equal to 0 (where 0 means default)");
+ interval_msec = uint64_t(p_interval * 1000);
+}
+
+double MultiplayerSynchronizer::get_replication_interval() const {
+ return double(interval_msec) / 1000.0;
+}
+
+uint64_t MultiplayerSynchronizer::get_replication_interval_msec() const {
+ return interval_msec;
+}
+
+void MultiplayerSynchronizer::set_replication_config(Ref<SceneReplicationConfig> p_config) {
+ replication_config = p_config;
+}
+
+Ref<SceneReplicationConfig> MultiplayerSynchronizer::get_replication_config() {
+ return replication_config;
+}
+
+void MultiplayerSynchronizer::set_root_path(const NodePath &p_path) {
+ _stop();
+ root_path = p_path;
+ _start();
+}
+
+NodePath MultiplayerSynchronizer::get_root_path() const {
+ return root_path;
+}
+
+void MultiplayerSynchronizer::set_multiplayer_authority(int p_peer_id, bool p_recursive) {
+ Node *node = is_inside_tree() ? get_node_or_null(root_path) : nullptr;
+ if (!node) {
+ Node::set_multiplayer_authority(p_peer_id, p_recursive);
+ return;
+ }
+ get_multiplayer()->replication_stop(node, this);
+ Node::set_multiplayer_authority(p_peer_id, p_recursive);
+ get_multiplayer()->replication_start(node, this);
+}
diff --git a/scene/multiplayer/multiplayer_synchronizer.h b/scene/multiplayer/multiplayer_synchronizer.h
new file mode 100644
index 0000000000..e856745379
--- /dev/null
+++ b/scene/multiplayer/multiplayer_synchronizer.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* multiplayer_synchronizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MULTIPLAYER_SYNCHRONIZER_H
+#define MULTIPLAYER_SYNCHRONIZER_H
+
+#include "scene/main/node.h"
+
+#include "scene/resources/scene_replication_config.h"
+
+class MultiplayerSynchronizer : public Node {
+ GDCLASS(MultiplayerSynchronizer, Node);
+
+private:
+ Ref<SceneReplicationConfig> replication_config;
+ NodePath root_path;
+ uint64_t interval_msec = 0;
+
+ static Object *_get_prop_target(Object *p_obj, const NodePath &p_prop);
+ void _start();
+ void _stop();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static Error get_state(const List<NodePath> &p_properties, Object *p_obj, Vector<Variant> &r_variant, Vector<const Variant *> &r_variant_ptrs);
+ static Error set_state(const List<NodePath> &p_properties, Object *p_obj, const Vector<Variant> &p_state);
+
+ void set_replication_interval(double p_interval);
+ double get_replication_interval() const;
+ uint64_t get_replication_interval_msec() const;
+
+ void set_replication_config(Ref<SceneReplicationConfig> p_config);
+ Ref<SceneReplicationConfig> get_replication_config();
+
+ void set_root_path(const NodePath &p_path);
+ NodePath get_root_path() const;
+ virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true) override;
+
+ MultiplayerSynchronizer() {}
+};
+
+#endif // MULTIPLAYER_SYNCHRONIZER_H
diff --git a/scene/multiplayer/scene_cache_interface.cpp b/scene/multiplayer/scene_cache_interface.cpp
new file mode 100644
index 0000000000..de4a94470a
--- /dev/null
+++ b/scene/multiplayer/scene_cache_interface.cpp
@@ -0,0 +1,249 @@
+/*************************************************************************/
+/* scene_cache_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_cache_interface.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+#include "scene/main/window.h"
+
+MultiplayerCacheInterface *SceneCacheInterface::_create(MultiplayerAPI *p_multiplayer) {
+ return memnew(SceneCacheInterface(p_multiplayer));
+}
+
+void SceneCacheInterface::make_default() {
+ MultiplayerAPI::create_default_cache_interface = _create;
+}
+
+void SceneCacheInterface::on_peer_change(int p_id, bool p_connected) {
+ if (p_connected) {
+ path_get_cache.insert(p_id, PathGetCache());
+ } else {
+ // Cleanup get cache.
+ path_get_cache.erase(p_id);
+ // Cleanup sent cache.
+ // Some refactoring is needed to make this faster and do paths GC.
+ List<NodePath> keys;
+ path_send_cache.get_key_list(&keys);
+ for (const NodePath &E : keys) {
+ PathSentCache *psc = path_send_cache.getptr(E);
+ psc->confirmed_peers.erase(p_id);
+ }
+ }
+}
+
+void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND(!root_node);
+ ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small.");
+ int ofs = 1;
+
+ String methods_md5;
+ methods_md5.parse_utf8((const char *)(p_packet + ofs), 32);
+ ofs += 33;
+
+ int id = decode_uint32(&p_packet[ofs]);
+ ofs += 4;
+
+ String paths;
+ paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from] = PathGetCache();
+ }
+
+ Node *node = root_node->get_node(path);
+ ERR_FAIL_COND(node == nullptr);
+ const bool valid_rpc_checksum = multiplayer->get_rpc_md5(node) == methods_md5;
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path = path;
+
+ path_get_cache[p_from].nodes[id] = ni;
+
+ // Encode path to send ack.
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(), nullptr);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + 1 + len);
+ packet.write[0] = MultiplayerAPI::NETWORK_COMMAND_CONFIRM_PATH;
+ packet.write[1] = valid_rpc_checksum;
+ encode_cstring(pname.get_data(), &packet.write[2]);
+
+ Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND(multiplayer_peer.is_null());
+
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ multiplayer_peer->set_target_peer(p_from);
+ multiplayer_peer->put_packet(packet.ptr(), packet.size());
+}
+
+void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+ ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small.");
+
+ const bool valid_rpc_checksum = p_packet[1];
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2);
+
+ NodePath path = paths;
+
+ if (valid_rpc_checksum == false) {
+ ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path);
+ }
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
+
+ Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND_MSG(!E, "Invalid packet received. Source peer was not found in cache for the given path.");
+ E->get() = true;
+}
+
+bool SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) {
+ bool has_all_peers = true;
+ List<int> peers_to_add; // If one is missing, take note to add it.
+
+ for (const Set<int>::Element *E = multiplayer->get_connected_peers().front(); E; E = E->next()) {
+ if (p_target < 0 && E->get() == -p_target) {
+ continue; // Continue, excluded.
+ }
+
+ if (p_target > 0 && E->get() != p_target) {
+ continue; // Continue, not for this peer.
+ }
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+
+ if (!F || !F->get()) {
+ // Path was not cached, or was cached but is unconfirmed.
+ if (!F) {
+ // Not cached at all, take note.
+ peers_to_add.push_back(E->get());
+ }
+
+ has_all_peers = false;
+ }
+ }
+
+ if (peers_to_add.size() > 0) {
+ // Those that need to be added, send a message for this.
+
+ // Encode function name.
+ const CharString path = String(p_path).utf8();
+ const int path_len = encode_cstring(path.get_data(), nullptr);
+
+ // Extract MD5 from rpc methods list.
+ const String methods_md5 = multiplayer->get_rpc_md5(p_node);
+ const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder.
+
+ Vector<uint8_t> packet;
+ packet.resize(1 + 4 + path_len + methods_md5_len);
+ int ofs = 0;
+
+ packet.write[ofs] = MultiplayerAPI::NETWORK_COMMAND_SIMPLIFY_PATH;
+ ofs += 1;
+
+ ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]);
+
+ ofs += encode_uint32(psc->id, &packet.write[ofs]);
+
+ ofs += encode_cstring(path.get_data(), &packet.write[ofs]);
+
+ Ref<MultiplayerPeer> multiplayer_peer = multiplayer->get_multiplayer_peer();
+ ERR_FAIL_COND_V(multiplayer_peer.is_null(), false);
+
+ for (int &E : peers_to_add) {
+ multiplayer_peer->set_target_peer(E); // To all of you.
+ multiplayer_peer->set_transfer_channel(0);
+ multiplayer_peer->set_transfer_mode(Multiplayer::TRANSFER_MODE_RELIABLE);
+ multiplayer_peer->put_packet(packet.ptr(), packet.size());
+
+ psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed.
+ }
+ }
+
+ return has_all_peers;
+}
+
+bool SceneCacheInterface::is_cache_confirmed(NodePath p_path, int p_peer) {
+ const PathSentCache *psc = path_send_cache.getptr(p_path);
+ ERR_FAIL_COND_V(!psc, false);
+ const Map<int, bool>::Element *F = psc->confirmed_peers.find(p_peer);
+ ERR_FAIL_COND_V(!F, false); // Should never happen.
+ return F->get();
+}
+
+bool SceneCacheInterface::send_object_cache(Object *p_obj, NodePath p_path, int p_peer_id, int &r_id) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, false);
+ // See if the path is cached.
+ PathSentCache *psc = path_send_cache.getptr(p_path);
+ if (!psc) {
+ // Path is not cached, create.
+ path_send_cache[p_path] = PathSentCache();
+ psc = path_send_cache.getptr(p_path);
+ psc->id = last_send_cache_id++;
+ }
+ r_id = psc->id;
+
+ return _send_confirm_path(node, p_path, psc, p_peer_id);
+}
+
+Object *SceneCacheInterface::get_cached_object(int p_from, uint32_t p_cache_id) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND_V(!root_node, nullptr);
+ Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
+ ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("No cache found for peer %d.", p_from));
+
+ Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(p_cache_id);
+ ERR_FAIL_COND_V_MSG(!F, nullptr, vformat("ID %d not found in cache of peer %d.", p_cache_id, p_from));
+
+ PathGetCache::NodeInfo *ni = &F->get();
+ Node *node = root_node->get_node(ni->path);
+ if (!node) {
+ ERR_PRINT("Failed to get cached path: " + String(ni->path) + ".");
+ }
+ return node;
+}
+
+void SceneCacheInterface::clear() {
+ path_get_cache.clear();
+ path_send_cache.clear();
+ last_send_cache_id = 1;
+}
diff --git a/scene/multiplayer/scene_cache_interface.h b/scene/multiplayer/scene_cache_interface.h
new file mode 100644
index 0000000000..91a53cb948
--- /dev/null
+++ b/scene/multiplayer/scene_cache_interface.h
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* scene_cache_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_CACHE_INTERFACE_H
+#define SCENE_CACHE_INTERFACE_H
+
+#include "core/multiplayer/multiplayer_api.h"
+
+class SceneCacheInterface : public MultiplayerCacheInterface {
+ GDCLASS(SceneCacheInterface, MultiplayerCacheInterface);
+
+private:
+ MultiplayerAPI *multiplayer;
+
+ //path sent caches
+ struct PathSentCache {
+ Map<int, bool> confirmed_peers;
+ int id;
+ };
+
+ //path get caches
+ struct PathGetCache {
+ struct NodeInfo {
+ NodePath path;
+ ObjectID instance;
+ };
+
+ Map<int, NodeInfo> nodes;
+ };
+
+ HashMap<NodePath, PathSentCache> path_send_cache;
+ Map<int, PathGetCache> path_get_cache;
+ int last_send_cache_id = 1;
+
+protected:
+ bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target);
+ static MultiplayerCacheInterface *_create(MultiplayerAPI *p_multiplayer);
+
+public:
+ static void make_default();
+
+ virtual void clear() override;
+ virtual void on_peer_change(int p_id, bool p_connected) override;
+ virtual void process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
+ virtual void process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) override;
+
+ // Returns true if all peers have cached path.
+ virtual bool send_object_cache(Object *p_obj, NodePath p_path, int p_target, int &p_id) override;
+ virtual Object *get_cached_object(int p_from, uint32_t p_cache_id) override;
+ virtual bool is_cache_confirmed(NodePath p_path, int p_peer) override;
+
+ SceneCacheInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
+};
+
+#endif // SCENE_CACHE_INTERFACE_H
diff --git a/scene/multiplayer/scene_replication_interface.cpp b/scene/multiplayer/scene_replication_interface.cpp
new file mode 100644
index 0000000000..2088a43ba7
--- /dev/null
+++ b/scene/multiplayer/scene_replication_interface.cpp
@@ -0,0 +1,411 @@
+/*************************************************************************/
+/* scene_replication_interface.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_replication_interface.h"
+
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+#include "scene/multiplayer/multiplayer_spawner.h"
+#include "scene/multiplayer/multiplayer_synchronizer.h"
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) \
+ packet_cache.resize(m_amount);
+
+MultiplayerReplicationInterface *SceneReplicationInterface::_create(MultiplayerAPI *p_multiplayer) {
+ return memnew(SceneReplicationInterface(p_multiplayer));
+}
+
+void SceneReplicationInterface::make_default() {
+ MultiplayerAPI::create_default_replication_interface = _create;
+}
+
+void SceneReplicationInterface::_free_remotes(int p_id) {
+ const HashMap<uint32_t, ObjectID> remotes = rep_state->peer_get_remotes(p_id);
+ const uint32_t *k = nullptr;
+ while ((k = remotes.next(k))) {
+ Node *node = rep_state->get_node(remotes.get(*k));
+ ERR_CONTINUE(!node);
+ node->queue_delete();
+ }
+}
+
+void SceneReplicationInterface::on_peer_change(int p_id, bool p_connected) {
+ if (p_connected) {
+ rep_state->on_peer_change(p_id, p_connected);
+ for (const ObjectID &oid : rep_state->get_spawned_nodes()) {
+ _send_spawn(rep_state->get_node(oid), rep_state->get_spawner(oid), p_id);
+ }
+ for (const ObjectID &oid : rep_state->get_path_only_nodes()) {
+ Node *node = rep_state->get_node(oid);
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!node || !sync);
+ if (sync->is_multiplayer_authority()) {
+ rep_state->peer_add_node(p_id, oid);
+ }
+ }
+ } else {
+ _free_remotes(p_id);
+ rep_state->on_peer_change(p_id, p_connected);
+ }
+}
+
+void SceneReplicationInterface::on_reset() {
+ for (int pid : rep_state->get_peers()) {
+ _free_remotes(pid);
+ }
+ rep_state->reset();
+}
+
+void SceneReplicationInterface::on_network_process() {
+ uint64_t msec = OS::get_singleton()->get_ticks_msec();
+ for (int peer : rep_state->get_peers()) {
+ _send_sync(peer, msec);
+ }
+}
+
+Error SceneReplicationInterface::on_spawn(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!spawner, ERR_INVALID_PARAMETER);
+ Error err = rep_state->config_add_spawn(node, spawner);
+ ERR_FAIL_COND_V(err != OK, err);
+ return _send_spawn(node, spawner, 0);
+}
+
+Error SceneReplicationInterface::on_despawn(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!p_obj || !spawner, ERR_INVALID_PARAMETER);
+ Error err = rep_state->config_del_spawn(node, spawner);
+ ERR_FAIL_COND_V(err != OK, err);
+ return _send_despawn(node, 0);
+}
+
+Error SceneReplicationInterface::on_replication_start(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!sync, ERR_INVALID_PARAMETER);
+ rep_state->config_add_sync(node, sync);
+ // Try to apply initial state if spawning (hack to apply if before ready).
+ if (pending_spawn == p_obj->get_instance_id()) {
+ pending_spawn = ObjectID(); // Make sure this only happens once.
+ const List<NodePath> props = sync->get_replication_config()->get_spawn_properties();
+ Vector<Variant> vars;
+ vars.resize(props.size());
+ int consumed;
+ Error err = MultiplayerAPI::decode_and_decompress_variants(vars, pending_buffer, pending_buffer_size, consumed);
+ ERR_FAIL_COND_V(err, err);
+ err = MultiplayerSynchronizer::set_state(props, node, vars);
+ ERR_FAIL_COND_V(err, err);
+ } else if (multiplayer->has_multiplayer_peer() && sync->is_multiplayer_authority()) {
+ // Either it's a spawn or a static sync, in any case add it to the list of known nodes.
+ rep_state->peer_add_node(0, p_obj->get_instance_id());
+ }
+ return OK;
+}
+
+Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_config) {
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node || p_config.get_type() != Variant::OBJECT, ERR_INVALID_PARAMETER);
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(p_config.get_validated_object());
+ ERR_FAIL_COND_V(!p_obj || !sync, ERR_INVALID_PARAMETER);
+ return rep_state->config_del_sync(node, sync);
+}
+
+Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable) {
+ ERR_FAIL_COND_V(!p_buffer || p_size < 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!multiplayer, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!multiplayer->has_multiplayer_peer(), ERR_UNCONFIGURED);
+ Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
+ peer->set_target_peer(p_peer);
+ peer->set_transfer_channel(0);
+ peer->set_transfer_mode(p_reliable ? Multiplayer::TRANSFER_MODE_RELIABLE : Multiplayer::TRANSFER_MODE_UNRELIABLE);
+ return peer->put_packet(p_buffer, p_size);
+}
+
+Error SceneReplicationInterface::_send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer) {
+ ERR_FAIL_COND_V(p_peer < 0, ERR_BUG);
+ ERR_FAIL_COND_V(!multiplayer, ERR_BUG);
+ ERR_FAIL_COND_V(!p_spawner || !p_node, ERR_BUG);
+
+ const ObjectID oid = p_node->get_instance_id();
+ uint32_t nid = rep_state->ensure_net_id(oid);
+
+ // Prepare custom arg and scene_id
+ uint8_t scene_id = p_spawner->get_spawn_id(oid);
+ bool is_custom = scene_id == MultiplayerSpawner::INVALID_ID;
+ Variant spawn_arg = p_spawner->get_spawn_argument(oid);
+ int spawn_arg_size = 0;
+ if (is_custom) {
+ Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, nullptr, spawn_arg_size, false);
+ ERR_FAIL_COND_V(err, err);
+ }
+
+ // Prepare spawn state.
+ int state_size = 0;
+ Vector<Variant> state_vars;
+ Vector<const Variant *> state_varp;
+ MultiplayerSynchronizer *synchronizer = rep_state->get_synchronizer(oid);
+ if (synchronizer && synchronizer->get_replication_config().is_valid()) {
+ const List<NodePath> props = synchronizer->get_replication_config()->get_spawn_properties();
+ Error err = MultiplayerSynchronizer::get_state(props, p_node, state_vars, state_varp);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to retrieve spawn state.");
+ err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), nullptr, state_size);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to encode spawn state.");
+ }
+
+ // Prepare simplified path.
+ NodePath rel_path = multiplayer->get_root_path().rel_path_to(p_spawner->get_path());
+
+ int path_id = 0;
+ multiplayer->send_object_cache(p_spawner, rel_path, p_peer, path_id);
+
+ // Encode name and parent ID.
+ CharString cname = p_node->get_name().operator String().utf8();
+ int nlen = encode_cstring(cname.get_data(), nullptr);
+ MAKE_ROOM(1 + 1 + 4 + 4 + 4 + nlen + (is_custom ? 4 + spawn_arg_size : 0) + state_size);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_SPAWN;
+ ptr[1] = scene_id;
+ int ofs = 2;
+ ofs += encode_uint32(path_id, &ptr[ofs]);
+ ofs += encode_uint32(nid, &ptr[ofs]);
+ ofs += encode_uint32(nlen, &ptr[ofs]);
+ ofs += encode_cstring(cname.get_data(), &ptr[ofs]);
+ // Write args
+ if (is_custom) {
+ ofs += encode_uint32(spawn_arg_size, &ptr[ofs]);
+ Error err = MultiplayerAPI::encode_and_compress_variant(spawn_arg, &ptr[ofs], spawn_arg_size, false);
+ ERR_FAIL_COND_V(err, err);
+ ofs += spawn_arg_size;
+ }
+ // Write state.
+ if (state_size) {
+ Error err = MultiplayerAPI::encode_and_compress_variants(state_varp.ptrw(), state_varp.size(), &ptr[ofs], state_size);
+ ERR_FAIL_COND_V(err, err);
+ ofs += state_size;
+ }
+ Error err = _send_raw(ptr, ofs, p_peer, true);
+ ERR_FAIL_COND_V(err, err);
+ return rep_state->peer_add_node(p_peer, oid);
+}
+
+Error SceneReplicationInterface::_send_despawn(Node *p_node, int p_peer) {
+ const ObjectID oid = p_node->get_instance_id();
+ MAKE_ROOM(5);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = (uint8_t)MultiplayerAPI::NETWORK_COMMAND_DESPAWN;
+ int ofs = 1;
+ uint32_t nid = rep_state->get_net_id(oid);
+ ofs += encode_uint32(nid, &ptr[ofs]);
+ Error err = _send_raw(ptr, ofs, p_peer, true);
+ ERR_FAIL_COND_V(err, err);
+ return rep_state->peer_del_node(p_peer, oid);
+}
+
+Error SceneReplicationInterface::on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 14, ERR_INVALID_DATA, "Invalid spawn packet received");
+ int ofs = 1; // The spawn/despawn command.
+ uint8_t scene_id = p_buffer[ofs];
+ ofs += 1;
+ uint32_t node_target = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ MultiplayerSpawner *spawner = Object::cast_to<MultiplayerSpawner>(multiplayer->get_cached_object(p_from, node_target));
+ ERR_FAIL_COND_V(!spawner, ERR_DOES_NOT_EXIST);
+ ERR_FAIL_COND_V(p_from != spawner->get_multiplayer_authority(), ERR_UNAUTHORIZED);
+
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ uint32_t name_len = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ ERR_FAIL_COND_V_MSG(name_len > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA, vformat("Invalid spawn packet size: %d, wants: %d", p_buffer_len, ofs + name_len));
+ ERR_FAIL_COND_V_MSG(name_len < 1, ERR_INVALID_DATA, "Zero spawn name size.");
+
+ // We need to make sure no trickery happens here, but we want to allow autogenerated ("@") node names.
+ const String name = String::utf8((const char *)&p_buffer[ofs], name_len);
+ ERR_FAIL_COND_V_MSG(name.validate_node_name() != name, ERR_INVALID_DATA, vformat("Invalid node name received: '%s'. Make sure to add nodes via 'add_child(node, true)' remotely.", name));
+ ofs += name_len;
+
+ // Check that we can spawn.
+ Node *parent = spawner->get_node_or_null(spawner->get_spawn_path());
+ ERR_FAIL_COND_V(!parent, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(parent->has_node(name), ERR_INVALID_DATA);
+
+ Node *node = nullptr;
+ if (scene_id == MultiplayerSpawner::INVALID_ID) {
+ // Custom spawn.
+ ERR_FAIL_COND_V(p_buffer_len - ofs < 4, ERR_INVALID_DATA);
+ uint32_t arg_size = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ ERR_FAIL_COND_V(arg_size > uint32_t(p_buffer_len - ofs), ERR_INVALID_DATA);
+ Variant v;
+ Error err = MultiplayerAPI::decode_and_decompress_variant(v, &p_buffer[ofs], arg_size, nullptr, false);
+ ERR_FAIL_COND_V(err != OK, err);
+ ofs += arg_size;
+ node = spawner->instantiate_custom(v);
+ } else {
+ // Scene based spawn.
+ node = spawner->instantiate_scene(scene_id);
+ }
+ ERR_FAIL_COND_V(!node, ERR_UNAUTHORIZED);
+ node->set_name(name);
+ rep_state->peer_add_remote(p_from, net_id, node, spawner);
+ // The initial state will be applied during the sync config (i.e. before _ready).
+ int state_len = p_buffer_len - ofs;
+ if (state_len) {
+ pending_spawn = node->get_instance_id();
+ pending_buffer = &p_buffer[ofs];
+ pending_buffer_size = state_len;
+ }
+ parent->add_child(node);
+ pending_spawn = ObjectID();
+ pending_buffer = nullptr;
+ pending_buffer_size = 0;
+ return OK;
+}
+
+Error SceneReplicationInterface::on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 5, ERR_INVALID_DATA, "Invalid spawn packet received");
+ int ofs = 1; // The spawn/despawn command.
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ Node *node = nullptr;
+ Error err = rep_state->peer_del_remote(p_from, net_id, &node);
+ ERR_FAIL_COND_V(err != OK, err);
+ ERR_FAIL_COND_V(!node, ERR_BUG);
+ node->queue_delete();
+ return OK;
+}
+
+void SceneReplicationInterface::_send_sync(int p_peer, uint64_t p_msec) {
+ const Set<ObjectID> &known = rep_state->get_known_nodes(p_peer);
+ if (known.is_empty()) {
+ return;
+ }
+ MAKE_ROOM(sync_mtu);
+ uint8_t *ptr = packet_cache.ptrw();
+ ptr[0] = MultiplayerAPI::NETWORK_COMMAND_SYNC;
+ int ofs = 1;
+ ofs += encode_uint16(rep_state->peer_sync_next(p_peer), &ptr[1]);
+ // Can only send updates for already notified nodes.
+ // This is a lazy implementation, we could optimize much more here with by grouping by replication config.
+ for (const ObjectID &oid : known) {
+ if (!rep_state->update_sync_time(oid, p_msec)) {
+ continue; // nothing to sync.
+ }
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_CONTINUE(!sync);
+ Node *node = rep_state->get_node(oid);
+ ERR_CONTINUE(!node);
+ int size;
+ Vector<Variant> vars;
+ Vector<const Variant *> varp;
+ const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
+ Error err = MultiplayerSynchronizer::get_state(props, node, vars, varp);
+ ERR_CONTINUE_MSG(err != OK, "Unable to retrieve sync state.");
+ err = MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), nullptr, size);
+ ERR_CONTINUE_MSG(err != OK, "Unable to encode sync state.");
+ // TODO Handle single state above MTU.
+ ERR_CONTINUE_MSG(size > 3 + 4 + 4 + sync_mtu, vformat("Node states bigger then MTU will not be sent (%d > %d): %s", size, sync_mtu, node->get_path()));
+ if (ofs + 4 + 4 + size > sync_mtu) {
+ // Send what we got, and reset write.
+ _send_raw(packet_cache.ptr(), ofs, p_peer, false);
+ ofs = 3;
+ }
+ if (size) {
+ uint32_t net_id = rep_state->get_net_id(oid);
+ if (net_id == 0) {
+ // First time path based ID.
+ NodePath rel_path = multiplayer->get_root_path().rel_path_to(sync->get_path());
+ int path_id = 0;
+ multiplayer->send_object_cache(sync, rel_path, p_peer, path_id);
+ net_id = path_id;
+ rep_state->set_net_id(oid, net_id | 0x80000000);
+ }
+ ofs += encode_uint32(rep_state->get_net_id(oid), &ptr[ofs]);
+ ofs += encode_uint32(size, &ptr[ofs]);
+ MultiplayerAPI::encode_and_compress_variants(varp.ptrw(), varp.size(), &ptr[ofs], size);
+ ofs += size;
+ }
+ }
+ if (ofs > 3) {
+ // Got some left over to send.
+ _send_raw(packet_cache.ptr(), ofs, p_peer, false);
+ }
+}
+
+Error SceneReplicationInterface::on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_COND_V_MSG(p_buffer_len < 11, ERR_INVALID_DATA, "Invalid sync packet received");
+ uint16_t time = decode_uint16(&p_buffer[1]);
+ int ofs = 3;
+ rep_state->peer_sync_recv(p_from, time);
+ while (ofs + 8 < p_buffer_len) {
+ uint32_t net_id = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ uint32_t size = decode_uint32(&p_buffer[ofs]);
+ ofs += 4;
+ Node *node = nullptr;
+ if (net_id & 0x80000000) {
+ MultiplayerSynchronizer *sync = Object::cast_to<MultiplayerSynchronizer>(multiplayer->get_cached_object(p_from, net_id & 0x7FFFFFFF));
+ ERR_FAIL_COND_V(!sync || sync->get_multiplayer_authority() != p_from, ERR_UNAUTHORIZED);
+ node = sync->get_node(sync->get_root_path());
+ } else {
+ node = rep_state->peer_get_remote(p_from, net_id);
+ }
+ if (!node) {
+ // Not received yet.
+ ofs += size;
+ continue;
+ }
+ const ObjectID oid = node->get_instance_id();
+ if (!rep_state->update_last_node_sync(oid, time)) {
+ // State is too old.
+ ofs += size;
+ continue;
+ }
+ MultiplayerSynchronizer *sync = rep_state->get_synchronizer(oid);
+ ERR_FAIL_COND_V(!sync, ERR_BUG);
+ ERR_FAIL_COND_V(size > uint32_t(p_buffer_len - ofs), ERR_BUG);
+ const List<NodePath> props = sync->get_replication_config()->get_sync_properties();
+ Vector<Variant> vars;
+ vars.resize(props.size());
+ int consumed;
+ Error err = MultiplayerAPI::decode_and_decompress_variants(vars, &p_buffer[ofs], size, consumed);
+ ERR_FAIL_COND_V(err, err);
+ err = MultiplayerSynchronizer::set_state(props, node, vars);
+ ERR_FAIL_COND_V(err, err);
+ ofs += size;
+ }
+ return OK;
+}
diff --git a/scene/multiplayer/scene_replication_interface.h b/scene/multiplayer/scene_replication_interface.h
new file mode 100644
index 0000000000..855878d029
--- /dev/null
+++ b/scene/multiplayer/scene_replication_interface.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* scene_replication_interface.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_TREE_REPLICATOR_INTERFACE_H
+#define SCENE_TREE_REPLICATOR_INTERFACE_H
+
+#include "core/multiplayer/multiplayer_api.h"
+
+#include "scene/multiplayer/scene_replication_state.h"
+
+class SceneReplicationInterface : public MultiplayerReplicationInterface {
+ GDCLASS(SceneReplicationInterface, MultiplayerReplicationInterface);
+
+private:
+ void _send_sync(int p_peer, uint64_t p_msec);
+ Error _send_spawn(Node *p_node, MultiplayerSpawner *p_spawner, int p_peer);
+ Error _send_despawn(Node *p_node, int p_peer);
+ Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable);
+
+ void _free_remotes(int p_peer);
+
+ Ref<SceneReplicationState> rep_state;
+ MultiplayerAPI *multiplayer;
+ PackedByteArray packet_cache;
+ int sync_mtu = 1350; // Highly dependent on underlying protocol.
+
+ // An hack to apply the initial state before ready.
+ ObjectID pending_spawn;
+ const uint8_t *pending_buffer = nullptr;
+ int pending_buffer_size = 0;
+
+protected:
+ static MultiplayerReplicationInterface *_create(MultiplayerAPI *p_multiplayer);
+
+public:
+ static void make_default();
+
+ virtual void on_reset() override;
+ virtual void on_peer_change(int p_id, bool p_connected) override;
+
+ virtual Error on_spawn(Object *p_obj, Variant p_config) override;
+ virtual Error on_despawn(Object *p_obj, Variant p_config) override;
+ virtual Error on_replication_start(Object *p_obj, Variant p_config) override;
+ virtual Error on_replication_stop(Object *p_obj, Variant p_config) override;
+ virtual void on_network_process() override;
+
+ virtual Error on_spawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
+ virtual Error on_despawn_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
+ virtual Error on_sync_receive(int p_from, const uint8_t *p_buffer, int p_buffer_len) override;
+
+ SceneReplicationInterface(MultiplayerAPI *p_multiplayer) {
+ rep_state.instantiate();
+ multiplayer = p_multiplayer;
+ }
+};
+
+#endif // SCENE_TREE_REPLICATOR_INTERFACE_H
diff --git a/scene/multiplayer/scene_replication_state.cpp b/scene/multiplayer/scene_replication_state.cpp
new file mode 100644
index 0000000000..b8dadeff24
--- /dev/null
+++ b/scene/multiplayer/scene_replication_state.cpp
@@ -0,0 +1,258 @@
+/*************************************************************************/
+/* scene_replication_state.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/multiplayer/scene_replication_state.h"
+
+#include "core/multiplayer/multiplayer_api.h"
+#include "scene/multiplayer/multiplayer_spawner.h"
+#include "scene/multiplayer/multiplayer_synchronizer.h"
+#include "scene/scene_string_names.h"
+
+SceneReplicationState::TrackedNode &SceneReplicationState::_track(const ObjectID &p_id) {
+ if (!tracked_nodes.has(p_id)) {
+ tracked_nodes[p_id] = TrackedNode(p_id);
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(p_id));
+ node->connect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack), varray(p_id), Node::CONNECT_ONESHOT);
+ }
+ return tracked_nodes[p_id];
+}
+
+void SceneReplicationState::_untrack(const ObjectID &p_id) {
+ if (tracked_nodes.has(p_id)) {
+ uint32_t net_id = tracked_nodes[p_id].net_id;
+ uint32_t peer = tracked_nodes[p_id].remote_peer;
+ tracked_nodes.erase(p_id);
+ // If it was spawned by a remote, remove it from the received nodes.
+ if (peer && peers_info.has(peer)) {
+ peers_info[peer].recv_nodes.erase(net_id);
+ }
+ // If we spawned or synced it, we need to remove it from any peer it was sent to.
+ if (net_id || peer == 0) {
+ const int *k = nullptr;
+ while ((k = peers_info.next(k))) {
+ peers_info.get(*k).known_nodes.erase(p_id);
+ }
+ }
+ }
+}
+
+const HashMap<uint32_t, ObjectID> SceneReplicationState::peer_get_remotes(int p_peer) const {
+ return peers_info.has(p_peer) ? peers_info[p_peer].recv_nodes : HashMap<uint32_t, ObjectID>();
+}
+
+bool SceneReplicationState::update_last_node_sync(const ObjectID &p_id, uint16_t p_time) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, false);
+ if (p_time <= tnode->last_sync && tnode->last_sync - p_time < 32767) {
+ return false;
+ }
+ tnode->last_sync = p_time;
+ return true;
+}
+
+bool SceneReplicationState::update_sync_time(const ObjectID &p_id, uint64_t p_msec) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, false);
+ MultiplayerSynchronizer *sync = get_synchronizer(p_id);
+ if (!sync) {
+ return false;
+ }
+ if (tnode->last_sync_msec == p_msec) {
+ return true;
+ }
+ if (p_msec >= tnode->last_sync_msec + sync->get_replication_interval_msec()) {
+ tnode->last_sync_msec = p_msec;
+ return true;
+ }
+ return false;
+}
+
+const Set<ObjectID> SceneReplicationState::get_known_nodes(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), Set<ObjectID>());
+ return peers_info[p_peer].known_nodes;
+}
+
+uint32_t SceneReplicationState::get_net_id(const ObjectID &p_id) const {
+ const TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, 0);
+ return tnode->net_id;
+}
+
+void SceneReplicationState::set_net_id(const ObjectID &p_id, uint32_t p_net_id) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND(!tnode);
+ tnode->net_id = p_net_id;
+}
+
+uint32_t SceneReplicationState::ensure_net_id(const ObjectID &p_id) {
+ TrackedNode *tnode = tracked_nodes.getptr(p_id);
+ ERR_FAIL_COND_V(!tnode, 0);
+ if (tnode->net_id == 0) {
+ tnode->net_id = ++last_net_id;
+ }
+ return tnode->net_id;
+}
+
+void SceneReplicationState::on_peer_change(int p_peer, bool p_connected) {
+ if (p_connected) {
+ peers_info[p_peer] = PeerInfo();
+ known_peers.insert(p_peer);
+ } else {
+ peers_info.erase(p_peer);
+ known_peers.erase(p_peer);
+ }
+}
+
+void SceneReplicationState::reset() {
+ peers_info.clear();
+ known_peers.clear();
+ // Tracked nodes are cleared on deletion, here we only reset the ids so they can be later re-assigned.
+ const ObjectID *oid = nullptr;
+ while ((oid = tracked_nodes.next(oid))) {
+ TrackedNode &tobj = tracked_nodes[*oid];
+ tobj.net_id = 0;
+ tobj.remote_peer = 0;
+ tobj.last_sync = 0;
+ }
+}
+
+Error SceneReplicationState::config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
+ const ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.spawner != ObjectID(), ERR_ALREADY_IN_USE);
+ tobj.spawner = p_spawner->get_instance_id();
+ spawned_nodes.insert(oid);
+ // The spawner may be notified after the synchronizer.
+ path_only_nodes.erase(oid);
+ return OK;
+}
+
+Error SceneReplicationState::config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner) {
+ const ObjectID oid = p_node->get_instance_id();
+ ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.spawner != p_spawner->get_instance_id(), ERR_INVALID_PARAMETER);
+ tobj.spawner = ObjectID();
+ spawned_nodes.erase(oid);
+ return OK;
+}
+
+Error SceneReplicationState::config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
+ const ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.synchronizer != ObjectID(), ERR_ALREADY_IN_USE);
+ tobj.synchronizer = p_sync->get_instance_id();
+ // If it doesn't have a spawner, we might need to assign ID for this node using it's path.
+ if (tobj.spawner.is_null()) {
+ path_only_nodes.insert(oid);
+ }
+ return OK;
+}
+
+Error SceneReplicationState::config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync) {
+ const ObjectID oid = p_node->get_instance_id();
+ ERR_FAIL_COND_V(!is_tracked(oid), ERR_INVALID_PARAMETER);
+ TrackedNode &tobj = _track(oid);
+ ERR_FAIL_COND_V(tobj.synchronizer != p_sync->get_instance_id(), ERR_INVALID_PARAMETER);
+ tobj.synchronizer = ObjectID();
+ if (path_only_nodes.has(oid)) {
+ p_node->disconnect(SceneStringNames::get_singleton()->tree_exited, callable_mp(this, &SceneReplicationState::_untrack));
+ _untrack(oid);
+ path_only_nodes.erase(oid);
+ }
+ return OK;
+}
+
+Error SceneReplicationState::peer_add_node(int p_peer, const ObjectID &p_id) {
+ if (p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].known_nodes.insert(p_id);
+ } else {
+ const int *pid = nullptr;
+ while ((pid = peers_info.next(pid))) {
+ peers_info.get(*pid).known_nodes.insert(p_id);
+ }
+ }
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_node(int p_peer, const ObjectID &p_id) {
+ if (p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_INVALID_PARAMETER);
+ peers_info[p_peer].known_nodes.erase(p_id);
+ } else {
+ const int *pid = nullptr;
+ while ((pid = peers_info.next(pid))) {
+ peers_info.get(*pid).known_nodes.erase(p_id);
+ }
+ }
+ return OK;
+}
+
+Node *SceneReplicationState::peer_get_remote(int p_peer, uint32_t p_net_id) {
+ PeerInfo *info = peers_info.getptr(p_peer);
+ return info && info->recv_nodes.has(p_net_id) ? Object::cast_to<Node>(ObjectDB::get_instance(info->recv_nodes[p_net_id])) : nullptr;
+}
+
+Error SceneReplicationState::peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner) {
+ ERR_FAIL_COND_V(!p_node || !p_spawner, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAVAILABLE);
+ PeerInfo &pinfo = peers_info[p_peer];
+ ObjectID oid = p_node->get_instance_id();
+ TrackedNode &tobj = _track(oid);
+ tobj.spawner = p_spawner->get_instance_id();
+ tobj.net_id = p_net_id;
+ tobj.remote_peer = p_peer;
+ tobj.last_sync = pinfo.last_recv_sync;
+ // Also track as a remote.
+ ERR_FAIL_COND_V(pinfo.recv_nodes.has(p_net_id), ERR_ALREADY_IN_USE);
+ pinfo.recv_nodes[p_net_id] = oid;
+ return OK;
+}
+
+Error SceneReplicationState::peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), ERR_UNAUTHORIZED);
+ PeerInfo &info = peers_info[p_peer];
+ ERR_FAIL_COND_V(!info.recv_nodes.has(p_net_id), ERR_UNAUTHORIZED);
+ *r_node = Object::cast_to<Node>(ObjectDB::get_instance(info.recv_nodes[p_net_id]));
+ info.recv_nodes.erase(p_net_id);
+ return OK;
+}
+
+uint16_t SceneReplicationState::peer_sync_next(int p_peer) {
+ ERR_FAIL_COND_V(!peers_info.has(p_peer), 0);
+ PeerInfo &info = peers_info[p_peer];
+ return ++info.last_sent_sync;
+}
+
+void SceneReplicationState::peer_sync_recv(int p_peer, uint16_t p_time) {
+ ERR_FAIL_COND(!peers_info.has(p_peer));
+ peers_info[p_peer].last_recv_sync = p_time;
+}
diff --git a/scene/multiplayer/scene_replication_state.h b/scene/multiplayer/scene_replication_state.h
new file mode 100644
index 0000000000..18e4d9fa39
--- /dev/null
+++ b/scene/multiplayer/scene_replication_state.h
@@ -0,0 +1,121 @@
+/*************************************************************************/
+/* scene_replication_state.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_REPLICATON_STATE_H
+#define SCENE_REPLICATON_STATE_H
+
+#include "core/object/ref_counted.h"
+
+class MultiplayerSpawner;
+class MultiplayerSynchronizer;
+class Node;
+
+class SceneReplicationState : public RefCounted {
+private:
+ struct TrackedNode {
+ ObjectID id;
+ uint32_t net_id = 0;
+ uint32_t remote_peer = 0;
+ ObjectID spawner;
+ ObjectID synchronizer;
+ uint16_t last_sync = 0;
+ uint64_t last_sync_msec = 0;
+
+ bool operator==(const ObjectID &p_other) { return id == p_other; }
+
+ Node *get_node() const { return id.is_valid() ? Object::cast_to<Node>(ObjectDB::get_instance(id)) : nullptr; }
+ MultiplayerSpawner *get_spawner() const { return spawner.is_valid() ? Object::cast_to<MultiplayerSpawner>(ObjectDB::get_instance(spawner)) : nullptr; }
+ MultiplayerSynchronizer *get_synchronizer() const { return synchronizer.is_valid() ? Object::cast_to<MultiplayerSynchronizer>(ObjectDB::get_instance(synchronizer)) : nullptr; }
+ TrackedNode() {}
+ TrackedNode(const ObjectID &p_id) { id = p_id; }
+ TrackedNode(const ObjectID &p_id, uint32_t p_net_id) {
+ id = p_id;
+ net_id = p_net_id;
+ }
+ };
+
+ struct PeerInfo {
+ Set<ObjectID> known_nodes;
+ HashMap<uint32_t, ObjectID> recv_nodes;
+ uint16_t last_sent_sync = 0;
+ uint16_t last_recv_sync = 0;
+ };
+
+ Set<int> known_peers;
+ uint32_t last_net_id = 0;
+ HashMap<ObjectID, TrackedNode> tracked_nodes;
+ HashMap<int, PeerInfo> peers_info;
+ Set<ObjectID> spawned_nodes;
+ Set<ObjectID> path_only_nodes;
+
+ TrackedNode &_track(const ObjectID &p_id);
+ void _untrack(const ObjectID &p_id);
+ bool is_tracked(const ObjectID &p_id) const { return tracked_nodes.has(p_id); }
+
+public:
+ const Set<int> get_peers() const { return known_peers; }
+ const Set<ObjectID> get_spawned_nodes() const { return spawned_nodes; }
+ const Set<ObjectID> get_path_only_nodes() const { return path_only_nodes; }
+
+ MultiplayerSynchronizer *get_synchronizer(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_synchronizer() : nullptr; }
+ MultiplayerSpawner *get_spawner(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_spawner() : nullptr; }
+ Node *get_node(const ObjectID &p_id) { return tracked_nodes.has(p_id) ? tracked_nodes[p_id].get_node() : nullptr; }
+ bool update_last_node_sync(const ObjectID &p_id, uint16_t p_time);
+ bool update_sync_time(const ObjectID &p_id, uint64_t p_msec);
+
+ const Set<ObjectID> get_known_nodes(int p_peer);
+ uint32_t get_net_id(const ObjectID &p_id) const;
+ void set_net_id(const ObjectID &p_id, uint32_t p_net_id);
+ uint32_t ensure_net_id(const ObjectID &p_id);
+
+ void reset();
+ void on_peer_change(int p_peer, bool p_connected);
+
+ Error config_add_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
+ Error config_del_spawn(Node *p_node, MultiplayerSpawner *p_spawner);
+
+ Error config_add_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
+ Error config_del_sync(Node *p_node, MultiplayerSynchronizer *p_sync);
+
+ Error peer_add_node(int p_peer, const ObjectID &p_id);
+ Error peer_del_node(int p_peer, const ObjectID &p_id);
+
+ const HashMap<uint32_t, ObjectID> peer_get_remotes(int p_peer) const;
+ Node *peer_get_remote(int p_peer, uint32_t p_net_id);
+ Error peer_add_remote(int p_peer, uint32_t p_net_id, Node *p_node, MultiplayerSpawner *p_spawner);
+ Error peer_del_remote(int p_peer, uint32_t p_net_id, Node **r_node);
+
+ uint16_t peer_sync_next(int p_peer);
+ void peer_sync_recv(int p_peer, uint16_t p_time);
+
+ SceneReplicationState() {}
+};
+
+#endif // SCENE_REPLICATON_STATE_H
diff --git a/core/multiplayer/rpc_manager.cpp b/scene/multiplayer/scene_rpc_interface.cpp
index 7736637349..7d7f57b9a1 100644
--- a/core/multiplayer/rpc_manager.cpp
+++ b/scene/multiplayer/scene_rpc_interface.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* rpc_manager.cpp */
+/* scene_rpc_interface.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/multiplayer/rpc_manager.h"
+#include "scene/multiplayer/scene_rpc_interface.h"
#include "core/debugger/engine_debugger.h"
#include "core/io/marshalls.h"
#include "core/multiplayer/multiplayer_api.h"
#include "scene/main/node.h"
+#include "scene/main/window.h"
+
+MultiplayerRPCInterface *SceneRPCInterface::_create(MultiplayerAPI *p_multiplayer) {
+ return memnew(SceneRPCInterface(p_multiplayer));
+}
+
+void SceneRPCInterface::make_default() {
+ MultiplayerAPI::create_default_rpc_interface = _create;
+}
#ifdef DEBUG_ENABLED
-_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {
if (EngineDebugger::is_profiling("multiplayer")) {
Array values;
values.push_back("node");
@@ -46,7 +55,7 @@ _FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectI
}
}
#else
-_FORCE_INLINE_ void RPCManager::_profile_node_data(const String &p_what, ObjectID p_id) {}
+_FORCE_INLINE_ void SceneRPCInterface::_profile_node_data(const String &p_what, ObjectID p_id) {}
#endif
// Returns the packet size stripping the node path added when the node is not yet cached.
@@ -110,14 +119,16 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, Multiplayer::RPCMode mode, int
return false;
}
-String RPCManager::get_rpc_md5(const Node *p_node) {
+String SceneRPCInterface::get_rpc_md5(const Object *p_obj) const {
+ const Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND_V(!node, "");
String rpc_list;
- const Vector<Multiplayer::RPCConfig> node_config = p_node->get_node_rpc_methods();
+ const Vector<Multiplayer::RPCConfig> node_config = node->get_node_rpc_methods();
for (int i = 0; i < node_config.size(); i++) {
rpc_list += String(node_config[i].name);
}
- if (p_node->get_script_instance()) {
- const Vector<Multiplayer::RPCConfig> script_config = p_node->get_script_instance()->get_rpc_methods();
+ if (node->get_script_instance()) {
+ const Vector<Multiplayer::RPCConfig> script_config = node->get_script_instance()->get_rpc_methods();
for (int i = 0; i < script_config.size(); i++) {
rpc_list += String(script_config[i].name);
}
@@ -125,7 +136,9 @@ String RPCManager::get_rpc_md5(const Node *p_node) {
return rpc_list.md5_text();
}
-Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
+Node *SceneRPCInterface::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) {
+ Node *root_node = SceneTree::get_singleton()->get_root()->get_node(multiplayer->get_root_path());
+ ERR_FAIL_COND_V(!root_node, nullptr);
Node *node = nullptr;
if (p_node_target & 0x80000000) {
@@ -139,7 +152,7 @@ Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_
NodePath np = paths;
- node = multiplayer->get_root_node()->get_node(np);
+ node = root_node->get_node(np);
if (!node) {
ERR_PRINT("Failed to get path from RPC: " + String(np) + ".");
@@ -147,11 +160,11 @@ Node *RPCManager::_process_get_node(int p_from, const uint8_t *p_packet, uint32_
return node;
} else {
// Use cached path.
- return multiplayer->get_cached_node(p_from, p_node_target);
+ return Object::cast_to<Node>(multiplayer->get_cached_object(p_from, p_node_target));
}
}
-void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
+void SceneRPCInterface::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) {
// Extract packet meta
int packet_min_size = 1;
int name_id_offset = 1;
@@ -224,7 +237,7 @@ void RPCManager::process_rpc(int p_from, const uint8_t *p_packet, int p_packet_l
_process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size);
}
-void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+void SceneRPCInterface::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small.");
// Check that remote can call the RPC on this node.
@@ -235,16 +248,12 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(config.name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)config.rpc_mode) + ", authority is " + itos(p_node->get_multiplayer_authority()) + ".");
int argc = 0;
- bool byte_only = false;
const bool byte_only_or_no_args = p_packet[0] & BYTE_ONLY_OR_NO_ARGS_FLAG;
if (byte_only_or_no_args) {
if (p_offset < p_packet_len) {
// This packet contains only bytes.
argc = 1;
- byte_only = true;
- } else {
- // This rpc calls a method without parameters.
}
} else {
// Normal variant, takes the argument count from the packet.
@@ -262,25 +271,10 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int
_profile_node_data("in_rpc", p_node->get_instance_id());
#endif
- if (byte_only) {
- Vector<uint8_t> pure_data;
- const int len = p_packet_len - p_offset;
- pure_data.resize(len);
- memcpy(pure_data.ptrw(), &p_packet[p_offset], len);
- args.write[0] = pure_data;
- argp.write[0] = &args[0];
- p_offset += len;
- } else {
- for (int i = 0; i < argc; i++) {
- ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
-
- int vlen;
- Error err = multiplayer->decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
- ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
-
- argp.write[i] = &args[i];
- p_offset += vlen;
- }
+ int out;
+ MultiplayerAPI::decode_and_decompress_variants(args, &p_packet[p_offset], p_packet_len - p_offset, out, byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ for (int i = 0; i < argc; i++) {
+ argp.write[i] = &args[i];
}
Callable::CallError ce;
@@ -293,7 +287,7 @@ void RPCManager::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int
}
}
-void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
+void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Multiplayer::RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) {
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
ERR_FAIL_COND_MSG(peer.is_null(), "Attempt to call RPC without active multiplayer peer.");
@@ -309,12 +303,12 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult
ERR_FAIL_MSG("Attempt to call RPC with unknown peer ID: " + itos(p_to) + ".");
}
- NodePath from_path = (multiplayer->get_root_node()->get_path()).rel_path_to(p_from->get_path());
+ NodePath from_path = multiplayer->get_root_path().rel_path_to(p_from->get_path());
ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
// See if all peers have cached path (if so, call can be fast).
int psc_id;
- const bool has_all_peers = multiplayer->send_confirm_path(p_from, from_path, p_to, psc_id);
+ const bool has_all_peers = multiplayer->send_object_cache(p_from, from_path, p_to, psc_id);
// Create base packet, lots of hardcode because it must be tight.
@@ -380,28 +374,19 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult
ofs += 2;
}
- if (p_argcount == 0) {
- byte_only_or_no_args = true;
- } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) {
- byte_only_or_no_args = true;
- // Special optimization when only the byte vector is sent.
- const Vector<uint8_t> data = *p_arg[0];
- MAKE_ROOM(ofs + data.size());
- memcpy(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size());
- ofs += data.size();
+ int len;
+ Error err = MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, nullptr, len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC arguments. THIS IS LIKELY A BUG IN THE ENGINE!");
+ if (byte_only_or_no_args) {
+ MAKE_ROOM(ofs + len);
} else {
- // Arguments
- MAKE_ROOM(ofs + 1);
+ MAKE_ROOM(ofs + 1 + len);
packet_cache.write[ofs] = p_argcount;
ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- int len(0);
- Error err = multiplayer->encode_and_compress_variant(*p_arg[i], nullptr, len);
- ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
- MAKE_ROOM(ofs + len);
- multiplayer->encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len);
- ofs += len;
- }
+ }
+ if (len) {
+ MultiplayerAPI::encode_and_compress_variants(p_arg, p_argcount, &packet_cache.write[ofs], len, &byte_only_or_no_args, multiplayer->is_object_decoding_allowed());
+ ofs += len;
}
ERR_FAIL_COND(command_type > 7);
@@ -461,19 +446,21 @@ void RPCManager::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const Mult
}
}
-void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+void SceneRPCInterface::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer();
ERR_FAIL_COND_MSG(!peer.is_valid(), "Trying to call an RPC while no multiplayer peer is active.");
- ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
+ Node *node = Object::cast_to<Node>(p_obj);
+ ERR_FAIL_COND(!node);
+ ERR_FAIL_COND_MSG(!node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
ERR_FAIL_COND_MSG(peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a multiplayer peer which is not connected.");
int node_id = peer->get_unique_id();
bool call_local_native = false;
bool call_local_script = false;
uint16_t rpc_id = UINT16_MAX;
- const Multiplayer::RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id);
+ const Multiplayer::RPCConfig config = _get_rpc_config(node, p_method, rpc_id);
ERR_FAIL_COND_MSG(config.name == StringName(),
- vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path()));
+ vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, node->get_path()));
if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
if (rpc_id & (1 << 15)) {
call_local_native = config.call_local;
@@ -484,21 +471,21 @@ void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, c
if (p_peer_id != node_id) {
#ifdef DEBUG_ENABLED
- _profile_node_data("out_rpc", p_node->get_instance_id());
+ _profile_node_data("out_rpc", node->get_instance_id());
#endif
- _send_rpc(p_node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
+ _send_rpc(node, p_peer_id, rpc_id, config, p_method, p_arg, p_argcount);
}
if (call_local_native) {
Callable::CallError ce;
multiplayer->set_remote_sender_override(peer->get_unique_id());
- p_node->call(p_method, p_arg, p_argcount, ce);
+ node->call(p_method, p_arg, p_argcount, ce);
multiplayer->set_remote_sender_override(0);
if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in local call: - " + error + ".";
ERR_PRINT(error);
return;
@@ -510,11 +497,11 @@ void RPCManager::rpcp(Node *p_node, int p_peer_id, const StringName &p_method, c
ce.error = Callable::CallError::CALL_OK;
multiplayer->set_remote_sender_override(peer->get_unique_id());
- p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
+ node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
multiplayer->set_remote_sender_override(0);
if (ce.error != Callable::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ String error = Variant::get_call_error_text(node, p_method, p_arg, p_argcount, ce);
error = "rpc() aborted in script local call: - " + error + ".";
ERR_PRINT(error);
return;
diff --git a/core/multiplayer/rpc_manager.h b/scene/multiplayer/scene_rpc_interface.h
index 00bd1f9cb0..86e1d0d280 100644
--- a/core/multiplayer/rpc_manager.h
+++ b/scene/multiplayer/scene_rpc_interface.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* rpc_manager.h */
+/* scene_rpc_interface.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,15 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef MULTIPLAYER_RPC_H
-#define MULTIPLAYER_RPC_H
+#ifndef SCENE_RPC_INTERFACE_H
+#define SCENE_RPC_INTERFACE_H
#include "core/multiplayer/multiplayer.h"
#include "core/multiplayer/multiplayer_api.h"
-#include "core/object/ref_counted.h"
-class RPCManager : public RefCounted {
- GDCLASS(RPCManager, RefCounted);
+class SceneRPCInterface : public MultiplayerRPCInterface {
+ GDCLASS(SceneRPCInterface, MultiplayerRPCInterface);
private:
enum NetworkNodeIdCompression {
@@ -71,6 +70,8 @@ private:
Vector<uint8_t> packet_cache;
protected:
+ static MultiplayerRPCInterface *_create(MultiplayerAPI *p_multiplayer);
+
_FORCE_INLINE_ void _profile_node_data(const String &p_what, ObjectID p_id);
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
@@ -78,12 +79,13 @@ protected:
Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len);
public:
- // Called by Node.rpc
- void rpcp(Node *p_node, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
- void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len);
+ static void make_default();
+
+ virtual void rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
+ virtual void process_rpc(int p_from, const uint8_t *p_packet, int p_packet_len) override;
+ virtual String get_rpc_md5(const Object *p_obj) const override;
- String get_rpc_md5(const Node *p_node);
- RPCManager(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
+ SceneRPCInterface(MultiplayerAPI *p_multiplayer) { multiplayer = p_multiplayer; }
};
-#endif // MULTIPLAYER_RPC_H
+#endif // SCENE_RPC_INTERFACE_H
diff --git a/scene/property_utils.cpp b/scene/property_utils.cpp
index 2540a633a9..a9b7e9acbe 100644
--- a/scene/property_utils.cpp
+++ b/scene/property_utils.cpp
@@ -130,7 +130,7 @@ Variant PropertyUtils::get_property_default_value(const Object *p_object, const
if (p != -1 && p < prop_str.length() - 1) {
bool all_digits = true;
for (int i = p + 1; i < prop_str.length(); i++) {
- if (prop_str[i] < '0' || prop_str[i] > '9') {
+ if (!is_digit(prop_str[i])) {
all_digits = false;
break;
}
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 838cef824b..87e9bd023b 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -134,6 +134,11 @@
#include "scene/main/timer.h"
#include "scene/main/viewport.h"
#include "scene/main/window.h"
+#include "scene/multiplayer/multiplayer_spawner.h"
+#include "scene/multiplayer/multiplayer_synchronizer.h"
+#include "scene/multiplayer/scene_cache_interface.h"
+#include "scene/multiplayer/scene_replication_interface.h"
+#include "scene/multiplayer/scene_rpc_interface.h"
#include "scene/resources/audio_stream_sample.h"
#include "scene/resources/bit_map.h"
#include "scene/resources/box_shape_3d.h"
@@ -301,6 +306,8 @@ void register_scene_types() {
GDREGISTER_CLASS(SubViewport);
GDREGISTER_CLASS(ViewportTexture);
GDREGISTER_CLASS(HTTPRequest);
+ GDREGISTER_CLASS(MultiplayerSpawner);
+ GDREGISTER_CLASS(MultiplayerSynchronizer);
GDREGISTER_CLASS(Timer);
GDREGISTER_CLASS(CanvasLayer);
GDREGISTER_CLASS(CanvasModulate);
@@ -463,7 +470,12 @@ void register_scene_types() {
GDREGISTER_CLASS(XROrigin3D);
GDREGISTER_CLASS(MeshInstance3D);
GDREGISTER_CLASS(OccluderInstance3D);
- GDREGISTER_CLASS(Occluder3D);
+ GDREGISTER_VIRTUAL_CLASS(Occluder3D);
+ GDREGISTER_CLASS(ArrayOccluder3D);
+ GDREGISTER_CLASS(QuadOccluder3D);
+ GDREGISTER_CLASS(BoxOccluder3D);
+ GDREGISTER_CLASS(SphereOccluder3D);
+ GDREGISTER_CLASS(PolygonOccluder3D);
GDREGISTER_VIRTUAL_CLASS(SpriteBase3D);
GDREGISTER_CLASS(Sprite3D);
GDREGISTER_CLASS(AnimatedSprite3D);
@@ -822,6 +834,8 @@ void register_scene_types() {
GDREGISTER_CLASS(Font);
GDREGISTER_CLASS(Curve);
+ GDREGISTER_CLASS(SceneReplicationConfig);
+
GDREGISTER_CLASS(TextLine);
GDREGISTER_CLASS(TextParagraph);
@@ -1050,6 +1064,9 @@ void register_scene_types() {
}
SceneDebugger::initialize();
+ SceneReplicationInterface::make_default();
+ SceneRPCInterface::make_default();
+ SceneCacheInterface::make_default();
NativeExtensionManager::get_singleton()->initialize_extensions(NativeExtension::INITIALIZATION_LEVEL_SCENE);
}
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 670b141080..c92a46a98c 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -175,7 +175,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, Ref<Te
theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
- theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 0.4));
theme->set_constant("hseparation", "Button", 2 * scale);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 1b81455d4c..c03faa2c2d 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -430,7 +430,7 @@ Error ResourceLoaderText::load() {
}
}
- if (path.find("://") == -1 && path.is_relative_path()) {
+ if (!path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -774,7 +774,7 @@ void ResourceLoaderText::get_dependencies(FileAccess *p_f, List<String> *p_depen
}
}
- if (!using_uid && path.find("://") == -1 && path.is_relative_path()) {
+ if (!using_uid && !path.contains("://") && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(local_path.get_base_dir().plus_file(path));
}
@@ -1951,7 +1951,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
- if (p_path.ends_with(".sct") && p_resource->get_class() != "PackedScene") {
+ if (p_path.ends_with(".tscn") && !Ref<PackedScene>(p_resource).is_valid()) {
return ERR_FILE_UNRECOGNIZED;
}
@@ -1960,14 +1960,14 @@ Error ResourceFormatSaverText::save(const String &p_path, const RES &p_resource,
}
bool ResourceFormatSaverText::recognize(const RES &p_resource) const {
- return true; // all recognized!
+ return true; // All resources recognized!
}
void ResourceFormatSaverText::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
- if (p_resource->get_class() == "PackedScene") {
- p_extensions->push_back("tscn"); //text scene
+ if (Ref<PackedScene>(p_resource).is_valid()) {
+ p_extensions->push_back("tscn"); // Text scene.
} else {
- p_extensions->push_back("tres"); //text resource
+ p_extensions->push_back("tres"); // Text resource.
}
}
diff --git a/scene/resources/scene_replication_config.cpp b/scene/resources/scene_replication_config.cpp
new file mode 100644
index 0000000000..2acc0f1922
--- /dev/null
+++ b/scene/resources/scene_replication_config.cpp
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* scene_replication_config.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene_replication_config.h"
+
+#include "core/multiplayer/multiplayer_api.h"
+#include "scene/main/node.h"
+
+bool SceneReplicationConfig::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+
+ if (name.begins_with("properties/")) {
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+
+ if (properties.size() == idx && what == "path") {
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::NODE_PATH, false);
+ NodePath path = p_value;
+ ERR_FAIL_COND_V(path.is_empty() || path.get_subname_count() == 0, false);
+ add_property(path);
+ return true;
+ }
+ ERR_FAIL_COND_V(p_value.get_type() != Variant::BOOL, false);
+ ERR_FAIL_INDEX_V(idx, properties.size(), false);
+ ReplicationProperty &prop = properties[idx];
+ if (what == "sync") {
+ prop.sync = p_value;
+ sync_props.push_back(prop.name);
+ return true;
+ } else if (what == "spawn") {
+ prop.spawn = p_value;
+ spawn_props.push_back(prop.name);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SceneReplicationConfig::_get(const StringName &p_name, Variant &r_ret) const {
+ String name = p_name;
+
+ if (name.begins_with("properties/")) {
+ int idx = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+ ERR_FAIL_INDEX_V(idx, properties.size(), false);
+ const ReplicationProperty &prop = properties[idx];
+ if (what == "path") {
+ r_ret = prop.name;
+ return true;
+ } else if (what == "sync") {
+ r_ret = prop.sync;
+ return true;
+ } else if (what == "spawn") {
+ r_ret = prop.spawn;
+ return true;
+ }
+ }
+ return false;
+}
+
+void SceneReplicationConfig::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < properties.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/spawn", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::STRING, "properties/" + itos(i) + "/sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
+ }
+}
+
+TypedArray<NodePath> SceneReplicationConfig::get_properties() const {
+ TypedArray<NodePath> paths;
+ for (const ReplicationProperty &prop : properties) {
+ paths.push_back(prop.name);
+ }
+ return paths;
+}
+
+void SceneReplicationConfig::add_property(const NodePath &p_path, int p_index) {
+ ERR_FAIL_COND(properties.find(p_path));
+
+ if (p_index < 0 || p_index == properties.size()) {
+ properties.push_back(ReplicationProperty(p_path));
+ return;
+ }
+
+ ERR_FAIL_INDEX(p_index, properties.size());
+
+ List<ReplicationProperty>::Element *I = properties.front();
+ int c = 0;
+ while (c < p_index) {
+ I = I->next();
+ c++;
+ }
+ properties.insert_before(I, ReplicationProperty(p_path));
+}
+
+void SceneReplicationConfig::remove_property(const NodePath &p_path) {
+ properties.erase(p_path);
+}
+
+int SceneReplicationConfig::property_get_index(const NodePath &p_path) const {
+ for (int i = 0; i < properties.size(); i++) {
+ if (properties[i].name == p_path) {
+ return i;
+ }
+ }
+ ERR_FAIL_V(-1);
+}
+
+bool SceneReplicationConfig::property_get_spawn(const NodePath &p_path) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND_V(!E, false);
+ return E->get().spawn;
+}
+
+void SceneReplicationConfig::property_set_spawn(const NodePath &p_path, bool p_enabled) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND(!E);
+ if (E->get().spawn == p_enabled) {
+ return;
+ }
+ E->get().spawn = p_enabled;
+ spawn_props.clear();
+ for (const ReplicationProperty &prop : properties) {
+ if (prop.spawn) {
+ spawn_props.push_back(p_path);
+ }
+ }
+}
+
+bool SceneReplicationConfig::property_get_sync(const NodePath &p_path) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND_V(!E, false);
+ return E->get().sync;
+}
+
+void SceneReplicationConfig::property_set_sync(const NodePath &p_path, bool p_enabled) {
+ List<ReplicationProperty>::Element *E = properties.find(p_path);
+ ERR_FAIL_COND(!E);
+ if (E->get().sync == p_enabled) {
+ return;
+ }
+ E->get().sync = p_enabled;
+ sync_props.clear();
+ for (const ReplicationProperty &prop : properties) {
+ if (prop.sync) {
+ sync_props.push_back(p_path);
+ }
+ }
+}
+
+void SceneReplicationConfig::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_properties"), &SceneReplicationConfig::get_properties);
+ ClassDB::bind_method(D_METHOD("add_property", "path", "index"), &SceneReplicationConfig::add_property, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_property", "path"), &SceneReplicationConfig::remove_property);
+ ClassDB::bind_method(D_METHOD("property_get_index", "path"), &SceneReplicationConfig::property_get_index);
+ ClassDB::bind_method(D_METHOD("property_get_spawn", "path"), &SceneReplicationConfig::property_get_spawn);
+ ClassDB::bind_method(D_METHOD("property_set_spawn", "path", "enabled"), &SceneReplicationConfig::property_set_spawn);
+ ClassDB::bind_method(D_METHOD("property_get_sync", "path"), &SceneReplicationConfig::property_get_sync);
+ ClassDB::bind_method(D_METHOD("property_set_sync", "path", "enabled"), &SceneReplicationConfig::property_set_sync);
+}
diff --git a/scene/resources/scene_replication_config.h b/scene/resources/scene_replication_config.h
new file mode 100644
index 0000000000..b791be9414
--- /dev/null
+++ b/scene/resources/scene_replication_config.h
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* scene_replication_config.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_REPLICATION_CONFIG_H
+#define SCENE_REPLICATION_CONFIG_H
+
+#include "core/io/resource.h"
+
+#include "core/variant/typed_array.h"
+
+class SceneReplicationConfig : public Resource {
+ GDCLASS(SceneReplicationConfig, Resource);
+ OBJ_SAVE_TYPE(SceneReplicationConfig);
+ RES_BASE_EXTENSION("repl");
+
+private:
+ struct ReplicationProperty {
+ NodePath name;
+ bool spawn = true;
+ bool sync = true;
+
+ bool operator==(const ReplicationProperty &p_to) {
+ return name == p_to.name;
+ }
+
+ ReplicationProperty() {}
+
+ ReplicationProperty(const NodePath &p_name) {
+ name = p_name;
+ }
+ };
+
+ List<ReplicationProperty> properties;
+ List<NodePath> spawn_props;
+ List<NodePath> sync_props;
+
+protected:
+ static void _bind_methods();
+
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+public:
+ TypedArray<NodePath> get_properties() const;
+
+ void add_property(const NodePath &p_path, int p_index = -1);
+ void remove_property(const NodePath &p_path);
+
+ int property_get_index(const NodePath &p_path) const;
+ bool property_get_spawn(const NodePath &p_path);
+ void property_set_spawn(const NodePath &p_path, bool p_enabled);
+
+ bool property_get_sync(const NodePath &p_path);
+ void property_set_sync(const NodePath &p_path, bool p_enabled);
+
+ const List<NodePath> &get_spawn_properties() { return spawn_props; }
+ const List<NodePath> &get_sync_properties() { return sync_props; }
+
+ SceneReplicationConfig() {}
+};
+
+#endif // SCENE_REPLICATION_CONFIG_H
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 0677d9c1a8..52151ae846 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -37,6 +37,44 @@ SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr;
SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr;
+SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr;
+SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr;
+SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr;
+
+void SurfaceTool::strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) {
+ ERR_FAIL_COND_MSG(!generate_remap_func || !remap_vertex_func || !remap_index_func, "Meshoptimizer library is not initialized.");
+
+ Vector<uint32_t> remap;
+ remap.resize(r_vertices.size());
+ uint32_t new_vertex_count = generate_remap_func(remap.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), (float *)r_vertices.ptr(), r_vertices.size(), sizeof(Vector3));
+ remap_vertex_func(r_vertices.ptrw(), r_vertices.ptr(), r_vertices.size(), sizeof(Vector3), remap.ptr());
+ r_vertices.resize(new_vertex_count);
+ remap_index_func((unsigned int *)r_indices.ptrw(), (unsigned int *)r_indices.ptr(), r_indices.size(), remap.ptr());
+
+ HashMap<const int *, bool, TriangleHasher, TriangleHasher> found_triangles;
+ int *idx_ptr = r_indices.ptrw();
+
+ int filtered_indices_count = 0;
+ for (int i = 0; i < r_indices.size() / 3; i++) {
+ const int *tri = idx_ptr + (i * 3);
+
+ if (tri[0] == tri[1] || tri[1] == tri[2] || tri[2] == tri[0]) {
+ continue;
+ }
+
+ if (found_triangles.has(tri)) {
+ continue;
+ }
+
+ if (i != filtered_indices_count) {
+ memcpy(idx_ptr + (filtered_indices_count * 3), tri, sizeof(int) * 3);
+ }
+
+ found_triangles[tri] = true;
+ filtered_indices_count++;
+ }
+ r_indices.resize(filtered_indices_count * 3);
+}
bool SurfaceTool::Vertex::operator==(const Vertex &p_vertex) const {
if (vertex != p_vertex.vertex) {
@@ -107,6 +145,47 @@ uint32_t SurfaceTool::VertexHasher::hash(const Vertex &p_vtx) {
return h;
}
+uint32_t SurfaceTool::TriangleHasher::hash(const int *p_triangle) {
+ int t0 = p_triangle[0];
+ int t1 = p_triangle[1];
+ int t2 = p_triangle[2];
+
+ if (t0 > t1)
+ SWAP(t0, t1);
+ if (t1 > t2)
+ SWAP(t1, t2);
+ if (t0 > t1)
+ SWAP(t0, t1);
+
+ return (t0 * 73856093) ^ (t1 * 19349663) ^ (t2 * 83492791);
+}
+
+bool SurfaceTool::TriangleHasher::compare(const int *p_lhs, const int *p_rhs) {
+ int r0 = p_rhs[0];
+ int r1 = p_rhs[1];
+ int r2 = p_rhs[2];
+
+ if (r0 > r1)
+ SWAP(r0, r1);
+ if (r1 > r2)
+ SWAP(r1, r2);
+ if (r0 > r1)
+ SWAP(r0, r1);
+
+ int l0 = p_lhs[0];
+ int l1 = p_lhs[1];
+ int l2 = p_lhs[2];
+
+ if (l0 > l1)
+ SWAP(l0, l1);
+ if (l1 > l2)
+ SWAP(l1, l2);
+ if (l0 > l1)
+ SWAP(l0, l1);
+
+ return l0 == r0 && l1 == r1 && l2 == r2;
+}
+
void SurfaceTool::begin(Mesh::PrimitiveType p_primitive) {
clear();
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 9cb83e0e68..bf4332ad2a 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -84,12 +84,24 @@ public:
static SimplifyScaleFunc simplify_scale_func;
typedef size_t (*SimplifySloppyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float *out_result_error);
static SimplifySloppyFunc simplify_sloppy_func;
+ typedef size_t (*GenerateRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const void *vertices, size_t vertex_count, size_t vertex_size);
+ static GenerateRemapFunc generate_remap_func;
+ typedef void (*RemapVertexFunc)(void *destination, const void *vertices, size_t vertex_count, size_t vertex_size, const unsigned int *remap);
+ static RemapVertexFunc remap_vertex_func;
+ typedef void (*RemapIndexFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const unsigned int *remap);
+ static RemapIndexFunc remap_index_func;
+ static void strip_mesh_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices);
private:
struct VertexHasher {
static _FORCE_INLINE_ uint32_t hash(const Vertex &p_vtx);
};
+ struct TriangleHasher {
+ static _FORCE_INLINE_ uint32_t hash(const int *p_triangle);
+ static _FORCE_INLINE_ bool compare(const int *p_lhs, const int *p_rhs);
+ };
+
struct WeightSort {
int index = 0;
float weight = 0.0;
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index e0aa21ac37..f1eddd8ffc 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -116,14 +116,6 @@ void SyntaxHighlighter::_bind_methods() {
////////////////////////////////////////////////////////////////////////////////
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
-static bool _is_hex_symbol(char32_t c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
@@ -166,7 +158,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
color = font_color;
bool is_char = !is_symbol(str[j]);
bool is_a_symbol = is_symbol(str[j]);
- bool is_number = (str[j] >= '0' && str[j] <= '9');
+ bool is_number = is_digit(str[j]);
/* color regions */
if (is_a_symbol || in_region != -1) {
@@ -304,7 +296,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
// Allow ABCDEF in hex notation.
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ if (is_hex_notation && (is_hex_digit(str[j]) || is_number)) {
is_number = true;
} else {
is_hex_notation = false;
@@ -321,7 +313,7 @@ Dictionary CodeHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
}
}
- if (!in_word && _is_char(str[j]) && !is_number) {
+ if (!in_word && (is_ascii_char(str[j]) || is_underscore(str[j])) && !is_number) {
in_word = true;
}
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index ee66a61da8..0ee0e4b33e 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1902,7 +1902,7 @@ void GradientTexture2D::_queue_update() {
return;
}
update_pending = true;
- call_deferred("_update");
+ call_deferred(SNAME("_update"));
}
void GradientTexture2D::_update() {
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 8da287042e..f962e55666 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -46,7 +46,7 @@ int Theme::fallback_font_size = 16;
bool Theme::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
- if (sname.find("/") != -1) {
+ if (sname.contains("/")) {
String type = sname.get_slicec('/', 1);
String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
@@ -78,7 +78,7 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) {
bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
String sname = p_name;
- if (sname.find("/") != -1) {
+ if (sname.contains("/")) {
String type = sname.get_slicec('/', 1);
String theme_type = sname.get_slicec('/', 0);
String name = sname.get_slicec('/', 2);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 2854cbe30d..1e84947b87 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -4410,7 +4410,7 @@ void TileSetAtlasSource::_clear_tiles_outside_texture() {
void TileSetAtlasSource::_queue_update_padded_texture() {
padded_texture_needs_update = true;
- call_deferred("_update_padded_texture");
+ call_deferred(SNAME("_update_padded_texture"));
}
void TileSetAtlasSource::_update_padded_texture() {
@@ -4434,7 +4434,7 @@ void TileSetAtlasSource::_update_padded_texture() {
Ref<Image> image;
image.instantiate();
- image->create(size.x, size.y, false, Image::FORMAT_RGBA8);
+ image->create(size.x, size.y, false, src->get_format());
for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) {
for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) {
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index f41058d59c..dae61c8609 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -131,7 +131,7 @@ void VisualShaderNode::set_input_port_default_value(int p_port, const Variant &p
} break;
case Variant::VECTOR2: {
Vector2 pv = p_prev_value;
- value = Vector3(pv.x, pv.y, 0.0);
+ value = Vector3(pv.x, pv.y, pv.y);
} break;
case Variant::VECTOR3: {
value = p_prev_value;
@@ -250,7 +250,7 @@ int VisualShaderNode::get_expanded_output_port_count() const {
case PORT_TYPE_VECTOR_2D: {
count2 += 2;
} break;
- case PORT_TYPE_VECTOR: {
+ case PORT_TYPE_VECTOR_3D: {
count2 += 3;
} break;
default:
@@ -289,7 +289,7 @@ String VisualShaderNode::generate_global(Shader::Mode p_mode, VisualShader::Type
return String();
}
-String VisualShaderNode::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNode::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
return String();
}
@@ -359,7 +359,7 @@ void VisualShaderNode::_bind_methods() {
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR_INT);
BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_2D);
- BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(PORT_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(PORT_TYPE_BOOLEAN);
BIND_ENUM_CONSTANT(PORT_TYPE_TRANSFORM);
BIND_ENUM_CONSTANT(PORT_TYPE_SAMPLER);
@@ -472,7 +472,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
return code;
}
-String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String ret;
if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) {
String code = "// " + get_caption() + "\n";
@@ -1088,7 +1088,7 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
code += " COLOR.rgb = vec3(n_out" + itos(p_node) + "p" + itos(p_port) + ", 0.0);\n";
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
code += " COLOR.rgb = n_out" + itos(p_node) + "p" + itos(p_port) + ";\n";
} break;
default: {
@@ -1106,10 +1106,6 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
return final_code;
}
-#define IS_INITIAL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z'))
-
-#define IS_SYMBOL_CHAR(m_d) (((m_d) >= 'a' && (m_d) <= 'z') || ((m_d) >= 'A' && (m_d) <= 'Z') || ((m_d) >= '0' && (m_d) <= '9') || (m_d) == '_')
-
String VisualShader::validate_port_name(const String &p_port_name, VisualShaderNode *p_node, int p_port_id, bool p_output) const {
String name = p_port_name;
@@ -1117,7 +1113,7 @@ String VisualShader::validate_port_name(const String &p_port_name, VisualShaderN
return String();
}
- while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ while (name.length() && !is_ascii_char(name[0])) {
name = name.substr(1, name.length() - 1);
}
@@ -1125,7 +1121,7 @@ String VisualShader::validate_port_name(const String &p_port_name, VisualShaderN
String valid_name;
for (int i = 0; i < name.length(); i++) {
- if (IS_SYMBOL_CHAR(name[i])) {
+ if (is_ascii_identifier_char(name[i])) {
valid_name += String::chr(name[i]);
} else if (name[i] == ' ') {
valid_name += "_";
@@ -1162,14 +1158,14 @@ String VisualShader::validate_port_name(const String &p_port_name, VisualShaderN
String VisualShader::validate_uniform_name(const String &p_name, const Ref<VisualShaderNodeUniform> &p_uniform) const {
String name = p_name; //validate name first
- while (name.length() && !IS_INITIAL_CHAR(name[0])) {
+ while (name.length() && !is_ascii_char(name[0])) {
name = name.substr(1, name.length() - 1);
}
if (!name.is_empty()) {
String valid_name;
for (int i = 0; i < name.length(); i++) {
- if (IS_SYMBOL_CHAR(name[i])) {
+ if (is_ascii_identifier_char(name[i])) {
valid_name += String::chr(name[i]);
} else if (name[i] == ' ') {
valid_name += "_";
@@ -1206,7 +1202,7 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
if (exists) {
//remove numbers, put new and try again
attempt++;
- while (name.length() && name[name.length() - 1] >= '0' && name[name.length() - 1] <= '9') {
+ while (name.length() && is_digit(name[name.length() - 1])) {
name = name.substr(0, name.length() - 1);
}
ERR_FAIL_COND_V(name.is_empty(), String());
@@ -1489,7 +1485,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
class_name = vsnode->get_script_instance()->get_script()->get_path();
}
if (!r_classes.has(class_name)) {
- global_code_per_node += vsnode->generate_global_per_node(get_mode(), type, node);
+ global_code_per_node += vsnode->generate_global_per_node(get_mode(), node);
for (int i = 0; i < TYPE_MAX; i++) {
global_code_per_func[Type(i)] += vsnode->generate_global_per_func(get_mode(), Type(i), node);
}
@@ -1553,7 +1549,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
inputs[i] = "dot(" + src_var + ", vec2(0.333333, 0.333333))";
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
inputs[i] = "dot(" + src_var + ", vec3(0.333333, 0.333333, 0.333333))";
} break;
default:
@@ -1571,7 +1567,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
inputs[i] = "dot(float(" + src_var + "), vec2(0.333333, 0.333333))";
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
inputs[i] = "dot(float(" + src_var + "), vec3(0.333333, 0.333333, 0.333333))";
} break;
default:
@@ -1589,7 +1585,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
inputs[i] = "all(bvec2(" + src_var + "))";
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
inputs[i] = "all(bvec3(" + src_var + "))";
} break;
default:
@@ -1607,7 +1603,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_BOOLEAN: {
inputs[i] = "vec2(" + src_var + " ? 1.0 : 0.0)";
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
inputs[i] = "vec2(" + src_var + ".xy)";
} break;
default:
@@ -1615,7 +1611,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
switch (out_type) {
case VisualShaderNode::PORT_TYPE_SCALAR: {
inputs[i] = "vec3(" + src_var + ")";
@@ -1699,7 +1695,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
output_count += 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
output_count += 3;
} break;
default:
@@ -1726,7 +1722,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D:
outputs[i] = "vec2 " + var_name;
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
outputs[i] = "vec3 " + var_name;
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
@@ -1743,7 +1739,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
j += 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
j += 3;
} break;
default:
@@ -1765,7 +1761,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D:
code += " vec2 " + outputs[i] + ";\n";
break;
- case VisualShaderNode::PORT_TYPE_VECTOR:
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D:
code += " vec3 " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
@@ -1782,7 +1778,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
case VisualShaderNode::PORT_TYPE_VECTOR_2D: {
j += 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
j += 3;
} break;
default:
@@ -1826,7 +1822,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
i += 2;
} break;
- case VisualShaderNode::PORT_TYPE_VECTOR: {
+ case VisualShaderNode::PORT_TYPE_VECTOR_3D: {
if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
if (!new_line_inserted) {
code += "\n";
@@ -2150,7 +2146,11 @@ void VisualShader::_update_shader() const {
global_compute_code += " return __rand_from_seed(seed) * (to - from) + from;\n";
global_compute_code += "}\n\n";
- global_compute_code += "vec3 __randv_range(inout uint seed, vec3 from, vec3 to) {\n";
+ global_compute_code += "vec2 __randv2_range(inout uint seed, vec2 from, vec2 to) {\n";
+ global_compute_code += " return vec2(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y));\n";
+ global_compute_code += "}\n\n";
+
+ global_compute_code += "vec3 __randv3_range(inout uint seed, vec3 from, vec3 to) {\n";
global_compute_code += " return vec3(__randf_range(seed, from.x, to.x), __randf_range(seed, from.y, to.y), __randf_range(seed, from.z, to.z));\n";
global_compute_code += "}\n\n";
@@ -2326,17 +2326,17 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Node3D
// Node3D, Vertex
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR_INT, "instance_id", "INSTANCE_ID" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
@@ -2350,15 +2350,15 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "output_is_srgb", "OUTPUT_IS_SRGB" },
// Node3D, Fragment
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
@@ -2376,18 +2376,18 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "depth_texture", "DEPTH_TEXTURE" },
// Node3D, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "view", "VIEW" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "view", "VIEW" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "attenuation", "ATTENUATION" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_TRANSFORM, "world", "WORLD_MATRIX" },
@@ -2404,7 +2404,7 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Canvas Item, Vertex
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
@@ -2413,13 +2413,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "screen", "SCREEN_MATRIX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_light_pass", "AT_LIGHT_PASS" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "instance_custom", "INSTANCE_CUSTOM.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "instance_custom", "INSTANCE_CUSTOM.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "instance_custom_alpha", "INSTANCE_CUSTOM.a" },
// Canvas Item, Fragment
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
@@ -2430,41 +2430,41 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "normal_texture", "NORMAL_TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "screen_texture", "SCREEN_TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SAMPLER, "specular_shininess_texture", "SPECULAR_SHININESS_TEXTURE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
// Canvas Item, Light
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.xyz" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.xyz" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_color", "LIGHT_COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_color", "LIGHT_COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_color_alpha", "LIGHT_COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_position", "LIGHT_POSITION" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "shadow", "SHADOW_MODULATE.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_position", "LIGHT_POSITION" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "shadow", "SHADOW_MODULATE.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "shadow_alpha", "SHADOW_MODULATE.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "texture_pixel_size", "TEXTURE_PIXEL_SIZE" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "point_coord", "POINT_COORD" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SAMPLER, "texture", "TEXTURE" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular_shininess", "SPECULAR_SHININESS.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular_shininess", "SPECULAR_SHININESS.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "specular_shininess_alpha", "SPECULAR_SHININESS.a" },
// Particles, Start
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
@@ -2474,13 +2474,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Start (Custom)
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
@@ -2490,13 +2490,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
@@ -2506,13 +2506,13 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Process (Custom)
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
@@ -2522,15 +2522,15 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Particles, Collide
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "attractor_force", "ATTRACTOR_FORCE" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "attractor_force", "ATTRACTOR_FORCE" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "collision_depth", "COLLISION_DEPTH" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "collision_normal", "COLLISION_NORMAL" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "collision_normal", "COLLISION_NORMAL" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "velocity", "VELOCITY" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "velocity", "VELOCITY" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "restart", "RESTART" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_BOOLEAN, "active", "ACTIVE" },
- { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR, "custom", "CUSTOM.rgb" },
+ { Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_VECTOR_3D, "custom", "CUSTOM.rgb" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "custom_alpha", "CUSTOM.a" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_TRANSFORM, "transform", "TRANSFORM" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "delta", "DELTA" },
@@ -2543,27 +2543,27 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_cubemap_pass", "AT_CUBEMAP_PASS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_half_res_pass", "AT_HALF_RES_PASS" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "at_quarter_res_pass", "AT_QUARTER_RES_PASS" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "eyedir", "EYEDIR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "half_res_color", "HALF_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "eyedir", "EYEDIR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "half_res_color", "HALF_RES_COLOR.rgb" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "half_res_alpha", "HALF_RES_COLOR.a" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_color", "LIGHT0_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light0_direction", "LIGHT0_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_color", "LIGHT0_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light0_direction", "LIGHT0_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light0_enabled", "LIGHT0_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light0_energy", "LIGHT0_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_color", "LIGHT1_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light1_direction", "LIGHT1_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light1_color", "LIGHT1_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light1_direction", "LIGHT1_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light1_enabled", "LIGHT1_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light1_energy", "LIGHT1_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_color", "LIGHT2_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light2_direction", "LIGHT2_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light2_color", "LIGHT2_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light2_direction", "LIGHT2_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light2_enabled", "LIGHT2_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light2_energy", "LIGHT2_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_color", "LIGHT3_COLOR" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "light3_direction", "LIGHT3_DIRECTION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light3_color", "LIGHT3_COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light3_direction", "LIGHT3_DIRECTION" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_BOOLEAN, "light3_enabled", "LIGHT3_ENABLED" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "light3_energy", "LIGHT3_ENERGY" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "position", "POSITION" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "position", "POSITION" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "quarter_res_color", "QUARTER_RES_COLOR.rgb" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "quarter_res_alpha", "QUARTER_RES_COLOR.a" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SAMPLER, "radiance", "RADIANCE" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
@@ -2572,10 +2572,10 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
// Fog, Fog
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "world_position", "WORLD_POSITION" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "object_position", "OBJECT_POSITION" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "uvw", "UVW" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "extents", "EXTENTS" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "world_position", "WORLD_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "object_position", "OBJECT_POSITION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "uvw", "UVW" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "extents", "EXTENTS" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "sdf", "SDF" },
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
@@ -2585,25 +2585,25 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Spatial, Vertex
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Fragment
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "vec3(1.0, 0.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
@@ -2611,8 +2611,8 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
// Spatial, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "viewport_size", "vec2(1.0)" },
@@ -2622,25 +2622,25 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Fragment
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Canvas Item, Light
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "screen_uv", "SCREEN_UV" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
@@ -2722,7 +2722,7 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_VECTOR_2D: {
code = " " + p_output_vars[0] + " = vec2(0.0);\n";
} break;
- case PORT_TYPE_VECTOR: {
+ case PORT_TYPE_VECTOR_3D: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
case PORT_TYPE_BOOLEAN: {
@@ -2964,12 +2964,12 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_output_port
case UniformType::UNIFORM_TYPE_VECTOR2:
return PortType::PORT_TYPE_VECTOR_2D;
case UniformType::UNIFORM_TYPE_VECTOR3:
- return PortType::PORT_TYPE_VECTOR;
+ return PortType::PORT_TYPE_VECTOR_3D;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PortType::PORT_TYPE_TRANSFORM;
case UniformType::UNIFORM_TYPE_COLOR:
if (p_port == 0) {
- return PortType::PORT_TYPE_VECTOR;
+ return PortType::PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
}
@@ -3065,11 +3065,11 @@ VisualShaderNodeUniformRef::PortType VisualShaderNodeUniformRef::get_port_type_b
case UniformType::UNIFORM_TYPE_VECTOR2:
return PORT_TYPE_VECTOR_2D;
case UniformType::UNIFORM_TYPE_VECTOR3:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case UniformType::UNIFORM_TYPE_TRANSFORM:
return PORT_TYPE_TRANSFORM;
case UniformType::UNIFORM_TYPE_COLOR:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3135,29 +3135,29 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
// Node3D, Vertex.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "VERTEX" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "TANGENT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "BINORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "vertex", "VERTEX" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "tangent", "TANGENT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "binormal", "BINORMAL" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv2", "UV2" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
////////////////////////////////////////////////////////////////////////
// Node3D, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "metallic", "METALLIC" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "specular", "SPECULAR" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao", "AO" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "rim", "RIM" },
@@ -3167,15 +3167,15 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "anisotropy", "ANISOTROPY" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "anisotropy_flow", "ANISOTROPY_FLOW" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "subsurf_scatter", "SSS_STRENGTH" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "backlight", "BACKLIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "backlight", "BACKLIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha_scissor_threshold", "ALPHA_SCISSOR_THRESHOLD" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "ao_light_affect", "AO_LIGHT_AFFECT" },
////////////////////////////////////////////////////////////////////////
// Node3D, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "diffuse", "DIFFUSE_LIGHT" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "specular", "SPECULAR_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "diffuse", "DIFFUSE_LIGHT" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "specular", "SPECULAR_LIGHT" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
////////////////////////////////////////////////////////////////////////
@@ -3185,39 +3185,39 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "vertex", "VERTEX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_2D, "uv", "UV" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "point_size", "POINT_SIZE" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Fragment.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "NORMAL" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal_map", "NORMAL_MAP" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal", "NORMAL" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "normal_map", "NORMAL_MAP" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "normal_map_depth", "NORMAL_MAP_DEPTH" },
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "light_vertex", "LIGHT_VERTEX" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light_vertex", "LIGHT_VERTEX" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR_2D, "shadow_vertex", "SHADOW_VERTEX" },
////////////////////////////////////////////////////////////////////////
// Canvas Item, Light.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "light", "LIGHT.rgb" },
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR_3D, "light", "LIGHT.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "light_alpha", "LIGHT.a" },
////////////////////////////////////////////////////////////////////////
// Sky, Sky.
////////////////////////////////////////////////////////////////////////
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "color", "COLOR" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "ALPHA" },
- { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "fog", "FOG.rgb" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR_3D, "fog", "FOG.rgb" },
{ Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "fog_alpha", "FOG.a" },
////////////////////////////////////////////////////////////////////////
// Fog, Fog.
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_SCALAR, "density", "DENSITY" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
- { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR, "emission", "EMISSION" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "albedo", "ALBEDO" },
+ { Shader::MODE_FOG, VisualShader::TYPE_FOG, VisualShaderNode::PORT_TYPE_VECTOR_3D, "emission", "EMISSION" },
////////////////////////////////////////////////////////////////////////
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
@@ -3312,7 +3312,7 @@ String VisualShaderNodeOutput::generate_code(Shader::Mode p_mode, VisualShader::
if (ports[idx].mode == shader_mode && ports[idx].shader_type == shader_type) {
if (!p_input_vars[count].is_empty()) {
String s = ports[idx].string;
- if (s.find(":") != -1) {
+ if (s.contains(":")) {
code += " " + s.get_slicec(':', 0) + " = " + p_input_vars[count] + "." + s.get_slicec(':', 1) + ";\n";
} else {
code += " " + s + " = " + p_input_vars[count] + ";\n";
@@ -4151,7 +4151,7 @@ String VisualShaderNodeExpression::generate_code(Shader::Mode p_mode, VisualShad
case PORT_TYPE_VECTOR_2D:
tk = "vec2(0.0, 0.0)";
break;
- case PORT_TYPE_VECTOR:
+ case PORT_TYPE_VECTOR_3D:
tk = "vec3(0.0, 0.0, 0.0)";
break;
case PORT_TYPE_BOOLEAN:
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 9f877e08a5..d3b5365893 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -215,7 +215,7 @@ public:
PORT_TYPE_SCALAR,
PORT_TYPE_SCALAR_INT,
PORT_TYPE_VECTOR_2D,
- PORT_TYPE_VECTOR,
+ PORT_TYPE_VECTOR_3D,
PORT_TYPE_BOOLEAN,
PORT_TYPE_TRANSFORM,
PORT_TYPE_SAMPLER,
@@ -275,7 +275,7 @@ public:
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const;
// If no output is connected, the output var passed will be empty. If no input is connected and input is NIL, the input var passed will be empty.
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const = 0;
@@ -335,7 +335,7 @@ protected:
void _set_input_port_default_value(int p_port, const Variant &p_value);
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
static void _bind_methods();
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 9a714fd3dd..f2479199ee 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -37,7 +37,7 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_input_port_
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -49,7 +49,7 @@ VisualShaderNodeVectorBase::PortType VisualShaderNodeVectorBase::get_output_port
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -291,7 +291,7 @@ int VisualShaderNodeColorConstant::get_input_port_count() const {
}
VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorConstant::get_input_port_name(int p_port) const {
@@ -303,7 +303,7 @@ int VisualShaderNodeColorConstant::get_output_port_count() const {
}
VisualShaderNodeColorConstant::PortType VisualShaderNodeColorConstant::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
@@ -426,7 +426,7 @@ int VisualShaderNodeVec3Constant::get_input_port_count() const {
}
VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Constant::get_input_port_name(int p_port) const {
@@ -438,7 +438,7 @@ int VisualShaderNodeVec3Constant::get_output_port_count() const {
}
VisualShaderNodeVec3Constant::PortType VisualShaderNodeVec3Constant::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Constant::get_output_port_name(int p_port) const {
@@ -488,7 +488,7 @@ int VisualShaderNodeTransformConstant::get_input_port_count() const {
}
VisualShaderNodeTransformConstant::PortType VisualShaderNodeTransformConstant::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformConstant::get_input_port_name(int p_port) const {
@@ -591,7 +591,7 @@ VisualShaderNodeTexture::PortType VisualShaderNodeTexture::get_output_port_type(
if (p_port == 0 && source == SOURCE_DEPTH) {
return PORT_TYPE_SCALAR;
}
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
@@ -1064,7 +1064,7 @@ int VisualShaderNodeCurveXYZTexture::get_output_port_count() const {
}
VisualShaderNodeCurveXYZTexture::PortType VisualShaderNodeCurveXYZTexture::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeCurveXYZTexture::get_output_port_name(int p_port) const {
@@ -1135,7 +1135,7 @@ int VisualShaderNodeSample3D::get_input_port_count() const {
VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -1161,7 +1161,7 @@ int VisualShaderNodeSample3D::get_output_port_count() const {
}
VisualShaderNodeSample3D::PortType VisualShaderNodeSample3D::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeSample3D::get_output_port_name(int p_port) const {
@@ -1398,7 +1398,7 @@ int VisualShaderNodeCubemap::get_input_port_count() const {
VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -1426,7 +1426,7 @@ int VisualShaderNodeCubemap::get_output_port_count() const {
}
VisualShaderNodeCubemap::PortType VisualShaderNodeCubemap::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeCubemap::get_output_port_name(int p_port) const {
@@ -2020,7 +2020,7 @@ int VisualShaderNodeColorOp::get_input_port_count() const {
}
VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorOp::get_input_port_name(int p_port) const {
@@ -2032,7 +2032,7 @@ int VisualShaderNodeColorOp::get_output_port_count() const {
}
VisualShaderNodeColorOp::PortType VisualShaderNodeColorOp::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorOp::get_output_port_name(int p_port) const {
@@ -2293,7 +2293,7 @@ int VisualShaderNodeTransformVecMult::get_input_port_count() const {
}
VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_input_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR;
+ return p_port == 0 ? PORT_TYPE_TRANSFORM : PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformVecMult::get_input_port_name(int p_port) const {
@@ -2305,7 +2305,7 @@ int VisualShaderNodeTransformVecMult::get_output_port_count() const {
}
VisualShaderNodeTransformVecMult::PortType VisualShaderNodeTransformVecMult::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformVecMult::get_output_port_name(int p_port) const {
@@ -2815,7 +2815,7 @@ int VisualShaderNodeColorFunc::get_input_port_count() const {
}
VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorFunc::get_input_port_name(int p_port) const {
@@ -2827,7 +2827,7 @@ int VisualShaderNodeColorFunc::get_output_port_count() const {
}
VisualShaderNodeColorFunc::PortType VisualShaderNodeColorFunc::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorFunc::get_output_port_name(int p_port) const {
@@ -3124,7 +3124,7 @@ int VisualShaderNodeDotProduct::get_input_port_count() const {
}
VisualShaderNodeDotProduct::PortType VisualShaderNodeDotProduct::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeDotProduct::get_input_port_name(int p_port) const {
@@ -3258,7 +3258,7 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_inp
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3278,7 +3278,7 @@ VisualShaderNodeDerivativeFunc::PortType VisualShaderNodeDerivativeFunc::get_out
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3389,7 +3389,7 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_input_port_type(int p
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3418,7 +3418,7 @@ VisualShaderNodeClamp::PortType VisualShaderNodeClamp::get_output_port_type(int
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3570,7 +3570,7 @@ int VisualShaderNodeOuterProduct::get_input_port_count() const {
}
VisualShaderNodeOuterProduct::PortType VisualShaderNodeOuterProduct::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeOuterProduct::get_input_port_name(int p_port) const {
@@ -3625,10 +3625,10 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_input_port_type(int p_p
}
break;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
break;
default:
@@ -3658,9 +3658,9 @@ VisualShaderNodeStep::PortType VisualShaderNodeStep::get_output_port_type(int p_
case OP_TYPE_VECTOR_2D_SCALAR:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3757,10 +3757,10 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_input_port_
}
break;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 2) {
- return PORT_TYPE_VECTOR; // x
+ return PORT_TYPE_VECTOR_3D; // x
}
break;
default:
@@ -3792,9 +3792,9 @@ VisualShaderNodeSmoothStep::PortType VisualShaderNodeSmoothStep::get_output_port
case OP_TYPE_VECTOR_2D_SCALAR:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -3954,7 +3954,7 @@ VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_input
return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVectorRefract::get_input_port_name(int p_port) const {
@@ -3974,7 +3974,7 @@ int VisualShaderNodeVectorRefract::get_output_port_count() const {
}
VisualShaderNodeVectorRefract::PortType VisualShaderNodeVectorRefract::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVectorRefract::get_output_port_name(int p_port) const {
@@ -4011,12 +4011,12 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_input_port_type(int p_por
}
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
if (p_port == 2) {
break;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -4044,9 +4044,9 @@ VisualShaderNodeMix::PortType VisualShaderNodeMix::get_output_port_type(int p_po
case OP_TYPE_VECTOR_2D_SCALAR:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_VECTOR_3D_SCALAR:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -4245,7 +4245,7 @@ int VisualShaderNodeTransformCompose::get_input_port_count() const {
}
VisualShaderNodeTransformCompose::PortType VisualShaderNodeTransformCompose::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformCompose::get_input_port_name(int p_port) const {
@@ -4402,7 +4402,7 @@ int VisualShaderNodeTransformDecompose::get_output_port_count() const {
}
VisualShaderNodeTransformDecompose::PortType VisualShaderNodeTransformDecompose::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformDecompose::get_output_port_name(int p_port) const {
@@ -4933,7 +4933,7 @@ int VisualShaderNodeColorUniform::get_input_port_count() const {
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeColorUniform::get_input_port_name(int p_port) const {
@@ -4945,7 +4945,7 @@ int VisualShaderNodeColorUniform::get_output_port_count() const {
}
VisualShaderNodeColorUniform::PortType VisualShaderNodeColorUniform::get_output_port_type(int p_port) const {
- return p_port == 0 ? PORT_TYPE_VECTOR : PORT_TYPE_SCALAR;
+ return p_port == 0 ? PORT_TYPE_VECTOR_3D : PORT_TYPE_SCALAR;
}
String VisualShaderNodeColorUniform::get_output_port_name(int p_port) const {
@@ -5137,7 +5137,7 @@ int VisualShaderNodeVec3Uniform::get_input_port_count() const {
}
VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Uniform::get_input_port_name(int p_port) const {
@@ -5149,7 +5149,7 @@ int VisualShaderNodeVec3Uniform::get_output_port_count() const {
}
VisualShaderNodeVec3Uniform::PortType VisualShaderNodeVec3Uniform::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeVec3Uniform::get_output_port_name(int p_port) const {
@@ -5237,7 +5237,7 @@ int VisualShaderNodeTransformUniform::get_input_port_count() const {
}
VisualShaderNodeTransformUniform::PortType VisualShaderNodeTransformUniform::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeTransformUniform::get_input_port_name(int p_port) const {
@@ -5358,7 +5358,7 @@ int VisualShaderNodeTextureUniform::get_output_port_count() const {
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniform::get_output_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
return PORT_TYPE_SCALAR;
case 2:
@@ -5680,7 +5680,7 @@ int VisualShaderNodeTextureUniformTriplanar::get_input_port_count() const {
VisualShaderNodeTextureUniform::PortType VisualShaderNodeTextureUniformTriplanar::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
@@ -5694,7 +5694,7 @@ String VisualShaderNodeTextureUniformTriplanar::get_input_port_name(int p_port)
return "";
}
-String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeTextureUniformTriplanar::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "// TRIPLANAR FUNCTION GLOBAL CODE\n";
@@ -5996,7 +5996,7 @@ VisualShaderNodeIf::PortType VisualShaderNodeIf::get_input_port_type(int p_port)
if (p_port == 0 || p_port == 1 || p_port == 2) {
return PORT_TYPE_SCALAR;
}
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeIf::get_input_port_name(int p_port) const {
@@ -6023,7 +6023,7 @@ int VisualShaderNodeIf::get_output_port_count() const {
}
VisualShaderNodeIf::PortType VisualShaderNodeIf::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeIf::get_output_port_name(int p_port) const {
@@ -6078,7 +6078,7 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_input_port_type(int
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case OP_TYPE_TRANSFORM:
@@ -6114,7 +6114,7 @@ VisualShaderNodeSwitch::PortType VisualShaderNodeSwitch::get_output_port_type(in
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case OP_TYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case OP_TYPE_TRANSFORM:
@@ -6224,15 +6224,15 @@ int VisualShaderNodeFresnel::get_input_port_count() const {
VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_input_port_type(int p_port) const {
switch (p_port) {
case 0:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 1:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 2:
return PORT_TYPE_BOOLEAN;
case 3:
return PORT_TYPE_SCALAR;
default:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
}
@@ -6418,7 +6418,7 @@ VisualShaderNodeCompare::PortType VisualShaderNodeCompare::get_input_port_type(i
case CTYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case CTYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case CTYPE_BOOLEAN:
return PORT_TYPE_BOOLEAN;
case CTYPE_TRANSFORM:
@@ -6670,7 +6670,7 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_input_por
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
@@ -6697,7 +6697,7 @@ VisualShaderNodeMultiplyAdd::PortType VisualShaderNodeMultiplyAdd::get_output_po
case OP_TYPE_VECTOR_2D:
return PORT_TYPE_VECTOR_2D;
case OP_TYPE_VECTOR_3D:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
default:
break;
}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index 5c7a674d34..eeeb91a3ee 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -2136,7 +2136,7 @@ public:
virtual bool is_input_port_default(int p_port, Shader::Mode p_mode) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_global_per_func(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index fbac92a06d..1885211d57 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -39,7 +39,10 @@ int VisualShaderNodeParticleEmitter::get_output_port_count() const {
}
VisualShaderNodeParticleEmitter::PortType VisualShaderNodeParticleEmitter::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
@@ -54,6 +57,9 @@ bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const
}
void VisualShaderNodeParticleEmitter::set_mode_2d(bool p_enabled) {
+ if (mode_2d == p_enabled) {
+ return;
+ }
mode_2d = p_enabled;
emit_changed();
}
@@ -111,7 +117,7 @@ String VisualShaderNodeParticleSphereEmitter::get_input_port_name(int p_port) co
return String();
}
-String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleSphereEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_in_circle(inout uint seed, float radius, float inner_radius) {\n";
@@ -129,7 +135,7 @@ String VisualShaderNodeParticleSphereEmitter::generate_code(Shader::Mode p_mode,
String code;
if (mode_2d) {
- code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ code += " " + p_output_vars[0] + " = __get_random_point_in_circle(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
} else {
code += " " + p_output_vars[0] + " = __get_random_point_in_sphere(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
}
@@ -154,11 +160,27 @@ int VisualShaderNodeParticleBoxEmitter::get_input_port_count() const {
VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleBoxEmitter::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
+void VisualShaderNodeParticleBoxEmitter::set_mode_2d(bool p_enabled) {
+ if (mode_2d == p_enabled) {
+ return;
+ }
+ if (p_enabled) {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ } else {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ }
+ mode_2d = p_enabled;
+ emit_changed();
+}
+
String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const {
if (p_port == 0) {
return "extents";
@@ -166,7 +188,7 @@ String VisualShaderNodeParticleBoxEmitter::get_input_port_name(int p_port) const
return String();
}
-String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_in_box2d(inout uint seed, vec2 extents) {\n";
@@ -185,7 +207,7 @@ String VisualShaderNodeParticleBoxEmitter::generate_global_per_node(Shader::Mode
String VisualShaderNodeParticleBoxEmitter::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
if (mode_2d) {
- code += " " + p_output_vars[0] + " = vec3(__get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ".xy), 0.0);\n";
+ code += " " + p_output_vars[0] + " = __get_random_point_in_box2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
} else {
code += " " + p_output_vars[0] + " = __get_random_point_in_box3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ");\n";
}
@@ -221,7 +243,7 @@ String VisualShaderNodeParticleRingEmitter::get_input_port_name(int p_port) cons
return String();
}
-String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
+String VisualShaderNodeParticleRingEmitter::generate_global_per_node(Shader::Mode p_mode, int p_id) const {
String code;
code += "vec2 __get_random_point_on_ring2d(inout uint seed, float radius, float inner_radius) {\n";
@@ -243,7 +265,7 @@ String VisualShaderNodeParticleRingEmitter::generate_code(Shader::Mode p_mode, V
String code;
if (mode_2d) {
- code = " " + p_output_vars[0] + " = vec3(__get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + "), 0.0);\n";
+ code = " " + p_output_vars[0] + " = __get_random_point_on_ring2d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
} else {
code = " " + p_output_vars[0] + " = __get_random_point_on_ring3d(__seed, " + (p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0]) + ", " + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ", " + (p_input_vars[2].is_empty() ? (String)get_input_port_default_value(2) : p_input_vars[2]) + ");\n";
}
@@ -269,18 +291,24 @@ int VisualShaderNodeParticleMeshEmitter::get_output_port_count() const {
VisualShaderNodeParticleBoxEmitter::PortType VisualShaderNodeParticleMeshEmitter::get_output_port_type(int p_port) const {
switch (p_port) {
- case 0:
- return PORT_TYPE_VECTOR; // position
- case 1:
- return PORT_TYPE_VECTOR; // normal
- case 2:
- return PORT_TYPE_VECTOR; // color
- case 3:
- return PORT_TYPE_SCALAR; // alpha
- case 4:
- return PORT_TYPE_VECTOR; // uv
- case 5:
- return PORT_TYPE_VECTOR; // uv2
+ case 0: // position
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case 1: // normal
+ if (mode_2d) {
+ return PORT_TYPE_VECTOR_2D;
+ }
+ return PORT_TYPE_VECTOR_3D;
+ case 2: // color
+ return PORT_TYPE_VECTOR_3D;
+ case 3: // alpha
+ return PORT_TYPE_SCALAR;
+ case 4: // uv
+ return PORT_TYPE_VECTOR_2D;
+ case 5: // uv2
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -341,18 +369,22 @@ String VisualShaderNodeParticleMeshEmitter::generate_global(Shader::Mode p_mode,
return code;
}
-String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, bool p_ignore_mode2d) const {
+String VisualShaderNodeParticleMeshEmitter::_generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, PortType p_port_type) const {
String code;
if (is_output_port_connected(p_index)) {
- if (mode_2d && !p_ignore_mode2d) {
- code += " " + p_output_vars[p_index] + " = vec3(";
- code += "texelFetch(";
- code += make_unique_id(p_type, p_id, p_texture_name) + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0).xy, 0.0);\n";
- } else {
- code += " " + p_output_vars[p_index] + " = texelFetch(";
- code += make_unique_id(p_type, p_id, p_texture_name) + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0).xyz;\n";
+ switch (p_port_type) {
+ case PORT_TYPE_VECTOR_2D: {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ } break;
+ case PORT_TYPE_VECTOR_3D: {
+ if (mode_2d) {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xy;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ } else {
+ code += vformat(" %s = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0).xyz;\n", p_output_vars[p_index], make_unique_id(p_type, p_id, p_texture_name));
+ }
+ } break;
+ default:
+ break;
}
}
return code;
@@ -362,27 +394,22 @@ String VisualShaderNodeParticleMeshEmitter::generate_code(Shader::Mode p_mode, V
String code;
code += " __scalar_ibuff = int(__rand_from_seed(__seed) * 65535.0) % " + itos(position_texture->get_width()) + ";\n";
- code += _generate_code(p_type, p_id, p_output_vars, 0, "mesh_vx");
- code += _generate_code(p_type, p_id, p_output_vars, 1, "mesh_nm");
+ code += _generate_code(p_type, p_id, p_output_vars, 0, "mesh_vx", VisualShaderNode::PORT_TYPE_VECTOR_3D);
+ code += _generate_code(p_type, p_id, p_output_vars, 1, "mesh_nm", VisualShaderNode::PORT_TYPE_VECTOR_3D);
if (is_output_port_connected(2) || is_output_port_connected(3)) {
- code += " __vec4_buff = texelFetch(";
- code += make_unique_id(p_type, p_id, "mesh_col") + ", ";
- code += "ivec2(__scalar_ibuff, 0), 0);\n";
+ code += vformat(" __vec4_buff = texelFetch(%s, ivec2(__scalar_ibuff, 0), 0);\n", make_unique_id(p_type, p_id, "mesh_col"));
+
if (is_output_port_connected(2)) {
code += " " + p_output_vars[2] + " = __vec4_buff.rgb;\n";
- } else {
- code += " " + p_output_vars[2] + " = vec3(0.0);\n";
}
if (is_output_port_connected(3)) {
code += " " + p_output_vars[3] + " = __vec4_buff.a;\n";
- } else {
- code += " " + p_output_vars[3] + " = 0.0;\n";
}
}
- code += _generate_code(p_type, p_id, p_output_vars, 4, "mesh_uv", true);
- code += _generate_code(p_type, p_id, p_output_vars, 5, "mesh_uv2", true);
+ code += _generate_code(p_type, p_id, p_output_vars, 4, "mesh_uv", VisualShaderNode::PORT_TYPE_VECTOR_2D);
+ code += _generate_code(p_type, p_id, p_output_vars, 5, "mesh_uv2", VisualShaderNode::PORT_TYPE_VECTOR_2D);
return code;
}
@@ -731,7 +758,7 @@ int VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_count() const {
VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) { // position, rotation_axis
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR; // angle (degrees/radians)
}
@@ -762,7 +789,7 @@ int VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_count() const {
}
VisualShaderNodeParticleMultiplyByAxisAngle::PortType VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleMultiplyByAxisAngle::get_output_port_name(int p_port) const {
@@ -815,7 +842,7 @@ int VisualShaderNodeParticleConeVelocity::get_input_port_count() const {
VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
}
@@ -836,7 +863,7 @@ int VisualShaderNodeParticleConeVelocity::get_output_port_count() const {
}
VisualShaderNodeParticleConeVelocity::PortType VisualShaderNodeParticleConeVelocity::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) const {
@@ -876,10 +903,11 @@ void VisualShaderNodeParticleRandomness::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_op_type", "type"), &VisualShaderNodeParticleRandomness::set_op_type);
ClassDB::bind_method(D_METHOD("get_op_type"), &VisualShaderNodeParticleRandomness::get_op_type);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector"), "set_op_type", "get_op_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "op_type", PROPERTY_HINT_ENUM, "Scalar,Vector2,Vector3"), "set_op_type", "get_op_type");
BIND_ENUM_CONSTANT(OP_TYPE_SCALAR);
- BIND_ENUM_CONSTANT(OP_TYPE_VECTOR);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_2D);
+ BIND_ENUM_CONSTANT(OP_TYPE_VECTOR_3D);
BIND_ENUM_CONSTANT(OP_TYPE_MAX);
}
@@ -898,8 +926,13 @@ int VisualShaderNodeParticleRandomness::get_output_port_count() const {
}
VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_output_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -913,8 +946,13 @@ int VisualShaderNodeParticleRandomness::get_input_port_count() const {
}
VisualShaderNodeParticleRandomness::PortType VisualShaderNodeParticleRandomness::get_input_port_type(int p_port) const {
- if (op_type == OP_TYPE_VECTOR) {
- return PORT_TYPE_VECTOR;
+ switch (op_type) {
+ case OP_TYPE_VECTOR_2D:
+ return PORT_TYPE_VECTOR_2D;
+ case OP_TYPE_VECTOR_3D:
+ return PORT_TYPE_VECTOR_3D;
+ default:
+ break;
}
return PORT_TYPE_SCALAR;
}
@@ -930,10 +968,18 @@ String VisualShaderNodeParticleRandomness::get_input_port_name(int p_port) const
String VisualShaderNodeParticleRandomness::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
- if (op_type == OP_TYPE_SCALAR) {
- code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
- } else if (op_type == OP_TYPE_VECTOR) {
- code += vformat(" %s = __randv_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ switch (op_type) {
+ case OP_TYPE_SCALAR: {
+ code += vformat(" %s = __randf_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ code += vformat(" %s = __randv2_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ code += vformat(" %s = __randv3_range(__seed, %s, %s);\n", p_output_vars[0], p_input_vars[0].is_empty() ? (String)get_input_port_default_value(0) : p_input_vars[0], p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]);
+ } break;
+ default:
+ break;
}
return code;
}
@@ -943,12 +989,21 @@ void VisualShaderNodeParticleRandomness::set_op_type(OpType p_op_type) {
if (op_type == p_op_type) {
return;
}
- if (p_op_type == OP_TYPE_SCALAR) {
- set_input_port_default_value(0, 0.0);
- set_input_port_default_value(1, 1.0);
- } else {
- set_input_port_default_value(0, Vector3(-1.0, -1.0, -1.0));
- set_input_port_default_value(1, Vector3(1.0, 1.0, 1.0));
+ switch (p_op_type) {
+ case OP_TYPE_SCALAR: {
+ set_input_port_default_value(0, 0.0, get_input_port_default_value(0));
+ set_input_port_default_value(1, 0.0, get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_2D: {
+ set_input_port_default_value(0, Vector2(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector2(), get_input_port_default_value(1));
+ } break;
+ case OP_TYPE_VECTOR_3D: {
+ set_input_port_default_value(0, Vector3(), get_input_port_default_value(0));
+ set_input_port_default_value(1, Vector3(), get_input_port_default_value(1));
+ } break;
+ default:
+ break;
}
op_type = p_op_type;
emit_changed();
@@ -963,7 +1018,7 @@ bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) con
}
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
- set_input_port_default_value(0, 0.0);
+ set_input_port_default_value(0, -1.0);
set_input_port_default_value(1, 1.0);
}
@@ -996,7 +1051,7 @@ int VisualShaderNodeParticleAccelerator::get_output_port_count() const {
}
VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
String VisualShaderNodeParticleAccelerator::get_output_port_name(int p_port) const {
@@ -1009,11 +1064,11 @@ int VisualShaderNodeParticleAccelerator::get_input_port_count() const {
VisualShaderNodeParticleAccelerator::PortType VisualShaderNodeParticleAccelerator::get_input_port_type(int p_port) const {
if (p_port == 0) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
} else if (p_port == 1) {
return PORT_TYPE_SCALAR;
} else if (p_port == 2) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
}
return PORT_TYPE_SCALAR;
}
@@ -1106,19 +1161,19 @@ VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_inp
switch (p_port) {
case 0:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
- return PORT_TYPE_VECTOR; // custom.rgb
+ return PORT_TYPE_VECTOR_3D; // custom.rgb
}
return PORT_TYPE_BOOLEAN; // active
case 1:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
break; // custom.a (scalar)
}
- return PORT_TYPE_VECTOR; // velocity
+ return PORT_TYPE_VECTOR_3D; // velocity
case 2:
- return PORT_TYPE_VECTOR; // color & velocity
+ return PORT_TYPE_VECTOR_3D; // color & velocity
case 3:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
- return PORT_TYPE_VECTOR; // color
+ return PORT_TYPE_VECTOR_3D; // color
}
break; // alpha (scalar)
case 4:
@@ -1131,18 +1186,18 @@ VisualShaderNodeParticleOutput::PortType VisualShaderNodeParticleOutput::get_inp
if (shader_type == VisualShader::TYPE_COLLIDE) {
return PORT_TYPE_TRANSFORM; // transform
}
- return PORT_TYPE_VECTOR; // position
+ return PORT_TYPE_VECTOR_3D; // position
case 5:
if (shader_type == VisualShader::TYPE_START_CUSTOM || shader_type == VisualShader::TYPE_PROCESS_CUSTOM) {
return PORT_TYPE_TRANSFORM; // transform
}
if (shader_type == VisualShader::TYPE_PROCESS) {
- return PORT_TYPE_VECTOR; // rotation_axis
+ return PORT_TYPE_VECTOR_3D; // rotation_axis
}
break; // scale (scalar)
case 6:
if (shader_type == VisualShader::TYPE_START) {
- return PORT_TYPE_VECTOR; // rotation_axis
+ return PORT_TYPE_VECTOR_3D; // rotation_axis
}
break;
case 7:
@@ -1372,13 +1427,13 @@ VisualShaderNodeParticleEmit::PortType VisualShaderNodeParticleEmit::get_input_p
case 1:
return PORT_TYPE_TRANSFORM;
case 2:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 3:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 4:
return PORT_TYPE_SCALAR;
case 5:
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_3D;
case 6:
return PORT_TYPE_SCALAR;
}
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index ce0d896c01..0b91cba5e0 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -48,7 +48,7 @@ public:
virtual String get_output_port_name(int p_port) const override;
virtual bool has_output_port_preview(int p_port) const override;
- void set_mode_2d(bool p_enabled);
+ virtual void set_mode_2d(bool p_enabled);
bool is_mode_2d() const;
Vector<StringName> get_editable_properties() const override;
@@ -68,7 +68,7 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleSphereEmitter();
@@ -83,8 +83,9 @@ public:
virtual int get_input_port_count() const override;
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
+ virtual void set_mode_2d(bool p_enabled) override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleBoxEmitter();
@@ -100,7 +101,7 @@ public:
virtual PortType get_input_port_type(int p_port) const override;
virtual String get_input_port_name(int p_port) const override;
- virtual String generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
+ virtual String generate_global_per_node(Shader::Mode p_mode, int p_id) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
VisualShaderNodeParticleRingEmitter();
@@ -118,7 +119,7 @@ class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitt
Ref<ImageTexture> uv_texture;
Ref<ImageTexture> uv2_texture;
- String _generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, bool p_ignore_mode2d = false) const;
+ String _generate_code(VisualShader::Type p_type, int p_id, const String *p_output_vars, int p_index, const String &p_texture_name, PortType p_port_type) const;
void _update_texture(const Vector<Vector2> &p_array, Ref<ImageTexture> &r_texture);
void _update_texture(const Vector<Vector3> &p_array, Ref<ImageTexture> &r_texture);
@@ -213,7 +214,8 @@ class VisualShaderNodeParticleRandomness : public VisualShaderNode {
public:
enum OpType {
OP_TYPE_SCALAR,
- OP_TYPE_VECTOR,
+ OP_TYPE_VECTOR_2D,
+ OP_TYPE_VECTOR_3D,
OP_TYPE_MAX,
};
diff --git a/scene/resources/visual_shader_sdf_nodes.cpp b/scene/resources/visual_shader_sdf_nodes.cpp
index 6654e2319b..cbd3ebd83b 100644
--- a/scene/resources/visual_shader_sdf_nodes.cpp
+++ b/scene/resources/visual_shader_sdf_nodes.cpp
@@ -41,7 +41,7 @@ int VisualShaderNodeSDFToScreenUV::get_input_port_count() const {
}
VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeSDFToScreenUV::get_input_port_name(int p_port) const {
@@ -53,7 +53,7 @@ int VisualShaderNodeSDFToScreenUV::get_output_port_count() const {
}
VisualShaderNodeSDFToScreenUV::PortType VisualShaderNodeSDFToScreenUV::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const {
@@ -61,7 +61,7 @@ String VisualShaderNodeSDFToScreenUV::get_output_port_name(int p_port) const {
}
String VisualShaderNodeSDFToScreenUV::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(sdf_to_screen_uv(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = sdf_to_screen_uv(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeSDFToScreenUV::VisualShaderNodeSDFToScreenUV() {
@@ -78,7 +78,7 @@ int VisualShaderNodeScreenUVToSDF::get_input_port_count() const {
}
VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeScreenUVToSDF::get_input_port_name(int p_port) const {
@@ -90,7 +90,7 @@ int VisualShaderNodeScreenUVToSDF::get_output_port_count() const {
}
VisualShaderNodeScreenUVToSDF::PortType VisualShaderNodeScreenUVToSDF::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeScreenUVToSDF::get_output_port_name(int p_port) const {
@@ -105,7 +105,7 @@ bool VisualShaderNodeScreenUVToSDF::is_input_port_default(int p_port, Shader::Mo
}
String VisualShaderNodeScreenUVToSDF::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(screen_uv_to_sdf(" + (p_input_vars[0].is_empty() ? "SCREEN_UV" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = screen_uv_to_sdf(" + (p_input_vars[0].is_empty() ? "SCREEN_UV" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeScreenUVToSDF::VisualShaderNodeScreenUVToSDF() {
@@ -122,7 +122,7 @@ int VisualShaderNodeTextureSDF::get_input_port_count() const {
}
VisualShaderNodeTextureSDF::PortType VisualShaderNodeTextureSDF::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDF::get_input_port_name(int p_port) const {
@@ -142,7 +142,7 @@ String VisualShaderNodeTextureSDF::get_output_port_name(int p_port) const {
}
String VisualShaderNodeTextureSDF::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + ");\n";
+ return " " + p_output_vars[0] + " = texture_sdf(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeTextureSDF::VisualShaderNodeTextureSDF() {
@@ -159,7 +159,7 @@ int VisualShaderNodeTextureSDFNormal::get_input_port_count() const {
}
VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_input_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDFNormal::get_input_port_name(int p_port) const {
@@ -171,7 +171,7 @@ int VisualShaderNodeTextureSDFNormal::get_output_port_count() const {
}
VisualShaderNodeTextureSDFNormal::PortType VisualShaderNodeTextureSDFNormal::get_output_port_type(int p_port) const {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const {
@@ -179,7 +179,7 @@ String VisualShaderNodeTextureSDFNormal::get_output_port_name(int p_port) const
}
String VisualShaderNodeTextureSDFNormal::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
- return " " + p_output_vars[0] + " = vec3(texture_sdf_normal(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0] + ".xy") + "), 0.0f);\n";
+ return " " + p_output_vars[0] + " = texture_sdf_normal(" + (p_input_vars[0].is_empty() ? "vec2(0.0)" : p_input_vars[0]) + ");\n";
}
VisualShaderNodeTextureSDFNormal::VisualShaderNodeTextureSDFNormal() {
@@ -197,7 +197,7 @@ int VisualShaderNodeSDFRaymarch::get_input_port_count() const {
VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_input_port_type(int p_port) const {
if (p_port == 0 || p_port == 1) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -221,7 +221,7 @@ VisualShaderNodeSDFRaymarch::PortType VisualShaderNodeSDFRaymarch::get_output_po
} else if (p_port == 1) {
return PORT_TYPE_BOOLEAN;
} else if (p_port == 2) {
- return PORT_TYPE_VECTOR;
+ return PORT_TYPE_VECTOR_2D;
}
return PORT_TYPE_SCALAR;
}
@@ -245,13 +245,13 @@ String VisualShaderNodeSDFRaymarch::generate_code(Shader::Mode p_mode, VisualSha
if (p_input_vars[0].is_empty()) {
code += " vec2 __from_pos = vec2(0.0f);\n";
} else {
- code += " vec2 __from_pos = " + p_input_vars[0] + ".xy;\n";
+ code += " vec2 __from_pos = " + p_input_vars[0] + ";\n";
}
if (p_input_vars[1].is_empty()) {
code += " vec2 __to_pos = vec2(0.0f);\n";
} else {
- code += " vec2 __to_pos = " + p_input_vars[1] + ".xy;\n";
+ code += " vec2 __to_pos = " + p_input_vars[1] + ";\n";
}
code += "\n vec2 __at = __from_pos;\n";
@@ -271,7 +271,7 @@ String VisualShaderNodeSDFRaymarch::generate_code(Shader::Mode p_mode, VisualSha
code += " float __dist = min(__max_dist, __accum);\n";
code += " " + p_output_vars[0] + " = __dist;\n";
code += " " + p_output_vars[1] + " = __accum < __max_dist;\n";
- code += " " + p_output_vars[2] + " = vec3(__from_pos + __dir * __dist, 0.0f);\n";
+ code += " " + p_output_vars[2] + " = __from_pos + __dir * __dist;\n";
code += " }\n";
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 170a7ef967..39060286a4 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -351,100 +351,372 @@ AudioStreamPlaybackMicrophone::AudioStreamPlaybackMicrophone() {
////////////////////////////////
-void AudioStreamRandomPitch::set_audio_stream(const Ref<AudioStream> &p_audio_stream) {
- audio_stream = p_audio_stream;
- if (audio_stream.is_valid()) {
- for (Set<AudioStreamPlaybackRandomPitch *>::Element *E = playbacks.front(); E; E = E->next()) {
- E->get()->playback = audio_stream->instance_playback();
- }
+void AudioStreamRandomizer::add_stream(int p_index) {
+ if (p_index < 0) {
+ p_index = audio_stream_pool.size();
+ }
+ ERR_FAIL_COND(p_index > audio_stream_pool.size());
+ PoolEntry entry{ nullptr, 1.0f };
+ audio_stream_pool.insert(p_index, entry);
+ emit_signal(SNAME("changed"));
+ notify_property_list_changed();
+}
+
+void AudioStreamRandomizer::move_stream(int p_index_from, int p_index_to) {
+ ERR_FAIL_COND(p_index_from < 0);
+ ERR_FAIL_COND(p_index_from >= audio_stream_pool.size());
+ ERR_FAIL_COND(p_index_to < 0);
+ ERR_FAIL_COND(p_index_to > audio_stream_pool.size());
+ audio_stream_pool.insert(p_index_to, audio_stream_pool[p_index_from]);
+ // If 'from' is strictly after 'to' we need to increment the index by one because of the insertion.
+ if (p_index_from > p_index_to) {
+ p_index_from++;
}
+ audio_stream_pool.remove_at(p_index_from);
+ emit_signal(SNAME("changed"));
+ notify_property_list_changed();
+}
+
+void AudioStreamRandomizer::remove_stream(int p_index) {
+ ERR_FAIL_COND(p_index < 0);
+ ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ audio_stream_pool.remove_at(p_index);
+ emit_signal(SNAME("changed"));
+ notify_property_list_changed();
+}
+
+void AudioStreamRandomizer::set_stream(int p_index, Ref<AudioStream> p_stream) {
+ ERR_FAIL_COND(p_index < 0);
+ ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ audio_stream_pool.write[p_index].stream = p_stream;
+ emit_signal(SNAME("changed"));
}
-Ref<AudioStream> AudioStreamRandomPitch::get_audio_stream() const {
- return audio_stream;
+Ref<AudioStream> AudioStreamRandomizer::get_stream(int p_index) const {
+ ERR_FAIL_COND_V(p_index < 0, nullptr);
+ ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), nullptr);
+ return audio_stream_pool[p_index].stream;
}
-void AudioStreamRandomPitch::set_random_pitch(float p_pitch) {
+void AudioStreamRandomizer::set_stream_probability_weight(int p_index, float p_weight) {
+ ERR_FAIL_COND(p_index < 0);
+ ERR_FAIL_COND(p_index >= audio_stream_pool.size());
+ audio_stream_pool.write[p_index].weight = p_weight;
+ emit_signal(SNAME("changed"));
+}
+
+float AudioStreamRandomizer::get_stream_probability_weight(int p_index) const {
+ ERR_FAIL_COND_V(p_index < 0, 0);
+ ERR_FAIL_COND_V(p_index >= audio_stream_pool.size(), 0);
+ return audio_stream_pool[p_index].weight;
+}
+
+void AudioStreamRandomizer::set_streams_count(int p_count) {
+ audio_stream_pool.resize(p_count);
+}
+
+int AudioStreamRandomizer::get_streams_count() const {
+ return audio_stream_pool.size();
+}
+
+void AudioStreamRandomizer::set_random_pitch(float p_pitch) {
if (p_pitch < 1) {
p_pitch = 1;
}
- random_pitch = p_pitch;
+ random_pitch_scale = p_pitch;
+}
+
+float AudioStreamRandomizer::get_random_pitch() const {
+ return random_pitch_scale;
+}
+
+void AudioStreamRandomizer::set_random_volume_offset_db(float p_volume_offset_db) {
+ if (p_volume_offset_db < 0) {
+ p_volume_offset_db = 0;
+ }
+ random_volume_offset_db = p_volume_offset_db;
+}
+
+float AudioStreamRandomizer::get_random_volume_offset_db() const {
+ return random_volume_offset_db;
}
-float AudioStreamRandomPitch::get_random_pitch() const {
- return random_pitch;
+void AudioStreamRandomizer::set_playback_mode(PlaybackMode p_playback_mode) {
+ playback_mode = p_playback_mode;
}
-Ref<AudioStreamPlayback> AudioStreamRandomPitch::instance_playback() {
- Ref<AudioStreamPlaybackRandomPitch> playback;
+AudioStreamRandomizer::PlaybackMode AudioStreamRandomizer::get_playback_mode() const {
+ return playback_mode;
+}
+
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_random() {
+ Ref<AudioStreamPlaybackRandomizer> playback;
playback.instantiate();
- if (audio_stream.is_valid()) {
- playback->playback = audio_stream->instance_playback();
+ playbacks.insert(playback.ptr());
+ playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
+
+ double total_weight = 0;
+ Vector<PoolEntry> local_pool;
+ for (const PoolEntry &entry : audio_stream_pool) {
+ if (entry.stream.is_valid() && entry.weight > 0) {
+ local_pool.push_back(entry);
+ total_weight += entry.weight;
+ }
+ }
+ if (local_pool.is_empty()) {
+ return playback;
}
+ double chosen_cumulative_weight = Math::random(0.0, total_weight);
+ double cumulative_weight = 0;
+ for (PoolEntry &entry : local_pool) {
+ cumulative_weight += entry.weight;
+ if (cumulative_weight > chosen_cumulative_weight) {
+ playback->playback = entry.stream->instance_playback();
+ last_playback = entry.stream;
+ break;
+ }
+ }
+ if (playback->playback.is_null()) {
+ // This indicates a floating point error. Take the last element.
+ last_playback = local_pool[local_pool.size() - 1].stream;
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ }
+ return playback;
+}
+
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_no_repeats() {
+ Ref<AudioStreamPlaybackRandomizer> playback;
+ double total_weight = 0;
+ Vector<PoolEntry> local_pool;
+ for (const PoolEntry &entry : audio_stream_pool) {
+ if (entry.stream == last_playback) {
+ continue;
+ }
+ if (entry.stream.is_valid() && entry.weight > 0) {
+ local_pool.push_back(entry);
+ total_weight += entry.weight;
+ }
+ }
+ if (local_pool.is_empty()) {
+ playback = instance_playback_random();
+ WARN_PRINT("Playback stream pool is too small to prevent repeats.");
+ return playback;
+ }
+
+ playback.instantiate();
playbacks.insert(playback.ptr());
- playback->random_pitch = Ref<AudioStreamRandomPitch>((AudioStreamRandomPitch *)this);
+ playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
+ double chosen_cumulative_weight = Math::random(0.0, total_weight);
+ double cumulative_weight = 0;
+ for (PoolEntry &entry : local_pool) {
+ cumulative_weight += entry.weight;
+ if (cumulative_weight > chosen_cumulative_weight) {
+ last_playback = entry.stream;
+ playback->playback = entry.stream->instance_playback();
+ break;
+ }
+ }
+ if (playback->playback.is_null()) {
+ // This indicates a floating point error. Take the last element.
+ last_playback = local_pool[local_pool.size() - 1].stream;
+ playback->playback = local_pool.write[local_pool.size() - 1].stream->instance_playback();
+ }
return playback;
}
-String AudioStreamRandomPitch::get_stream_name() const {
- if (audio_stream.is_valid()) {
- return "Random: " + audio_stream->get_name();
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback_sequential() {
+ Ref<AudioStreamPlaybackRandomizer> playback;
+ playback.instantiate();
+ playbacks.insert(playback.ptr());
+ playback->randomizer = Ref<AudioStreamRandomizer>((AudioStreamRandomizer *)this);
+
+ Vector<Ref<AudioStream>> local_pool;
+ for (const PoolEntry &entry : audio_stream_pool) {
+ if (entry.stream.is_null()) {
+ continue;
+ }
+ if (local_pool.find(entry.stream) != -1) {
+ WARN_PRINT("Duplicate stream in sequential playback pool");
+ continue;
+ }
+ local_pool.push_back(entry.stream);
+ }
+ if (local_pool.is_empty()) {
+ return playback;
}
- return "RandomPitch";
+ bool found_last_stream = false;
+ for (Ref<AudioStream> &entry : local_pool) {
+ if (found_last_stream) {
+ last_playback = entry;
+ playback->playback = entry->instance_playback();
+ break;
+ }
+ if (entry == last_playback) {
+ found_last_stream = true;
+ }
+ }
+ if (playback->playback.is_null()) {
+ // Wrap around
+ last_playback = local_pool[0];
+ playback->playback = local_pool.write[0]->instance_playback();
+ }
+ return playback;
}
-float AudioStreamRandomPitch::get_length() const {
- if (audio_stream.is_valid()) {
- return audio_stream->get_length();
+Ref<AudioStreamPlayback> AudioStreamRandomizer::instance_playback() {
+ switch (playback_mode) {
+ case PLAYBACK_RANDOM:
+ return instance_playback_random();
+ case PLAYBACK_RANDOM_NO_REPEATS:
+ return instance_playback_no_repeats();
+ case PLAYBACK_SEQUENTIAL:
+ return instance_playback_sequential();
+ default:
+ ERR_FAIL_V_MSG(nullptr, "Unhandled playback mode.");
}
+}
+String AudioStreamRandomizer::get_stream_name() const {
+ return "Randomizer";
+}
+
+float AudioStreamRandomizer::get_length() const {
return 0;
}
-bool AudioStreamRandomPitch::is_monophonic() const {
- if (audio_stream.is_valid()) {
- return audio_stream->is_monophonic();
+bool AudioStreamRandomizer::is_monophonic() const {
+ for (const PoolEntry &entry : audio_stream_pool) {
+ if (entry.stream.is_valid() && entry.stream->is_monophonic()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool AudioStreamRandomizer::_get(const StringName &p_name, Variant &r_ret) const {
+ if (AudioStream::_get(p_name, r_ret)) {
+ return true;
}
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) {
+ int index = components[0].trim_prefix("stream_").to_int();
+ if (index < 0 || index >= (int)audio_stream_pool.size()) {
+ return false;
+ }
- return true; // It doesn't really matter what we return here, but no sense instancing a many playbacks of a null stream.
+ if (components[1] == "stream") {
+ r_ret = get_stream(index);
+ return true;
+ } else if (components[1] == "weight") {
+ r_ret = get_stream_probability_weight(index);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+bool AudioStreamRandomizer::_set(const StringName &p_name, const Variant &p_value) {
+ if (AudioStream::_set(p_name, p_value)) {
+ return true;
+ }
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() == 2 && components[0].begins_with("stream_") && components[0].trim_prefix("stream_").is_valid_int()) {
+ int index = components[0].trim_prefix("stream_").to_int();
+ if (index < 0 || index >= (int)audio_stream_pool.size()) {
+ return false;
+ }
+
+ if (components[1] == "stream") {
+ set_stream(index, p_value);
+ return true;
+ } else if (components[1] == "weight") {
+ set_stream_probability_weight(index, p_value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+void AudioStreamRandomizer::_get_property_list(List<PropertyInfo> *p_list) const {
+ AudioStream::_get_property_list(p_list); // Define the trivial scalar properties.
+ p_list->push_back(PropertyInfo(Variant::NIL, "Streams", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ for (int i = 0; i < audio_stream_pool.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::OBJECT, vformat("stream_%d/stream", i), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("stream_%d/weight", i), PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"));
+ }
}
-void AudioStreamRandomPitch::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_audio_stream", "stream"), &AudioStreamRandomPitch::set_audio_stream);
- ClassDB::bind_method(D_METHOD("get_audio_stream"), &AudioStreamRandomPitch::get_audio_stream);
+void AudioStreamRandomizer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_stream", "index"), &AudioStreamRandomizer::add_stream);
+ ClassDB::bind_method(D_METHOD("move_stream", "index_from", "index_to"), &AudioStreamRandomizer::move_stream);
+ ClassDB::bind_method(D_METHOD("remove_stream", "index"), &AudioStreamRandomizer::remove_stream);
+
+ ClassDB::bind_method(D_METHOD("set_stream", "index", "stream"), &AudioStreamRandomizer::set_stream);
+ ClassDB::bind_method(D_METHOD("get_stream", "index"), &AudioStreamRandomizer::get_stream);
+ ClassDB::bind_method(D_METHOD("set_stream_probability_weight", "index", "weight"), &AudioStreamRandomizer::set_stream_probability_weight);
+ ClassDB::bind_method(D_METHOD("get_stream_probability_weight", "index"), &AudioStreamRandomizer::get_stream_probability_weight);
+
+ ClassDB::bind_method(D_METHOD("set_streams_count", "count"), &AudioStreamRandomizer::set_streams_count);
+ ClassDB::bind_method(D_METHOD("get_streams_count"), &AudioStreamRandomizer::get_streams_count);
+
+ ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomizer::set_random_pitch);
+ ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomizer::get_random_pitch);
- ClassDB::bind_method(D_METHOD("set_random_pitch", "scale"), &AudioStreamRandomPitch::set_random_pitch);
- ClassDB::bind_method(D_METHOD("get_random_pitch"), &AudioStreamRandomPitch::get_random_pitch);
+ ClassDB::bind_method(D_METHOD("set_random_volume_offset_db", "db_offset"), &AudioStreamRandomizer::set_random_volume_offset_db);
+ ClassDB::bind_method(D_METHOD("get_random_volume_offset_db"), &AudioStreamRandomizer::get_random_volume_offset_db);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "audio_stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_audio_stream", "get_audio_stream");
+ ClassDB::bind_method(D_METHOD("set_playback_mode", "mode"), &AudioStreamRandomizer::set_playback_mode);
+ ClassDB::bind_method(D_METHOD("get_playback_mode"), &AudioStreamRandomizer::get_playback_mode);
+
+ ADD_ARRAY("streams", "stream_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "streams_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_streams_count", "get_streams_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_mode", PROPERTY_HINT_ENUM, "Random (Avoid Repeats),Random,Sequential"), "set_playback_mode", "get_playback_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_pitch", PROPERTY_HINT_RANGE, "1,16,0.01"), "set_random_pitch", "get_random_pitch");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "random_volume_offset_db", PROPERTY_HINT_RANGE, "0,40,0"), "set_random_volume_offset_db", "get_random_volume_offset_db");
+
+ BIND_ENUM_CONSTANT(PLAYBACK_RANDOM_NO_REPEATS);
+ BIND_ENUM_CONSTANT(PLAYBACK_RANDOM);
+ BIND_ENUM_CONSTANT(PLAYBACK_SEQUENTIAL);
}
-AudioStreamRandomPitch::AudioStreamRandomPitch() {
- random_pitch = 1.1;
+AudioStreamRandomizer::AudioStreamRandomizer() {
+ random_pitch_scale = 1.1;
+ random_volume_offset_db = 5;
}
-void AudioStreamPlaybackRandomPitch::start(float p_from_pos) {
+void AudioStreamPlaybackRandomizer::start(float p_from_pos) {
playing = playback;
- float range_from = 1.0 / random_pitch->random_pitch;
- float range_to = random_pitch->random_pitch;
+ {
+ float range_from = 1.0 / randomizer->random_pitch_scale;
+ float range_to = randomizer->random_pitch_scale;
- pitch_scale = range_from + Math::randf() * (range_to - range_from);
+ pitch_scale = range_from + Math::randf() * (range_to - range_from);
+ }
+ {
+ float range_from = -randomizer->random_volume_offset_db;
+ float range_to = randomizer->random_volume_offset_db;
+
+ float volume_offset_db = range_from + Math::randf() * (range_to - range_from);
+ volume_scale = Math::db2linear(volume_offset_db);
+ }
if (playing.is_valid()) {
playing->start(p_from_pos);
}
}
-void AudioStreamPlaybackRandomPitch::stop() {
+void AudioStreamPlaybackRandomizer::stop() {
if (playing.is_valid()) {
playing->stop();
- ;
}
}
-bool AudioStreamPlaybackRandomPitch::is_playing() const {
+bool AudioStreamPlaybackRandomizer::is_playing() const {
if (playing.is_valid()) {
return playing->is_playing();
}
@@ -452,7 +724,7 @@ bool AudioStreamPlaybackRandomPitch::is_playing() const {
return false;
}
-int AudioStreamPlaybackRandomPitch::get_loop_count() const {
+int AudioStreamPlaybackRandomizer::get_loop_count() const {
if (playing.is_valid()) {
return playing->get_loop_count();
}
@@ -460,7 +732,7 @@ int AudioStreamPlaybackRandomPitch::get_loop_count() const {
return 0;
}
-float AudioStreamPlaybackRandomPitch::get_playback_position() const {
+float AudioStreamPlaybackRandomizer::get_playback_position() const {
if (playing.is_valid()) {
return playing->get_playback_position();
}
@@ -468,13 +740,13 @@ float AudioStreamPlaybackRandomPitch::get_playback_position() const {
return 0;
}
-void AudioStreamPlaybackRandomPitch::seek(float p_time) {
+void AudioStreamPlaybackRandomizer::seek(float p_time) {
if (playing.is_valid()) {
playing->seek(p_time);
}
}
-int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+int AudioStreamPlaybackRandomizer::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
if (playing.is_valid()) {
return playing->mix(p_buffer, p_rate_scale * pitch_scale, p_frames);
} else {
@@ -485,7 +757,7 @@ int AudioStreamPlaybackRandomPitch::mix(AudioFrame *p_buffer, float p_rate_scale
}
}
-AudioStreamPlaybackRandomPitch::~AudioStreamPlaybackRandomPitch() {
- random_pitch->playbacks.erase(this);
+AudioStreamPlaybackRandomizer::~AudioStreamPlaybackRandomizer() {
+ randomizer->playbacks.erase(this);
}
/////////////////////////////////////////////
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 32159e96ef..ce9bcabb9c 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -167,43 +167,86 @@ public:
//
-class AudioStreamPlaybackRandomPitch;
+class AudioStreamPlaybackRandomizer;
-class AudioStreamRandomPitch : public AudioStream {
- GDCLASS(AudioStreamRandomPitch, AudioStream);
- friend class AudioStreamPlaybackRandomPitch;
+class AudioStreamRandomizer : public AudioStream {
+ GDCLASS(AudioStreamRandomizer, AudioStream);
- Set<AudioStreamPlaybackRandomPitch *> playbacks;
- Ref<AudioStream> audio_stream;
- float random_pitch;
+public:
+ enum PlaybackMode {
+ PLAYBACK_RANDOM_NO_REPEATS,
+ PLAYBACK_RANDOM,
+ PLAYBACK_SEQUENTIAL,
+ };
+
+private:
+ friend class AudioStreamPlaybackRandomizer;
+
+ struct PoolEntry {
+ Ref<AudioStream> stream;
+ float weight;
+ };
+
+ Set<AudioStreamPlaybackRandomizer *> playbacks;
+ Vector<PoolEntry> audio_stream_pool;
+ float random_pitch_scale;
+ float random_volume_offset_db;
+
+ Ref<AudioStreamPlayback> instance_playback_random();
+ Ref<AudioStreamPlayback> instance_playback_no_repeats();
+ Ref<AudioStreamPlayback> instance_playback_sequential();
+
+ Ref<AudioStream> last_playback = nullptr;
+ PlaybackMode playback_mode = PLAYBACK_RANDOM_NO_REPEATS;
protected:
static void _bind_methods();
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
public:
- void set_audio_stream(const Ref<AudioStream> &p_audio_stream);
- Ref<AudioStream> get_audio_stream() const;
+ void add_stream(int p_index);
+ void move_stream(int p_index_from, int p_index_to);
+ void remove_stream(int p_index);
+
+ void set_stream(int p_index, Ref<AudioStream> p_stream);
+ Ref<AudioStream> get_stream(int p_index) const;
+ void set_stream_probability_weight(int p_index, float p_weight);
+ float get_stream_probability_weight(int p_index) const;
- void set_random_pitch(float p_pitch);
+ void set_streams_count(int p_count);
+ int get_streams_count() const;
+
+ void set_random_pitch(float p_pitch_scale);
float get_random_pitch() const;
+ void set_random_volume_offset_db(float p_volume_offset_db);
+ float get_random_volume_offset_db() const;
+
+ void set_playback_mode(PlaybackMode p_playback_mode);
+ PlaybackMode get_playback_mode() const;
+
virtual Ref<AudioStreamPlayback> instance_playback() override;
virtual String get_stream_name() const override;
virtual float get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
- AudioStreamRandomPitch();
+ AudioStreamRandomizer();
};
-class AudioStreamPlaybackRandomPitch : public AudioStreamPlayback {
- GDCLASS(AudioStreamPlaybackRandomPitch, AudioStreamPlayback);
- friend class AudioStreamRandomPitch;
+class AudioStreamPlaybackRandomizer : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackRandomizer, AudioStreamPlayback);
+ friend class AudioStreamRandomizer;
- Ref<AudioStreamRandomPitch> random_pitch;
+ Ref<AudioStreamRandomizer> randomizer;
Ref<AudioStreamPlayback> playback;
Ref<AudioStreamPlayback> playing;
+
float pitch_scale;
+ float volume_scale;
public:
virtual void start(float p_from_pos = 0.0) override;
@@ -217,7 +260,9 @@ public:
virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
- ~AudioStreamPlaybackRandomPitch();
+ ~AudioStreamPlaybackRandomizer();
};
+VARIANT_ENUM_CAST(AudioStreamRandomizer::PlaybackMode);
+
#endif // AUDIO_STREAM_H
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index 5ded5cf214..d880df2a9b 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -380,6 +380,7 @@ void DisplayServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("screen_get_scale", "screen"), &DisplayServer::screen_get_scale, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_is_touchscreen", "screen"), &DisplayServer::screen_is_touchscreen, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_max_scale"), &DisplayServer::screen_get_max_scale);
+ ClassDB::bind_method(D_METHOD("screen_get_refresh_rate", "screen"), &DisplayServer::screen_get_refresh_rate, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW));
@@ -539,6 +540,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(WINDOW_MODE_MINIMIZED);
BIND_ENUM_CONSTANT(WINDOW_MODE_MAXIMIZED);
BIND_ENUM_CONSTANT(WINDOW_MODE_FULLSCREEN);
+ BIND_ENUM_CONSTANT(WINDOW_MODE_EXCLUSIVE_FULLSCREEN);
BIND_ENUM_CONSTANT(WINDOW_FLAG_RESIZE_DISABLED);
BIND_ENUM_CONSTANT(WINDOW_FLAG_BORDERLESS);
diff --git a/servers/display_server.h b/servers/display_server.h
index 8c6586dc20..19bb111094 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -53,7 +53,8 @@ public:
WINDOW_MODE_WINDOWED,
WINDOW_MODE_MINIMIZED,
WINDOW_MODE_MAXIMIZED,
- WINDOW_MODE_FULLSCREEN
+ WINDOW_MODE_FULLSCREEN,
+ WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
};
// Keep the VSyncMode enum values in sync with the `display/window/vsync/vsync_mode`
@@ -174,6 +175,8 @@ public:
SCREEN_OF_MAIN_WINDOW = -1
};
+ const float SCREEN_REFRESH_RATE_FALLBACK = 60.0; // Returned by screen_get_refresh_rate if the method fails. Most screens are 60hz as of 2022.
+
virtual int get_screen_count() const = 0;
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
@@ -188,6 +191,7 @@ public:
}
return scale;
}
+ virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0;
virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
// Keep the ScreenOrientation enum values in sync with the `display/window/handheld/orientation`
diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h
index 4ef9dc622f..f74a8fad23 100644
--- a/servers/display_server_headless.h
+++ b/servers/display_server_headless.h
@@ -62,6 +62,7 @@ public:
int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 96; /* 0 might cause issues */ }
float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return 1; }
float screen_get_max_scale() const override { return 1; }
+ float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override { return SCREEN_REFRESH_RATE_FALLBACK; }
Vector<DisplayServer::WindowID> get_window_list() const override { return Vector<DisplayServer::WindowID>(); }
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index bf8b6379d2..3726bcde35 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -140,7 +140,7 @@ void register_server_types() {
GDREGISTER_CLASS(AudioStreamPlayback);
GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackResampled);
GDREGISTER_CLASS(AudioStreamMicrophone);
- GDREGISTER_CLASS(AudioStreamRandomPitch);
+ GDREGISTER_CLASS(AudioStreamRandomizer);
GDREGISTER_VIRTUAL_CLASS(AudioEffect);
GDREGISTER_VIRTUAL_CLASS(AudioEffectInstance);
GDREGISTER_CLASS(AudioEffectEQ);
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index 25a366aa4b..02a0b6f184 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -482,12 +482,11 @@ void EffectsRD::set_color(RID p_dest_texture, const Color &p_color, const Rect2i
RD::get_singleton()->compute_list_end();
}
-void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst) {
+void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst) {
ERR_FAIL_COND_MSG(prefer_raster_effects, "Can't use the compute version of the gaussian blur with the mobile renderer.");
memset(&copy.push_constant, 0, sizeof(CopyPushConstant));
- uint32_t base_flags = 0;
copy.push_constant.section[0] = p_region.position.x;
copy.push_constant.section[1] = p_region.position.y;
copy.push_constant.section[2] = p_region.size.width;
@@ -497,23 +496,12 @@ void EffectsRD::gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back
RD::DrawListID compute_list = RD::get_singleton()->compute_list_begin();
RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, copy.pipelines[p_8bit_dst ? COPY_MODE_GAUSSIAN_COPY_8BIT : COPY_MODE_GAUSSIAN_COPY]);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_rd_texture), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_back_texture), 3);
-
- copy.push_constant.flags = base_flags | COPY_FLAG_HORIZONTAL;
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
-
- RD::get_singleton()->compute_list_add_barrier(compute_list);
-
- //VERTICAL
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_back_texture), 0);
RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_texture), 3);
- copy.push_constant.flags = base_flags;
RD::get_singleton()->compute_list_set_push_constant(compute_list, &copy.push_constant, sizeof(CopyPushConstant));
RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_region.size.width, p_region.size.height, 1);
+
RD::get_singleton()->compute_list_end();
}
@@ -1453,7 +1441,6 @@ void EffectsRD::downsample_depth(RID p_depth_buffer, const Vector<RID> &p_depth_
RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE);
- ss_effects.used_full_mips_last_frame = use_mips;
ss_effects.used_full_mips_last_frame = use_full_mips;
ss_effects.used_half_size_last_frame = use_half_size;
}
@@ -2344,8 +2331,8 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
Vector<String> copy_modes;
copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n");
copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define DST_IMAGE_8BIT\n");
- copy_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n");
- copy_modes.push_back("\n#define MODE_GAUSSIAN_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n");
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n");
+ copy_modes.push_back("\n#define MODE_GAUSSIAN_BLUR\n#define MODE_GLOW\n#define GLOW_USE_AUTO_EXPOSURE\n");
copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n");
copy_modes.push_back("\n#define MODE_SIMPLE_COPY\n#define DST_IMAGE_8BIT\n");
copy_modes.push_back("\n#define MODE_SIMPLE_COPY_DEPTH\n");
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index 1a080756a8..f5e5b1ace7 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -899,7 +899,7 @@ public:
void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
- void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
+ void gaussian_blur(RID p_source_rd_texture, RID p_texture, const Rect2i &p_region, bool p_8bit_dst = false);
void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false);
void gaussian_glow(RID p_source_rd_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
void gaussian_glow_raster(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_high_quality = false, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_threshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index 145c4f902e..f345001539 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -7516,10 +7516,6 @@ void RendererStorageRD::_clear_render_target(RenderTarget *rt) {
if (rt->backbuffer.is_valid()) {
RD::get_singleton()->free(rt->backbuffer);
rt->backbuffer = RID();
- for (int i = 0; i < rt->backbuffer_mipmaps.size(); i++) {
- //just erase copies, since the rest are erased by dependency
- RD::get_singleton()->free(rt->backbuffer_mipmaps[i].mipmap_copy);
- }
rt->backbuffer_mipmaps.clear();
rt->backbuffer_uniform_set = RID(); //chain deleted
}
@@ -7636,7 +7632,9 @@ void RendererStorageRD::_create_render_target_backbuffer(RenderTarget *rt) {
tf.mipmaps = mipmaps_required;
rt->backbuffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ RD::get_singleton()->set_resource_name(rt->backbuffer, "Render Target Back Buffer");
rt->backbuffer_mipmap0 = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, 0);
+ RD::get_singleton()->set_resource_name(rt->backbuffer_mipmap0, "Back Buffer slice mipmap 0");
{
Vector<RID> fb_tex;
@@ -7651,23 +7649,10 @@ void RendererStorageRD::_create_render_target_backbuffer(RenderTarget *rt) {
}
//create mipmaps
for (uint32_t i = 1; i < mipmaps_required; i++) {
- RenderTarget::BackbufferMipmap mm;
- {
- mm.mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i);
- }
-
- {
- Size2 mm_size = Image::get_image_mipmap_size(tf.width, tf.height, Image::FORMAT_RGBA8, i);
+ RID mipmap = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rt->backbuffer, 0, i);
+ RD::get_singleton()->set_resource_name(mipmap, "Back Buffer slice mip: " + itos(i));
- RD::TextureFormat mmtf = tf;
- mmtf.width = mm_size.width;
- mmtf.height = mm_size.height;
- mmtf.mipmaps = 1;
-
- mm.mipmap_copy = RD::get_singleton()->texture_create(mmtf, RD::TextureView());
- }
-
- rt->backbuffer_mipmaps.push_back(mm);
+ rt->backbuffer_mipmaps.push_back(mipmap);
}
}
@@ -8111,7 +8096,7 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c
if (!p_gen_mipmaps) {
return;
}
-
+ RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps");
//then mipmap blur
RID prev_texture = rt->color; //use color, not backbuffer, as bb has mipmaps.
@@ -8121,10 +8106,11 @@ void RendererStorageRD::render_target_copy_to_back_buffer(RID p_render_target, c
region.size.x = MAX(1, region.size.x >> 1);
region.size.y = MAX(1, region.size.y >> 1);
- const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
- effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
- prev_texture = mm.mipmap;
+ RID mipmap = rt->backbuffer_mipmaps[i];
+ effects->gaussian_blur(prev_texture, mipmap, region, true);
+ prev_texture = mipmap;
}
+ RD::get_singleton()->draw_command_end_label();
}
void RendererStorageRD::render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color) {
@@ -8164,7 +8150,7 @@ void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_targe
return; //nothing to do
}
}
-
+ RD::get_singleton()->draw_command_begin_label("Gaussian Blur Mipmaps2");
//then mipmap blur
RID prev_texture = rt->backbuffer_mipmap0;
@@ -8174,10 +8160,11 @@ void RendererStorageRD::render_target_gen_back_buffer_mipmaps(RID p_render_targe
region.size.x = MAX(1, region.size.x >> 1);
region.size.y = MAX(1, region.size.y >> 1);
- const RenderTarget::BackbufferMipmap &mm = rt->backbuffer_mipmaps[i];
- effects->gaussian_blur(prev_texture, mm.mipmap, mm.mipmap_copy, region, true);
- prev_texture = mm.mipmap;
+ RID mipmap = rt->backbuffer_mipmaps[i];
+ effects->gaussian_blur(prev_texture, mipmap, region, true);
+ prev_texture = mipmap;
}
+ RD::get_singleton()->draw_command_end_label();
}
RID RendererStorageRD::render_target_get_framebuffer_uniform_set(RID p_render_target) {
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h
index 43bbcf6520..6febb71e93 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.h
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.h
@@ -1169,12 +1169,7 @@ private:
RID backbuffer_fb;
RID backbuffer_mipmap0;
- struct BackbufferMipmap {
- RID mipmap;
- RID mipmap_copy;
- };
-
- Vector<BackbufferMipmap> backbuffer_mipmaps;
+ Vector<RID> backbuffer_mipmaps;
RID framebuffer_uniform_set;
RID backbuffer_uniform_set;
diff --git a/servers/rendering/renderer_rd/shaders/copy.glsl b/servers/rendering/renderer_rd/shaders/copy.glsl
index d4d0ed0f56..ecf7bb9817 100644
--- a/servers/rendering/renderer_rd/shaders/copy.glsl
+++ b/servers/rendering/renderer_rd/shaders/copy.glsl
@@ -61,7 +61,7 @@ layout(rgba8, set = 3, binding = 0) uniform restrict writeonly image2D dest_buff
layout(rgba32f, set = 3, binding = 0) uniform restrict writeonly image2D dest_buffer;
#endif
-#ifdef MODE_GAUSSIAN_GLOW
+#ifdef MODE_GAUSSIAN_BLUR
shared vec4 local_cache[256];
shared vec4 temp_cache[128];
#endif
@@ -70,7 +70,7 @@ void main() {
// Pixel being shaded
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
-#ifndef MODE_GAUSSIAN_GLOW // Glow needs the extra threads
+#ifndef MODE_GAUSSIAN_BLUR // Gaussian blur needs the extra threads
if (any(greaterThanEqual(pos, params.section.zw))) { //too large, do nothing
return;
}
@@ -92,35 +92,11 @@ void main() {
#ifdef MODE_GAUSSIAN_BLUR
- //Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect
-
- if (bool(params.flags & FLAG_HORIZONTAL)) {
- ivec2 base_pos = (pos + params.section.xy) << 1;
- vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.214607;
- color += texelFetch(source_color, base_pos + ivec2(1, 0), 0) * 0.189879;
- color += texelFetch(source_color, base_pos + ivec2(2, 0), 0) * 0.131514;
- color += texelFetch(source_color, base_pos + ivec2(3, 0), 0) * 0.071303;
- color += texelFetch(source_color, base_pos + ivec2(-1, 0), 0) * 0.189879;
- color += texelFetch(source_color, base_pos + ivec2(-2, 0), 0) * 0.131514;
- color += texelFetch(source_color, base_pos + ivec2(-3, 0), 0) * 0.071303;
- imageStore(dest_buffer, pos + params.target, color);
- } else {
- ivec2 base_pos = (pos + params.section.xy);
- vec4 color = texelFetch(source_color, base_pos + ivec2(0, 0), 0) * 0.38774;
- color += texelFetch(source_color, base_pos + ivec2(0, 1), 0) * 0.24477;
- color += texelFetch(source_color, base_pos + ivec2(0, 2), 0) * 0.06136;
- color += texelFetch(source_color, base_pos + ivec2(0, -1), 0) * 0.24477;
- color += texelFetch(source_color, base_pos + ivec2(0, -2), 0) * 0.06136;
- imageStore(dest_buffer, pos + params.target, color);
- }
-#endif
-
-#ifdef MODE_GAUSSIAN_GLOW
-
// First pass copy texture into 16x16 local memory for every 8x8 thread block
vec2 quad_center_uv = clamp(vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.5) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
uint dest_index = gl_LocalInvocationID.x * 2 + gl_LocalInvocationID.y * 2 * 16;
+#ifdef MODE_GLOW
if (bool(params.flags & FLAG_HIGH_QUALITY_GLOW)) {
vec2 quad_offset_uv = clamp((vec2(gl_GlobalInvocationID.xy + gl_LocalInvocationID.xy - 3.0)) / params.section.zw, vec2(0.5 / params.section.zw), vec2(1.0 - 1.5 / params.section.zw));
@@ -128,12 +104,15 @@ void main() {
local_cache[dest_index + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.z, 0.0), 0)) * 0.5;
local_cache[dest_index + 16] = (textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0) + textureLod(source_color, quad_offset_uv + vec2(0.0, 1.0 / params.section.w), 0)) * 0.5;
local_cache[dest_index + 16 + 1] = (textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0) + textureLod(source_color, quad_offset_uv + vec2(1.0 / params.section.zw), 0)) * 0.5;
- } else {
+ } else
+#endif
+ {
local_cache[dest_index] = textureLod(source_color, quad_center_uv, 0);
local_cache[dest_index + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.z, 0.0), 0);
local_cache[dest_index + 16] = textureLod(source_color, quad_center_uv + vec2(0.0, 1.0 / params.section.w), 0);
local_cache[dest_index + 16 + 1] = textureLod(source_color, quad_center_uv + vec2(1.0 / params.section.zw), 0);
}
+#ifdef MODE_GLOW
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
// Tonemap initial samples to reduce weight of fireflies: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
local_cache[dest_index] /= 1.0 + dot(local_cache[dest_index].rgb, vec3(0.299, 0.587, 0.114));
@@ -141,29 +120,33 @@ void main() {
local_cache[dest_index + 16] /= 1.0 + dot(local_cache[dest_index + 16].rgb, vec3(0.299, 0.587, 0.114));
local_cache[dest_index + 16 + 1] /= 1.0 + dot(local_cache[dest_index + 16 + 1].rgb, vec3(0.299, 0.587, 0.114));
}
-
+ const float kernel[4] = { 0.174938, 0.165569, 0.140367, 0.106595 };
+#else
+ // Simpler blur uses SIGMA2 for the gaussian kernel for a stronger effect.
+ const float kernel[4] = { 0.214607, 0.189879, 0.131514, 0.071303 };
+#endif
memoryBarrierShared();
barrier();
// Horizontal pass. Needs to copy into 8x16 chunk of local memory so vertical pass has full resolution
uint read_index = gl_LocalInvocationID.x + gl_LocalInvocationID.y * 32 + 4;
vec4 color_top = vec4(0.0);
- color_top += local_cache[read_index] * 0.174938;
- color_top += local_cache[read_index + 1] * 0.165569;
- color_top += local_cache[read_index + 2] * 0.140367;
- color_top += local_cache[read_index + 3] * 0.106595;
- color_top += local_cache[read_index - 1] * 0.165569;
- color_top += local_cache[read_index - 2] * 0.140367;
- color_top += local_cache[read_index - 3] * 0.106595;
+ color_top += local_cache[read_index] * kernel[0];
+ color_top += local_cache[read_index + 1] * kernel[1];
+ color_top += local_cache[read_index + 2] * kernel[2];
+ color_top += local_cache[read_index + 3] * kernel[3];
+ color_top += local_cache[read_index - 1] * kernel[1];
+ color_top += local_cache[read_index - 2] * kernel[2];
+ color_top += local_cache[read_index - 3] * kernel[3];
vec4 color_bottom = vec4(0.0);
- color_bottom += local_cache[read_index + 16] * 0.174938;
- color_bottom += local_cache[read_index + 1 + 16] * 0.165569;
- color_bottom += local_cache[read_index + 2 + 16] * 0.140367;
- color_bottom += local_cache[read_index + 3 + 16] * 0.106595;
- color_bottom += local_cache[read_index - 1 + 16] * 0.165569;
- color_bottom += local_cache[read_index - 2 + 16] * 0.140367;
- color_bottom += local_cache[read_index - 3 + 16] * 0.106595;
+ color_bottom += local_cache[read_index + 16] * kernel[0];
+ color_bottom += local_cache[read_index + 1 + 16] * kernel[1];
+ color_bottom += local_cache[read_index + 2 + 16] * kernel[2];
+ color_bottom += local_cache[read_index + 3 + 16] * kernel[3];
+ color_bottom += local_cache[read_index - 1 + 16] * kernel[1];
+ color_bottom += local_cache[read_index - 2 + 16] * kernel[2];
+ color_bottom += local_cache[read_index - 3 + 16] * kernel[3];
// rotate samples to take advantage of cache coherency
uint write_index = gl_LocalInvocationID.y * 2 + gl_LocalInvocationID.x * 16;
@@ -174,18 +157,24 @@ void main() {
memoryBarrierShared();
barrier();
+ // If destination outside of texture, can stop doing work now
+ if (any(greaterThanEqual(pos, params.section.zw))) {
+ return;
+ }
+
// Vertical pass
uint index = gl_LocalInvocationID.y + gl_LocalInvocationID.x * 16 + 4;
vec4 color = vec4(0.0);
- color += temp_cache[index] * 0.174938;
- color += temp_cache[index + 1] * 0.165569;
- color += temp_cache[index + 2] * 0.140367;
- color += temp_cache[index + 3] * 0.106595;
- color += temp_cache[index - 1] * 0.165569;
- color += temp_cache[index - 2] * 0.140367;
- color += temp_cache[index - 3] * 0.106595;
+ color += temp_cache[index] * kernel[0];
+ color += temp_cache[index + 1] * kernel[1];
+ color += temp_cache[index + 2] * kernel[2];
+ color += temp_cache[index + 3] * kernel[3];
+ color += temp_cache[index - 1] * kernel[1];
+ color += temp_cache[index - 2] * kernel[2];
+ color += temp_cache[index - 3] * kernel[3];
+#ifdef MODE_GLOW
if (bool(params.flags & FLAG_GLOW_FIRST_PASS)) {
// Undo tonemap to restore range: https://graphicrants.blogspot.com/2013/12/tone-mapping.html
color /= 1.0 - dot(color.rgb, vec3(0.299, 0.587, 0.114));
@@ -205,7 +194,7 @@ void main() {
color = min(color * feedback, vec4(params.glow_luminance_cap));
}
-
+#endif
imageStore(dest_buffer, pos + params.target, color);
#endif
diff --git a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
index fde5688301..57349e55b1 100644
--- a/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl
@@ -190,8 +190,7 @@ void main() {
}
vec2 final_pos;
- float grad;
- grad = steps_taken / float(params.num_steps);
+ float grad = (steps_taken + 1.0) / float(params.num_steps);
float initial_fade = params.curve_fade_in == 0.0 ? 1.0 : pow(clamp(grad, 0.0, 1.0), params.curve_fade_in);
float fade = pow(clamp(1.0 - grad, 0.0, 1.0), params.distance_fade) * initial_fade;
final_pos = pos;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 8ded180633..5b2be8e174 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -3871,8 +3871,12 @@ void RendererSceneCull::update_dirty_instances() {
void RendererSceneCull::update() {
//optimize bvhs
- for (uint32_t i = 0; i < scenario_owner.get_rid_count(); i++) {
- Scenario *s = scenario_owner.get_ptr_by_index(i);
+
+ uint32_t rid_count = scenario_owner.get_rid_count();
+ RID *rids = (RID *)alloca(sizeof(RID) * rid_count);
+ scenario_owner.fill_owned_buffer(rids);
+ for (uint32_t i = 0; i < rid_count; i++) {
+ Scenario *s = scenario_owner.get_or_null(rids[i]);
s->indexers[Scenario::INDEXER_GEOMETRY].optimize_incremental(indexer_update_iterations);
s->indexers[Scenario::INDEXER_VOLUMES].optimize_incremental(indexer_update_iterations);
}
diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp
index 19cde610ba..e50ac42027 100644
--- a/servers/rendering/rendering_device_binds.cpp
+++ b/servers/rendering/rendering_device_binds.cpp
@@ -99,11 +99,11 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String
if (reading_versions) {
String l = line.strip_edges();
if (!l.is_empty()) {
- if (l.find("=") == -1) {
+ if (!l.contains("=")) {
base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
break;
}
- if (l.find(";") == -1) {
+ if (!l.contains(";")) {
// We don't require a semicolon per se, but it's needed for clang-format to handle things properly.
base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";
break;
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 43acae42f8..5b43ca4bcd 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -182,7 +182,7 @@ static String _mkid(const String &p_id) {
static String f2sp0(float p_float) {
String num = rtoss(p_float);
- if (num.find(".") == -1 && num.find("e") == -1) {
+ if (!num.contains(".") && !num.contains("e")) {
num += ".0";
}
return num;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 2e1ee41406..ead196b7dd 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -35,18 +35,6 @@
#define HAS_WARNING(flag) (warning_flags & flag)
-static bool _is_text_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
-}
-
-static bool _is_number(char32_t c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex(char32_t c) {
- return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
-}
-
String ShaderLanguage::get_operator_text(Operator p_op) {
static const char *op_names[OP_MAX] = { "==",
"!=",
@@ -543,7 +531,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
default: {
char_idx--; //go back one, since we have no idea what this is
- if (_is_number(GETCHAR(0)) || (GETCHAR(0) == '.' && _is_number(GETCHAR(1)))) {
+ if (is_digit(GETCHAR(0)) || (GETCHAR(0) == '.' && is_digit(GETCHAR(1)))) {
// parse number
bool hexa_found = false;
bool period_found = false;
@@ -584,7 +572,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
const char32_t symbol = String::char_lowercase(GETCHAR(i));
bool error = false;
- if (_is_number(symbol)) {
+ if (is_digit(symbol)) {
if (end_suffix_found) {
error = true;
}
@@ -617,8 +605,8 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
break;
}
}
- } else if (!hexa_found || !_is_hex(symbol)) {
- if (_is_text_char(symbol)) {
+ } else if (!hexa_found || !is_hex_digit(symbol)) {
+ if (is_ascii_identifier_char(symbol)) {
error = true;
} else {
break;
@@ -649,7 +637,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
return _make_token(TK_ERROR, "Invalid (hexadecimal) numeric constant");
}
} else if (period_found || exponent_found || float_suffix_found) { // Float
- if (exponent_found && (!_is_number(last_char) && last_char != 'f')) { // checks for eg: "2E", "2E-", "2E+"
+ if (exponent_found && (!is_digit(last_char) && last_char != 'f')) { // checks for eg: "2E", "2E-", "2E+"
return _make_token(TK_ERROR, "Invalid (float) numeric constant");
}
if (period_found) {
@@ -660,7 +648,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
}
} else {
//checks for eg. "1." or "1.99" notations
- if (last_char != '.' && !_is_number(last_char)) {
+ if (last_char != '.' && !is_digit(last_char)) {
return _make_token(TK_ERROR, "Invalid (float) numeric constant");
}
}
@@ -723,11 +711,11 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
return _make_token(TK_PERIOD);
}
- if (_is_text_char(GETCHAR(0))) {
+ if (is_ascii_identifier_char(GETCHAR(0))) {
// parse identifier
String str;
- while (_is_text_char(GETCHAR(0))) {
+ while (is_ascii_identifier_char(GETCHAR(0))) {
str += char32_t(GETCHAR(0));
char_idx++;
}
@@ -5267,8 +5255,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
} else {
#ifdef DEBUG_ENABLED
- if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
- _add_line_warning(ShaderWarning::FORMATTING_ERROR, RTR("Empty statement. Remove ';' to fix this warning."));
+ if (!p_block || (p_block->block_type != BlockNode::BLOCK_TYPE_FOR_INIT && p_block->block_type != BlockNode::BLOCK_TYPE_FOR_CONDITION)) {
+ if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
+ _add_line_warning(ShaderWarning::FORMATTING_ERROR, RTR("Empty statement. Remove ';' to fix this warning."));
+ }
}
#endif // DEBUG_ENABLED
_set_tkpos(prepos);
@@ -6382,6 +6372,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
bool is_struct = shader->structs.has(tk.text);
+ bool is_var_init = false;
+ bool is_condition = false;
if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block
if (p_just_one) {
@@ -6392,6 +6384,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return OK;
} else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type) || is_struct) {
+ is_var_init = true;
+
String struct_name = "";
if (is_struct) {
struct_name = tk.text;
@@ -6501,9 +6495,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
decl.name = name;
#ifdef DEBUG_ENABLED
- if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG)) {
- if (p_block && p_block->parent_function) {
- StringName func_name = p_block->parent_function->name;
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG) && p_block) {
+ FunctionNode *parent_function = nullptr;
+ {
+ BlockNode *block = p_block;
+ while (block && !block->parent_function) {
+ block = block->parent_block;
+ }
+ parent_function = block->parent_function;
+ }
+ if (parent_function) {
+ StringName func_name = parent_function->name;
if (!used_local_vars.has(func_name)) {
used_local_vars.insert(func_name, Map<StringName, Usage>());
@@ -7331,25 +7333,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
-
- bool empty = false;
+ is_condition = expr->type == Node::TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
if (expr->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
if (op->op == OP_EMPTY) {
- empty = true;
- }
- }
- if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_INIT) {
- if (!empty && expr->type != BlockNode::TYPE_VARIABLE_DECLARATION) {
- _set_error(RTR("The left expression is expected to be a variable declaration."));
- return ERR_PARSE_ERROR;
- }
- }
- if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
- if (!empty && expr->get_datatype() != TYPE_BOOL) {
- _set_error(RTR("The middle expression is expected to be boolean."));
- return ERR_PARSE_ERROR;
+ is_var_init = true;
+ is_condition = true;
}
}
@@ -7358,6 +7348,10 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
if (tk.type == TK_COMMA) {
+ if (!is_condition) {
+ _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ return ERR_PARSE_ERROR;
+ }
continue;
}
if (tk.type != TK_SEMICOLON) {
@@ -7378,6 +7372,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
+ if (p_block) {
+ if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_INIT && !is_var_init) {
+ _set_error(RTR("The left expression is expected to be a variable declaration."));
+ return ERR_PARSE_ERROR;
+ }
+ if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) {
+ _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ return ERR_PARSE_ERROR;
+ }
+ }
+
if (p_just_one) {
break;
}
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 584abbc351..ac181cb5eb 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -933,7 +933,7 @@ Error RenderingServer::mesh_create_surface_data_from_arrays(SurfaceData *r_surfa
}
}
- ERR_FAIL_COND_V_MSG((bsformat & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK) != (format & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK), ERR_INVALID_PARAMETER, "Blend shape format must match the main array format for Vertex, Normal and Tangent arrays.");
+ ERR_FAIL_COND_V_MSG(bsformat != (format & RS::ARRAY_FORMAT_BLEND_SHAPE_MASK), ERR_INVALID_PARAMETER, "Blend shape format must match the main array format for Vertex, Normal and Tangent arrays.");
}
}
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 472fff1bf1..d21f3a3299 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -259,7 +259,7 @@ public:
ARRAY_FORMAT_WEIGHTS = 1 << ARRAY_WEIGHTS,
ARRAY_FORMAT_INDEX = 1 << ARRAY_INDEX,
- ARRAY_FORMAT_BLEND_SHAPE_MASK = (~(ARRAY_FORMAT_COLOR | ARRAY_FORMAT_TEX_UV | ARRAY_FORMAT_TEX_UV2 | ARRAY_FORMAT_BONES | ARRAY_FORMAT_WEIGHTS | ARRAY_FORMAT_CUSTOM0 | ARRAY_FORMAT_CUSTOM1 | ARRAY_FORMAT_CUSTOM2 | ARRAY_FORMAT_CUSTOM3 | ARRAY_FORMAT_INDEX)) & 0x7FFFFFFF,
+ ARRAY_FORMAT_BLEND_SHAPE_MASK = ARRAY_FORMAT_VERTEX | ARRAY_FORMAT_NORMAL | ARRAY_FORMAT_TANGENT,
ARRAY_FORMAT_CUSTOM_BASE = (ARRAY_INDEX + 1),
ARRAY_FORMAT_CUSTOM_BITS = 3,
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index db1f589334..3c5faa4ef7 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -230,8 +230,8 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_is_ready, "shaped");
- GDVIRTUAL_BIND(_shaped_text_get_glyphs, "shaped", "r_glyphs");
- GDVIRTUAL_BIND(_shaped_text_sort_logical, "shaped", "r_glyphs");
+ GDVIRTUAL_BIND(_shaped_text_get_glyphs, "shaped");
+ GDVIRTUAL_BIND(_shaped_text_sort_logical, "shaped");
GDVIRTUAL_BIND(_shaped_text_get_glyph_count, "shaped");
GDVIRTUAL_BIND(_shaped_text_get_range, "shaped");
@@ -243,7 +243,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_get_trim_pos, "shaped");
GDVIRTUAL_BIND(_shaped_text_get_ellipsis_pos, "shaped");
GDVIRTUAL_BIND(_shaped_text_get_ellipsis_glyph_count, "shaped");
- GDVIRTUAL_BIND(_shaped_text_get_ellipsis_glyphs, "shaped", "r_glyphs");
+ GDVIRTUAL_BIND(_shaped_text_get_ellipsis_glyphs, "shaped");
GDVIRTUAL_BIND(_shaped_text_overrun_trim_to_width, "shaped", "width", "trim_flags");
@@ -505,7 +505,7 @@ void TextServerExtension::font_set_hinting(RID p_font_rid, TextServer::Hinting p
}
TextServer::Hinting TextServerExtension::font_get_hinting(RID p_font_rid) const {
- int ret;
+ TextServer::Hinting ret;
if (GDVIRTUAL_CALL(_font_get_hinting, p_font_rid, ret)) {
return (TextServer::Hinting)ret;
}
@@ -955,7 +955,7 @@ void TextServerExtension::shaped_text_set_direction(RID p_shaped, TextServer::Di
}
TextServer::Direction TextServerExtension::shaped_text_get_direction(RID p_shaped) const {
- int ret;
+ TextServer::Direction ret;
if (GDVIRTUAL_CALL(_shaped_text_get_direction, p_shaped, ret)) {
return (TextServer::Direction)ret;
}
@@ -963,7 +963,7 @@ TextServer::Direction TextServerExtension::shaped_text_get_direction(RID p_shape
}
TextServer::Direction TextServerExtension::shaped_text_get_inferred_direction(RID p_shaped) const {
- int ret;
+ TextServer::Direction ret;
if (GDVIRTUAL_CALL(_shaped_text_get_inferred_direction, p_shaped, ret)) {
return (TextServer::Direction)ret;
}
@@ -975,7 +975,7 @@ void TextServerExtension::shaped_text_set_orientation(RID p_shaped, TextServer::
}
TextServer::Orientation TextServerExtension::shaped_text_get_orientation(RID p_shaped) const {
- int ret;
+ TextServer::Orientation ret;
if (GDVIRTUAL_CALL(_shaped_text_get_orientation, p_shaped, ret)) {
return (TextServer::Orientation)ret;
}
@@ -1139,16 +1139,16 @@ bool TextServerExtension::shaped_text_is_ready(RID p_shaped) const {
}
const Glyph *TextServerExtension::shaped_text_get_glyphs(RID p_shaped) const {
- const Glyph *ret;
- if (GDVIRTUAL_CALL(_shaped_text_get_glyphs, p_shaped, &ret)) {
+ GDNativePtr<Glyph> ret;
+ if (GDVIRTUAL_CALL(_shaped_text_get_glyphs, p_shaped, ret)) {
return ret;
}
return nullptr;
}
const Glyph *TextServerExtension::shaped_text_sort_logical(RID p_shaped) {
- const Glyph *ret;
- if (GDVIRTUAL_CALL(_shaped_text_sort_logical, p_shaped, &ret)) {
+ GDNativePtr<Glyph> ret;
+ if (GDVIRTUAL_CALL(_shaped_text_sort_logical, p_shaped, ret)) {
return ret;
}
return nullptr;
@@ -1211,8 +1211,8 @@ int TextServerExtension::shaped_text_get_ellipsis_pos(RID p_shaped) const {
}
const Glyph *TextServerExtension::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
- const Glyph *ret;
- if (GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, &ret)) {
+ GDNativePtr<Glyph> ret;
+ if (GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, ret)) {
return ret;
}
return nullptr;
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index df1f1d7a70..9e7f666be1 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -132,7 +132,7 @@ public:
virtual void font_set_hinting(RID p_font_rid, Hinting p_hinting) override;
virtual Hinting font_get_hinting(RID p_font_rid) const override;
GDVIRTUAL2(_font_set_hinting, RID, Hinting);
- GDVIRTUAL1RC(/*Hinting*/ int, _font_get_hinting, RID);
+ GDVIRTUAL1RC(Hinting, _font_get_hinting, RID);
virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
@@ -317,8 +317,8 @@ public:
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
virtual Direction shaped_text_get_inferred_direction(RID p_shaped) const override;
GDVIRTUAL2(_shaped_text_set_direction, RID, Direction);
- GDVIRTUAL1RC(/*Direction*/ int, _shaped_text_get_direction, RID);
- GDVIRTUAL1RC(/*Direction*/ int, _shaped_text_get_inferred_direction, RID);
+ GDVIRTUAL1RC(Direction, _shaped_text_get_direction, RID);
+ GDVIRTUAL1RC(Direction, _shaped_text_get_inferred_direction, RID);
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
@@ -331,7 +331,7 @@ public:
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
GDVIRTUAL2(_shaped_text_set_orientation, RID, Orientation);
- GDVIRTUAL1RC(/*Orientation*/ int, _shaped_text_get_orientation, RID);
+ GDVIRTUAL1RC(Orientation, _shaped_text_get_orientation, RID);
virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
@@ -380,8 +380,8 @@ public:
virtual const Glyph *shaped_text_get_glyphs(RID p_shaped) const override;
virtual const Glyph *shaped_text_sort_logical(RID p_shaped) override;
virtual int shaped_text_get_glyph_count(RID p_shaped) const override;
- GDVIRTUAL2C(_shaped_text_get_glyphs, RID, GDNativePtr<const Glyph *>);
- GDVIRTUAL2(_shaped_text_sort_logical, RID, GDNativePtr<const Glyph *>);
+ GDVIRTUAL1RC(GDNativePtr<Glyph>, _shaped_text_get_glyphs, RID);
+ GDVIRTUAL1R(GDNativePtr<Glyph>, _shaped_text_sort_logical, RID);
GDVIRTUAL1RC(int, _shaped_text_get_glyph_count, RID);
virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
@@ -400,7 +400,7 @@ public:
virtual int shaped_text_get_ellipsis_glyph_count(RID p_shaped) const override;
GDVIRTUAL1RC(int, _shaped_text_get_trim_pos, RID);
GDVIRTUAL1RC(int, _shaped_text_get_ellipsis_pos, RID);
- GDVIRTUAL2C(_shaped_text_get_ellipsis_glyphs, RID, GDNativePtr<const Glyph *>);
+ GDVIRTUAL1RC(GDNativePtr<Glyph>, _shaped_text_get_ellipsis_glyphs, RID);
GDVIRTUAL1RC(int, _shaped_text_get_ellipsis_glyph_count, RID);
virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint16_t p_trim_flags) override;
diff --git a/servers/text_server.h b/servers/text_server.h
index 6724c02caf..629a633b9f 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -557,7 +557,6 @@ VARIANT_ENUM_CAST(TextServer::SpacingType);
VARIANT_ENUM_CAST(TextServer::FontStyle);
GDVIRTUAL_NATIVE_PTR(Glyph);
-GDVIRTUAL_NATIVE_PTR(Glyph *);
GDVIRTUAL_NATIVE_PTR(CaretInfo);
#endif // TEXT_SERVER_H
diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp
index 62e011654e..7b21eeba04 100644
--- a/servers/xr/xr_positional_tracker.cpp
+++ b/servers/xr/xr_positional_tracker.cpp
@@ -149,7 +149,7 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf
new_pose->set_tracking_confidence(p_tracking_confidence);
poses[p_action_name] = new_pose;
- emit_signal("pose_changed", new_pose);
+ emit_signal(SNAME("pose_changed"), new_pose);
// TODO discuss whether we also want to create and emit an InputEventXRPose event
}
@@ -182,20 +182,20 @@ void XRPositionalTracker::set_input(const StringName &p_action_name, const Varia
case Variant::BOOL: {
bool pressed = p_value;
if (pressed) {
- emit_signal("button_pressed", p_action_name);
+ emit_signal(SNAME("button_pressed"), p_action_name);
} else {
- emit_signal("button_released", p_action_name);
+ emit_signal(SNAME("button_released"), p_action_name);
}
// TODO discuss whether we also want to create and emit an InputEventXRButton event
} break;
case Variant::FLOAT: {
- emit_signal("input_value_changed", p_action_name, p_value);
+ emit_signal(SNAME("input_value_changed"), p_action_name, p_value);
// TODO discuss whether we also want to create and emit an InputEventXRValue event
} break;
case Variant::VECTOR2: {
- emit_signal("input_axis_changed", p_action_name, p_value);
+ emit_signal(SNAME("input_axis_changed"), p_action_name, p_value);
// TODO discuss whether we also want to create and emit an InputEventXRAxis event
} break;
diff --git a/tests/core/math/test_expression.h b/tests/core/math/test_expression.h
index 5a894b20f3..6e3be541b0 100644
--- a/tests/core/math/test_expression.h
+++ b/tests/core/math/test_expression.h
@@ -137,7 +137,7 @@ TEST_CASE("[Expression] Scientific notation") {
expression.parse("2e5") == OK,
"The expression should parse successfully.");
CHECK_MESSAGE(
- Math::is_equal_approx(double(expression.execute()), 25),
+ Math::is_equal_approx(double(expression.execute()), 2e5),
"The expression should return the expected result.");
CHECK_MESSAGE(
diff --git a/tests/core/math/test_math.cpp b/tests/core/math/test_math.cpp
index a24a8fde2b..4182455b7a 100644
--- a/tests/core/math/test_math.cpp
+++ b/tests/core/math/test_math.cpp
@@ -227,7 +227,7 @@ class GetClassAndNamespace {
return TK_SYMBOL;
}
- if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
+ if (code[idx] == '-' || is_digit(code[idx])) {
//a number
const char32_t *rptr;
double number = String::to_float(&code[idx], &rptr);
@@ -235,10 +235,10 @@ class GetClassAndNamespace {
value = number;
return TK_NUMBER;
- } else if ((code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) {
+ } else if (is_ascii_char(code[idx]) || code[idx] > 127) {
String id;
- while ((code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) {
+ while (is_ascii_char(code[idx]) || code[idx] > 127) {
id += code[idx];
idx++;
}
diff --git a/tests/core/math/test_rect2.h b/tests/core/math/test_rect2.h
index d98a94b1b5..0b1106ac3c 100644
--- a/tests/core/math/test_rect2.h
+++ b/tests/core/math/test_rect2.h
@@ -32,15 +32,11 @@
#define TEST_RECT2_H
#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
#include "thirdparty/doctest/doctest.h"
namespace TestRect2 {
-// We also test Rect2i here, for consistency with the source code where Rect2
-// and Rect2i are defined in the same file.
-
-// Rect2
-
TEST_CASE("[Rect2] Constructor methods") {
const Rect2 rect = Rect2(0, 100, 1280, 720);
const Rect2 rect_vector = Rect2(Vector2(0, 100), Vector2(1280, 720));
@@ -304,278 +300,6 @@ TEST_CASE("[Rect2] Merging") {
Rect2(0, 100, 1280, 720).merge(Rect2(-4000, -4000, 100, 100)).is_equal_approx(Rect2(-4000, -4000, 5280, 4820)),
"merge() with non-enclosed Rect2 should return the expected result.");
}
-
-// Rect2i
-
-TEST_CASE("[Rect2i] Constructor methods") {
- Rect2i recti = Rect2i(0, 100, 1280, 720);
- Rect2i recti_vector = Rect2i(Vector2i(0, 100), Vector2i(1280, 720));
- Rect2i recti_copy_recti = Rect2i(recti);
- Rect2i recti_copy_rect = Rect2i(Rect2(0, 100, 1280, 720));
-
- CHECK_MESSAGE(
- recti == recti_vector,
- "Rect2is created with the same dimensions but by different methods should be equal.");
- CHECK_MESSAGE(
- recti == recti_copy_recti,
- "Rect2is created with the same dimensions but by different methods should be equal.");
- CHECK_MESSAGE(
- recti == recti_copy_rect,
- "Rect2is created with the same dimensions but by different methods should be equal.");
-}
-
-TEST_CASE("[Rect2i] String conversion") {
- // Note: This also depends on the Vector2 string representation.
- CHECK_MESSAGE(
- String(Rect2i(0, 100, 1280, 720)) == "[P: (0, 100), S: (1280, 720)]",
- "The string representation should match the expected value.");
-}
-
-TEST_CASE("[Rect2i] Basic getters") {
- const Rect2i rect = Rect2i(0, 100, 1280, 720);
- CHECK_MESSAGE(
- rect.get_position() == Vector2i(0, 100),
- "get_position() should return the expected value.");
- CHECK_MESSAGE(
- rect.get_size() == Vector2i(1280, 720),
- "get_size() should return the expected value.");
- CHECK_MESSAGE(
- rect.get_end() == Vector2i(1280, 820),
- "get_end() should return the expected value.");
- CHECK_MESSAGE(
- rect.get_center() == Vector2i(640, 460),
- "get_center() should return the expected value.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1281, 721).get_center() == Vector2i(640, 460),
- "get_center() should return the expected value.");
-}
-
-TEST_CASE("[Rect2i] Basic setters") {
- Rect2i rect = Rect2i(0, 100, 1280, 720);
- rect.set_end(Vector2i(4000, 4000));
- CHECK_MESSAGE(
- rect == Rect2i(0, 100, 4000, 3900),
- "set_end() should result in the expected Rect2i.");
-
- rect = Rect2i(0, 100, 1280, 720);
- rect.set_position(Vector2i(4000, 4000));
- CHECK_MESSAGE(
- rect == Rect2i(4000, 4000, 1280, 720),
- "set_position() should result in the expected Rect2i.");
-
- rect = Rect2i(0, 100, 1280, 720);
- rect.set_size(Vector2i(4000, 4000));
- CHECK_MESSAGE(
- rect == Rect2i(0, 100, 4000, 4000),
- "set_size() should result in the expected Rect2i.");
-}
-
-TEST_CASE("[Rect2i] Area getters") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).get_area() == 921'600,
- "get_area() should return the expected value.");
- CHECK_MESSAGE(
- Rect2i(0, 100, -1280, -720).get_area() == 921'600,
- "get_area() should return the expected value.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, -720).get_area() == -921'600,
- "get_area() should return the expected value.");
- CHECK_MESSAGE(
- Rect2i(0, 100, -1280, 720).get_area() == -921'600,
- "get_area() should return the expected value.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 0, 720).get_area() == 0,
- "get_area() should return the expected value.");
-
- CHECK_MESSAGE(
- !Rect2i(0, 100, 1280, 720).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with an area.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 0, 500).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 500, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 0, 0).has_no_area(),
- "has_no_area() should return the expected value on Rect2i with no area.");
-}
-
-TEST_CASE("[Rect2i] Absolute coordinates") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).abs() == Rect2i(0, 100, 1280, 720),
- "abs() should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, -100, 1280, 720).abs() == Rect2i(0, -100, 1280, 720),
- "abs() should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, -100, -1280, -720).abs() == Rect2i(-1280, -820, 1280, 720),
- "abs() should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, 100, -1280, 720).abs() == Rect2i(-1280, 100, 1280, 720),
- "abs() should return the expected Rect2i.");
-}
-
-TEST_CASE("[Rect2i] Intersection") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).intersection(Rect2i(0, 300, 100, 100)) == Rect2i(0, 300, 100, 100),
- "intersection() with fully enclosed Rect2i should return the expected result.");
- // The resulting Rect2i is 100 pixels high because the first Rect2i is vertically offset by 100 pixels.
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).intersection(Rect2i(1200, 700, 100, 100)) == Rect2i(1200, 700, 80, 100),
- "intersection() with partially enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).intersection(Rect2i(-4000, -4000, 100, 100)) == Rect2i(),
- "intersection() with non-enclosed Rect2i should return the expected result.");
-}
-
-TEST_CASE("[Rect2i] Enclosing") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 300, 100, 100)),
- "encloses() with fully contained Rect2i should return the expected result.");
- CHECK_MESSAGE(
- !Rect2i(0, 100, 1280, 720).encloses(Rect2i(1200, 700, 100, 100)),
- "encloses() with partially contained Rect2i should return the expected result.");
- CHECK_MESSAGE(
- !Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)),
- "encloses() with non-contained Rect2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 100, 1280, 720)),
- "encloses() with identical Rect2i should return the expected result.");
-}
-
-TEST_CASE("[Rect2i] Expanding") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).expand(Vector2i(500, 600)) == Rect2i(0, 100, 1280, 720),
- "expand() with contained Vector2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).expand(Vector2i(0, 0)) == Rect2i(0, 0, 1280, 820),
- "expand() with non-contained Vector2i should return the expected result.");
-}
-
-TEST_CASE("[Rect2i] Growing") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow(100) == Rect2i(-100, 0, 1480, 920),
- "grow() with positive value should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow(-100) == Rect2i(100, 200, 1080, 520),
- "grow() with negative value should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow(-4000) == Rect2i(4000, 4100, -6720, -7280),
- "grow() with large negative value should return the expected Rect2i.");
-
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow_individual(100, 200, 300, 400) == Rect2i(-100, -100, 1680, 1320),
- "grow_individual() with positive values should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow_individual(-100, 200, 300, -400) == Rect2i(100, -100, 1480, 520),
- "grow_individual() with positive and negative values should return the expected Rect2i.");
-
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, 500) == Rect2i(0, -400, 1280, 1220),
- "grow_side() with positive value should return the expected Rect2i.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, -500) == Rect2i(0, 600, 1280, 220),
- "grow_side() with negative value should return the expected Rect2i.");
-}
-
-TEST_CASE("[Rect2i] Has point") {
- Rect2i rect = Rect2i(0, 100, 1280, 720);
- CHECK_MESSAGE(
- rect.has_point(Vector2i(500, 600)),
- "has_point() with contained Vector2i should return the expected result.");
- CHECK_MESSAGE(
- !rect.has_point(Vector2i(0, 0)),
- "has_point() with non-contained Vector2i should return the expected result.");
-
- CHECK_MESSAGE(
- rect.has_point(rect.position),
- "has_point() with positive size should include `position`.");
- CHECK_MESSAGE(
- rect.has_point(rect.position + Vector2i(1, 1)),
- "has_point() with positive size should include `position + (1, 1)`.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + Vector2i(1, -1)),
- "has_point() with positive size should not include `position + (1, -1)`.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + rect.size),
- "has_point() with positive size should not include `position + size`.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + rect.size + Vector2i(1, 1)),
- "has_point() with positive size should not include `position + size + (1, 1)`.");
- CHECK_MESSAGE(
- rect.has_point(rect.position + rect.size + Vector2i(-1, -1)),
- "has_point() with positive size should include `position + size + (-1, -1)`.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + rect.size + Vector2i(-1, 1)),
- "has_point() with positive size should not include `position + size + (-1, 1)`.");
-
- CHECK_MESSAGE(
- rect.has_point(rect.position + Vector2i(0, 10)),
- "has_point() with point located on left edge should return true.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + Vector2i(rect.size.x, 10)),
- "has_point() with point located on right edge should return false.");
- CHECK_MESSAGE(
- rect.has_point(rect.position + Vector2i(10, 0)),
- "has_point() with point located on top edge should return true.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + Vector2i(10, rect.size.y)),
- "has_point() with point located on bottom edge should return false.");
-
- /*
- // FIXME: Disabled for now until GH-37617 is fixed one way or another.
- // More tests should then be written like for the positive size case.
- rect = Rect2i(0, 100, -1280, -720);
- CHECK_MESSAGE(
- rect.has_point(rect.position),
- "has_point() with negative size should include `position`.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + rect.size),
- "has_point() with negative size should not include `position + size`.");
- */
-
- rect = Rect2i(-4000, -200, 1280, 720);
- CHECK_MESSAGE(
- rect.has_point(rect.position + Vector2i(0, 10)),
- "has_point() with negative position and point located on left edge should return true.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + Vector2i(rect.size.x, 10)),
- "has_point() with negative position and point located on right edge should return false.");
- CHECK_MESSAGE(
- rect.has_point(rect.position + Vector2i(10, 0)),
- "has_point() with negative position and point located on top edge should return true.");
- CHECK_MESSAGE(
- !rect.has_point(rect.position + Vector2i(10, rect.size.y)),
- "has_point() with negative position and point located on bottom edge should return false.");
-}
-
-TEST_CASE("[Rect2i] Intersection") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).intersects(Rect2i(0, 300, 100, 100)),
- "intersects() with fully enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).intersects(Rect2i(1200, 700, 100, 100)),
- "intersects() with partially enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- !Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)),
- "intersects() with non-enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- !Rect2i(0, 0, 2, 2).intersects(Rect2i(2, 2, 2, 2)),
- "intersects() with adjacent Rect2i should return the expected result.");
-}
-
-TEST_CASE("[Rect2i] Merging") {
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).merge(Rect2i(0, 300, 100, 100)) == Rect2i(0, 100, 1280, 720),
- "merge() with fully enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).merge(Rect2i(1200, 700, 100, 100)) == Rect2i(0, 100, 1300, 720),
- "merge() with partially enclosed Rect2i should return the expected result.");
- CHECK_MESSAGE(
- Rect2i(0, 100, 1280, 720).merge(Rect2i(-4000, -4000, 100, 100)) == Rect2i(-4000, -4000, 5280, 4820),
- "merge() with non-enclosed Rect2i should return the expected result.");
-}
} // namespace TestRect2
#endif // TEST_RECT2_H
diff --git a/tests/core/math/test_rect2i.h b/tests/core/math/test_rect2i.h
new file mode 100644
index 0000000000..0d1a088a66
--- /dev/null
+++ b/tests/core/math/test_rect2i.h
@@ -0,0 +1,311 @@
+/*************************************************************************/
+/* test_rect2i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_RECT2I_H
+#define TEST_RECT2I_H
+
+#include "core/math/rect2.h"
+#include "core/math/rect2i.h"
+
+#include "thirdparty/doctest/doctest.h"
+
+namespace TestRect2i {
+TEST_CASE("[Rect2i] Constructor methods") {
+ Rect2i recti = Rect2i(0, 100, 1280, 720);
+ Rect2i recti_vector = Rect2i(Vector2i(0, 100), Vector2i(1280, 720));
+ Rect2i recti_copy_recti = Rect2i(recti);
+ Rect2i recti_copy_rect = Rect2i(Rect2(0, 100, 1280, 720));
+
+ CHECK_MESSAGE(
+ recti == recti_vector,
+ "Rect2is created with the same dimensions but by different methods should be equal.");
+ CHECK_MESSAGE(
+ recti == recti_copy_recti,
+ "Rect2is created with the same dimensions but by different methods should be equal.");
+ CHECK_MESSAGE(
+ recti == recti_copy_rect,
+ "Rect2is created with the same dimensions but by different methods should be equal.");
+}
+
+TEST_CASE("[Rect2i] String conversion") {
+ // Note: This also depends on the Vector2 string representation.
+ CHECK_MESSAGE(
+ String(Rect2i(0, 100, 1280, 720)) == "[P: (0, 100), S: (1280, 720)]",
+ "The string representation should match the expected value.");
+}
+
+TEST_CASE("[Rect2i] Basic getters") {
+ const Rect2i rect = Rect2i(0, 100, 1280, 720);
+ CHECK_MESSAGE(
+ rect.get_position() == Vector2i(0, 100),
+ "get_position() should return the expected value.");
+ CHECK_MESSAGE(
+ rect.get_size() == Vector2i(1280, 720),
+ "get_size() should return the expected value.");
+ CHECK_MESSAGE(
+ rect.get_end() == Vector2i(1280, 820),
+ "get_end() should return the expected value.");
+ CHECK_MESSAGE(
+ rect.get_center() == Vector2i(640, 460),
+ "get_center() should return the expected value.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1281, 721).get_center() == Vector2i(640, 460),
+ "get_center() should return the expected value.");
+}
+
+TEST_CASE("[Rect2i] Basic setters") {
+ Rect2i rect = Rect2i(0, 100, 1280, 720);
+ rect.set_end(Vector2i(4000, 4000));
+ CHECK_MESSAGE(
+ rect == Rect2i(0, 100, 4000, 3900),
+ "set_end() should result in the expected Rect2i.");
+
+ rect = Rect2i(0, 100, 1280, 720);
+ rect.set_position(Vector2i(4000, 4000));
+ CHECK_MESSAGE(
+ rect == Rect2i(4000, 4000, 1280, 720),
+ "set_position() should result in the expected Rect2i.");
+
+ rect = Rect2i(0, 100, 1280, 720);
+ rect.set_size(Vector2i(4000, 4000));
+ CHECK_MESSAGE(
+ rect == Rect2i(0, 100, 4000, 4000),
+ "set_size() should result in the expected Rect2i.");
+}
+
+TEST_CASE("[Rect2i] Area getters") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).get_area() == 921'600,
+ "get_area() should return the expected value.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, -1280, -720).get_area() == 921'600,
+ "get_area() should return the expected value.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, -720).get_area() == -921'600,
+ "get_area() should return the expected value.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, -1280, 720).get_area() == -921'600,
+ "get_area() should return the expected value.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 0, 720).get_area() == 0,
+ "get_area() should return the expected value.");
+
+ CHECK_MESSAGE(
+ !Rect2i(0, 100, 1280, 720).has_no_area(),
+ "has_no_area() should return the expected value on Rect2i with an area.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 0, 500).has_no_area(),
+ "has_no_area() should return the expected value on Rect2i with no area.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 500, 0).has_no_area(),
+ "has_no_area() should return the expected value on Rect2i with no area.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 0, 0).has_no_area(),
+ "has_no_area() should return the expected value on Rect2i with no area.");
+}
+
+TEST_CASE("[Rect2i] Absolute coordinates") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).abs() == Rect2i(0, 100, 1280, 720),
+ "abs() should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, -100, 1280, 720).abs() == Rect2i(0, -100, 1280, 720),
+ "abs() should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, -100, -1280, -720).abs() == Rect2i(-1280, -820, 1280, 720),
+ "abs() should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, -1280, 720).abs() == Rect2i(-1280, 100, 1280, 720),
+ "abs() should return the expected Rect2i.");
+}
+
+TEST_CASE("[Rect2i] Intersection") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).intersection(Rect2i(0, 300, 100, 100)) == Rect2i(0, 300, 100, 100),
+ "intersection() with fully enclosed Rect2i should return the expected result.");
+ // The resulting Rect2i is 100 pixels high because the first Rect2i is vertically offset by 100 pixels.
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).intersection(Rect2i(1200, 700, 100, 100)) == Rect2i(1200, 700, 80, 100),
+ "intersection() with partially enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).intersection(Rect2i(-4000, -4000, 100, 100)) == Rect2i(),
+ "intersection() with non-enclosed Rect2i should return the expected result.");
+}
+
+TEST_CASE("[Rect2i] Enclosing") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 300, 100, 100)),
+ "encloses() with fully contained Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 100, 1280, 720).encloses(Rect2i(1200, 700, 100, 100)),
+ "encloses() with partially contained Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 100, 1280, 720).encloses(Rect2i(-4000, -4000, 100, 100)),
+ "encloses() with non-contained Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).encloses(Rect2i(0, 100, 1280, 720)),
+ "encloses() with identical Rect2i should return the expected result.");
+}
+
+TEST_CASE("[Rect2i] Expanding") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).expand(Vector2i(500, 600)) == Rect2i(0, 100, 1280, 720),
+ "expand() with contained Vector2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).expand(Vector2i(0, 0)) == Rect2i(0, 0, 1280, 820),
+ "expand() with non-contained Vector2i should return the expected result.");
+}
+
+TEST_CASE("[Rect2i] Growing") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow(100) == Rect2i(-100, 0, 1480, 920),
+ "grow() with positive value should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow(-100) == Rect2i(100, 200, 1080, 520),
+ "grow() with negative value should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow(-4000) == Rect2i(4000, 4100, -6720, -7280),
+ "grow() with large negative value should return the expected Rect2i.");
+
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow_individual(100, 200, 300, 400) == Rect2i(-100, -100, 1680, 1320),
+ "grow_individual() with positive values should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow_individual(-100, 200, 300, -400) == Rect2i(100, -100, 1480, 520),
+ "grow_individual() with positive and negative values should return the expected Rect2i.");
+
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, 500) == Rect2i(0, -400, 1280, 1220),
+ "grow_side() with positive value should return the expected Rect2i.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).grow_side(SIDE_TOP, -500) == Rect2i(0, 600, 1280, 220),
+ "grow_side() with negative value should return the expected Rect2i.");
+}
+
+TEST_CASE("[Rect2i] Has point") {
+ Rect2i rect = Rect2i(0, 100, 1280, 720);
+ CHECK_MESSAGE(
+ rect.has_point(Vector2i(500, 600)),
+ "has_point() with contained Vector2i should return the expected result.");
+ CHECK_MESSAGE(
+ !rect.has_point(Vector2i(0, 0)),
+ "has_point() with non-contained Vector2i should return the expected result.");
+
+ CHECK_MESSAGE(
+ rect.has_point(rect.position),
+ "has_point() with positive size should include `position`.");
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + Vector2i(1, 1)),
+ "has_point() with positive size should include `position + (1, 1)`.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + Vector2i(1, -1)),
+ "has_point() with positive size should not include `position + (1, -1)`.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + rect.size),
+ "has_point() with positive size should not include `position + size`.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + rect.size + Vector2i(1, 1)),
+ "has_point() with positive size should not include `position + size + (1, 1)`.");
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + rect.size + Vector2i(-1, -1)),
+ "has_point() with positive size should include `position + size + (-1, -1)`.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + rect.size + Vector2i(-1, 1)),
+ "has_point() with positive size should not include `position + size + (-1, 1)`.");
+
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + Vector2i(0, 10)),
+ "has_point() with point located on left edge should return true.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + Vector2i(rect.size.x, 10)),
+ "has_point() with point located on right edge should return false.");
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + Vector2i(10, 0)),
+ "has_point() with point located on top edge should return true.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + Vector2i(10, rect.size.y)),
+ "has_point() with point located on bottom edge should return false.");
+
+ /*
+ // FIXME: Disabled for now until GH-37617 is fixed one way or another.
+ // More tests should then be written like for the positive size case.
+ rect = Rect2i(0, 100, -1280, -720);
+ CHECK_MESSAGE(
+ rect.has_point(rect.position),
+ "has_point() with negative size should include `position`.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + rect.size),
+ "has_point() with negative size should not include `position + size`.");
+ */
+
+ rect = Rect2i(-4000, -200, 1280, 720);
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + Vector2i(0, 10)),
+ "has_point() with negative position and point located on left edge should return true.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + Vector2i(rect.size.x, 10)),
+ "has_point() with negative position and point located on right edge should return false.");
+ CHECK_MESSAGE(
+ rect.has_point(rect.position + Vector2i(10, 0)),
+ "has_point() with negative position and point located on top edge should return true.");
+ CHECK_MESSAGE(
+ !rect.has_point(rect.position + Vector2i(10, rect.size.y)),
+ "has_point() with negative position and point located on bottom edge should return false.");
+}
+
+TEST_CASE("[Rect2i] Intersection") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).intersects(Rect2i(0, 300, 100, 100)),
+ "intersects() with fully enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).intersects(Rect2i(1200, 700, 100, 100)),
+ "intersects() with partially enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 100, 1280, 720).intersects(Rect2i(-4000, -4000, 100, 100)),
+ "intersects() with non-enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ !Rect2i(0, 0, 2, 2).intersects(Rect2i(2, 2, 2, 2)),
+ "intersects() with adjacent Rect2i should return the expected result.");
+}
+
+TEST_CASE("[Rect2i] Merging") {
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).merge(Rect2i(0, 300, 100, 100)) == Rect2i(0, 100, 1280, 720),
+ "merge() with fully enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).merge(Rect2i(1200, 700, 100, 100)) == Rect2i(0, 100, 1300, 720),
+ "merge() with partially enclosed Rect2i should return the expected result.");
+ CHECK_MESSAGE(
+ Rect2i(0, 100, 1280, 720).merge(Rect2i(-4000, -4000, 100, 100)) == Rect2i(-4000, -4000, 5280, 4820),
+ "merge() with non-enclosed Rect2i should return the expected result.");
+}
+} // namespace TestRect2i
+
+#endif // TEST_RECT2I_H
diff --git a/tests/core/math/test_vector2.h b/tests/core/math/test_vector2.h
index cb447acd17..ff60467bf4 100644
--- a/tests/core/math/test_vector2.h
+++ b/tests/core/math/test_vector2.h
@@ -32,6 +32,7 @@
#define TEST_VECTOR2_H
#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "tests/test_macros.h"
namespace TestVector2 {
diff --git a/tests/core/math/test_vector2i.h b/tests/core/math/test_vector2i.h
index 86e254654d..841bb793a4 100644
--- a/tests/core/math/test_vector2i.h
+++ b/tests/core/math/test_vector2i.h
@@ -32,6 +32,7 @@
#define TEST_VECTOR2I_H
#include "core/math/vector2.h"
+#include "core/math/vector2i.h"
#include "tests/test_macros.h"
namespace TestVector2i {
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 0446f749cf..bf78298450 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -245,6 +245,19 @@ TEST_CASE("[String] Testing for empty string") {
CHECK(String("").is_empty());
}
+TEST_CASE("[String] Contains") {
+ String s = "C:\\Godot\\project\\string_test.tscn";
+ CHECK(s.contains(":\\"));
+ CHECK(s.contains("Godot"));
+ CHECK(s.contains(String("project\\string_test")));
+ CHECK(s.contains(String("\\string_test.tscn")));
+
+ CHECK(!s.contains("://"));
+ CHECK(!s.contains("Godoh"));
+ CHECK(!s.contains(String("project\\string test")));
+ CHECK(!s.contains(String("\\char_test.tscn")));
+}
+
TEST_CASE("[String] Test chr") {
CHECK(String::chr('H') == "H");
CHECK(String::chr(0x3012)[0] == 0x3012);
diff --git a/tests/scene/test_animation.h b/tests/scene/test_animation.h
new file mode 100644
index 0000000000..9199713fd9
--- /dev/null
+++ b/tests/scene/test_animation.h
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* test_animation.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEST_ANIMATION_H
+#define TEST_ANIMATION_H
+
+#include "scene/resources/animation.h"
+
+#include "tests/test_macros.h"
+
+namespace TestAnimation {
+
+TEST_CASE("[Animation] Empty animation getters") {
+ const Ref<Animation> animation = memnew(Animation);
+
+ CHECK(Math::is_equal_approx(animation->get_length(), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->get_step(), real_t(0.1)));
+}
+
+TEST_CASE("[Animation] Create value track") {
+ // This creates an animation that makes the node "Enemy" move to the right by
+ // 100 pixels in 0.5 seconds.
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_VALUE);
+ CHECK(track_index == 0);
+ animation->track_set_path(track_index, NodePath("Enemy:position:x"));
+ animation->track_insert_key(track_index, 0.0, 0);
+ animation->track_insert_key(track_index, 0.5, 100);
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+ CHECK(int(animation->track_get_key_value(0, 0)) == 0);
+ CHECK(int(animation->track_get_key_value(0, 1)) == 100);
+
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, -0.2), 0.0));
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.0), 0.0));
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.2), 40.0));
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.4), 80.0));
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.5), 100.0));
+ CHECK(Math::is_equal_approx(animation->value_track_interpolate(0, 0.6), 100.0));
+
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0)));
+
+ ERR_PRINT_OFF;
+ // Nonexistent keys.
+ CHECK(animation->track_get_key_value(0, 2).is_null());
+ CHECK(animation->track_get_key_value(0, -1).is_null());
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 2), real_t(-1.0)));
+ // Nonexistent track (and keys).
+ CHECK(animation->track_get_key_value(1, 0).is_null());
+ CHECK(animation->track_get_key_value(1, 1).is_null());
+ CHECK(animation->track_get_key_value(1, 2).is_null());
+ CHECK(animation->track_get_key_value(1, -1).is_null());
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(1, 0), real_t(-1.0)));
+
+ // This is a value track, so the methods below should return errors.
+ CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0)));
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Animation] Create 3D position track") {
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(track_index, NodePath("Enemy:position"));
+ animation->position_track_insert_key(track_index, 0.0, Vector3(0, 1, 2));
+ animation->position_track_insert_key(track_index, 0.5, Vector3(3.5, 4, 5));
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+ CHECK(Vector3(animation->track_get_key_value(0, 0)).is_equal_approx(Vector3(0, 1, 2)));
+ CHECK(Vector3(animation->track_get_key_value(0, 1)).is_equal_approx(Vector3(3.5, 4, 5)));
+
+ Vector3 r_interpolation;
+
+ CHECK(animation->position_track_interpolate(0, -0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
+
+ CHECK(animation->position_track_interpolate(0, 0.0, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
+
+ CHECK(animation->position_track_interpolate(0, 0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(1.4, 2.2, 3.2)));
+
+ CHECK(animation->position_track_interpolate(0, 0.4, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(2.8, 3.4, 4.4)));
+
+ CHECK(animation->position_track_interpolate(0, 0.5, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
+
+ CHECK(animation->position_track_interpolate(0, 0.6, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
+
+ // 3D position tracks always use linear interpolation for performance reasons.
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0)));
+
+ // This is a 3D position track, so the methods below should return errors.
+ ERR_PRINT_OFF;
+ CHECK(animation->value_track_interpolate(0, 0.0).is_null());
+ CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0)));
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Animation] Create 3D rotation track") {
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(track_index, NodePath("Enemy:rotation"));
+ animation->rotation_track_insert_key(track_index, 0.0, Quaternion(Vector3(0, 1, 2)));
+ animation->rotation_track_insert_key(track_index, 0.5, Quaternion(Vector3(3.5, 4, 5)));
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+ CHECK(Quaternion(animation->track_get_key_value(0, 0)).is_equal_approx(Quaternion(Vector3(0, 1, 2))));
+ CHECK(Quaternion(animation->track_get_key_value(0, 1)).is_equal_approx(Quaternion(Vector3(3.5, 4, 5))));
+
+ Quaternion r_interpolation;
+
+ CHECK(animation->rotation_track_interpolate(0, -0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.403423, 0.259035, 0.73846, 0.47416)));
+
+ CHECK(animation->rotation_track_interpolate(0, 0.0, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.403423, 0.259035, 0.73846, 0.47416)));
+
+ CHECK(animation->rotation_track_interpolate(0, 0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.336182, 0.30704, 0.751515, 0.477425)));
+
+ CHECK(animation->rotation_track_interpolate(0, 0.4, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.266585, 0.352893, 0.759303, 0.477344)));
+
+ CHECK(animation->rotation_track_interpolate(0, 0.5, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.231055, 0.374912, 0.761204, 0.476048)));
+
+ CHECK(animation->rotation_track_interpolate(0, 0.6, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Quaternion(0.231055, 0.374912, 0.761204, 0.476048)));
+
+ // 3D rotation tracks always use linear interpolation for performance reasons.
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0)));
+
+ // This is a 3D rotation track, so the methods below should return errors.
+ ERR_PRINT_OFF;
+ CHECK(animation->value_track_interpolate(0, 0.0).is_null());
+ CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0)));
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Animation] Create 3D scale track") {
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(track_index, NodePath("Enemy:scale"));
+ animation->scale_track_insert_key(track_index, 0.0, Vector3(0, 1, 2));
+ animation->scale_track_insert_key(track_index, 0.5, Vector3(3.5, 4, 5));
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+ CHECK(Vector3(animation->track_get_key_value(0, 0)).is_equal_approx(Vector3(0, 1, 2)));
+ CHECK(Vector3(animation->track_get_key_value(0, 1)).is_equal_approx(Vector3(3.5, 4, 5)));
+
+ Vector3 r_interpolation;
+
+ CHECK(animation->scale_track_interpolate(0, -0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
+
+ CHECK(animation->scale_track_interpolate(0, 0.0, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(0, 1, 2)));
+
+ CHECK(animation->scale_track_interpolate(0, 0.2, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(1.4, 2.2, 3.2)));
+
+ CHECK(animation->scale_track_interpolate(0, 0.4, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(2.8, 3.4, 4.4)));
+
+ CHECK(animation->scale_track_interpolate(0, 0.5, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
+
+ CHECK(animation->scale_track_interpolate(0, 0.6, &r_interpolation) == OK);
+ CHECK(r_interpolation.is_equal_approx(Vector3(3.5, 4, 5)));
+
+ // 3D scale tracks always use linear interpolation for performance reasons.
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0)));
+
+ // This is a 3D scale track, so the methods below should return errors.
+ ERR_PRINT_OFF;
+ CHECK(animation->value_track_interpolate(0, 0.0).is_null());
+ CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0)));
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Animation] Create blend shape track") {
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_BLEND_SHAPE);
+ animation->track_set_path(track_index, NodePath("Enemy:scale"));
+ // Negative values for blend shapes should work as expected.
+ animation->blend_shape_track_insert_key(track_index, 0.0, -1.0);
+ animation->blend_shape_track_insert_key(track_index, 0.5, 1.0);
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+
+ float r_blend = 0.0f;
+
+ CHECK(animation->blend_shape_track_get_key(0, 0, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, -1.0f));
+
+ CHECK(animation->blend_shape_track_get_key(0, 1, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, 1.0f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, -0.2, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, -1.0f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, -1.0f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, 0.2, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, -0.2f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, 0.4, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, 0.6f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, 0.5, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, 1.0f));
+
+ CHECK(animation->blend_shape_track_interpolate(0, 0.6, &r_blend) == OK);
+ CHECK(Math::is_equal_approx(r_blend, 1.0f));
+
+ // Blend shape tracks always use linear interpolation for performance reasons.
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 0), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->track_get_key_transition(0, 1), real_t(1.0)));
+
+ // This is a blend shape track, so the methods below should return errors.
+ ERR_PRINT_OFF;
+ CHECK(animation->value_track_interpolate(0, 0.0).is_null());
+ CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(Math::is_zero_approx(animation->bezier_track_interpolate(0, 0.0)));
+ ERR_PRINT_ON;
+}
+
+TEST_CASE("[Animation] Create Bezier track") {
+ Ref<Animation> animation = memnew(Animation);
+ const int track_index = animation->add_track(Animation::TYPE_BEZIER);
+ animation->track_set_path(track_index, NodePath("Enemy:scale"));
+ animation->bezier_track_insert_key(track_index, 0.0, -1.0, Vector2(-1, -1), Vector2(1, 1));
+ animation->bezier_track_insert_key(track_index, 0.5, 1.0, Vector2(0, 1), Vector2(1, 0.5));
+
+ CHECK(animation->get_track_count() == 1);
+ CHECK(!animation->track_is_compressed(0));
+
+ CHECK(Math::is_equal_approx(animation->bezier_track_get_key_value(0, 0), real_t(-1.0)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_get_key_value(0, 1), real_t(1.0)));
+
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, -0.2), real_t(-1.0)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.0), real_t(-1.0)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.2), real_t(-0.76057207584381)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.4), real_t(-0.39975279569626)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.5), real_t(1.0)));
+ CHECK(Math::is_equal_approx(animation->bezier_track_interpolate(0, 0.6), real_t(1.0)));
+
+ // This is a bezier track, so the methods below should return errors.
+ ERR_PRINT_OFF;
+ CHECK(animation->value_track_interpolate(0, 0.0).is_null());
+ CHECK(animation->position_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->rotation_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->scale_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ CHECK(animation->blend_shape_track_interpolate(0, 0.0, nullptr) == ERR_INVALID_PARAMETER);
+ ERR_PRINT_ON;
+}
+
+} // namespace TestAnimation
+
+#endif // TEST_ANIMATION_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 0190fa5184..504b83c2b0 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -48,6 +48,7 @@
#include "tests/core/math/test_math.h"
#include "tests/core/math/test_random_number_generator.h"
#include "tests/core/math/test_rect2.h"
+#include "tests/core/math/test_rect2i.h"
#include "tests/core/math/test_vector2.h"
#include "tests/core/math/test_vector2i.h"
#include "tests/core/math/test_vector3.h"
@@ -72,6 +73,7 @@
#include "tests/core/variant/test_array.h"
#include "tests/core/variant/test_dictionary.h"
#include "tests/core/variant/test_variant.h"
+#include "tests/scene/test_animation.h"
#include "tests/scene/test_code_edit.h"
#include "tests/scene/test_curve.h"
#include "tests/scene/test_gradient.h"
diff --git a/thirdparty/README.md b/thirdparty/README.md
index e1f911a9f9..34c33c3b56 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -52,13 +52,13 @@ Includes some patches in the `patches` folder which have been sent upstream.
## cvtt
-- Upstream: https://github.com/elasota/cvtt
-- Version: 1.0.0-beta4 (cc8472a04ba110fe999c686d07af40f7839051fd, 2018)
+- Upstream: https://github.com/elasota/ConvectionKernels
+- Version: git (dc2dbbe0ae2cf2be06ef56d1021e2222a56c7fe2, 2021)
- License: MIT
Files extracted from upstream source:
-- all .cpp, .h, and .txt files in ConvectionKernels/
+- all .cpp, .h, and .txt files except the folders MakeTables and etc2packer.
## doctest
diff --git a/thirdparty/cvtt/ConvectionKernels.cpp b/thirdparty/cvtt/ConvectionKernels.cpp
deleted file mode 100644
index 8d379344e1..0000000000
--- a/thirdparty/cvtt/ConvectionKernels.cpp
+++ /dev/null
@@ -1,7586 +0,0 @@
-/*
-Convection Texture Tools
-Copyright (c) 2018 Eric Lasota
-
-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.
-
--------------------------------------------------------------------------------------
-
-Portions based on DirectX Texture Library (DirectXTex)
-
-Copyright (c) Microsoft Corporation. All rights reserved.
-Licensed under the MIT License.
-
-http://go.microsoft.com/fwlink/?LinkId=248926
-*/
-#include "ConvectionKernels.h"
-#include "ConvectionKernels_BC7_SingleColor.h"
-
-#if (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(_M_X64) || defined(__SSE2__)
-#define CVTT_USE_SSE2
-#endif
-
-#ifdef CVTT_USE_SSE2
-#include <emmintrin.h>
-#endif
-
-#include <float.h>
-#include <assert.h>
-#include <string.h>
-#include <algorithm>
-#include <math.h>
-
-#define UNREFERENCED_PARAMETER(n) ((void)n)
-
-namespace cvtt
-{
-#ifdef CVTT_USE_SSE2
- // SSE2 version
- struct ParallelMath
- {
- typedef uint16_t ScalarUInt16;
- typedef int16_t ScalarSInt16;
-
- template<unsigned int TRoundingMode>
- struct RoundForScope
- {
- unsigned int m_oldCSR;
-
- RoundForScope()
- {
- m_oldCSR = _mm_getcsr();
- _mm_setcsr((m_oldCSR & ~_MM_ROUND_MASK) | (TRoundingMode));
- }
-
- ~RoundForScope()
- {
- _mm_setcsr(m_oldCSR);
- }
- };
-
- struct RoundTowardZeroForScope : RoundForScope<_MM_ROUND_TOWARD_ZERO>
- {
- };
-
- struct RoundTowardNearestForScope : RoundForScope<_MM_ROUND_NEAREST>
- {
- };
-
- struct RoundUpForScope : RoundForScope<_MM_ROUND_UP>
- {
- };
-
- struct RoundDownForScope : RoundForScope<_MM_ROUND_DOWN>
- {
- };
-
- static const int ParallelSize = 8;
-
- enum Int16Subtype
- {
- IntSubtype_Signed,
- IntSubtype_UnsignedFull,
- IntSubtype_UnsignedTruncated,
- IntSubtype_Abstract,
- };
-
- template<int TSubtype>
- struct VInt16
- {
- __m128i m_value;
-
- inline VInt16 operator+(int16_t other) const
- {
- VInt16 result;
- result.m_value = _mm_add_epi16(m_value, _mm_set1_epi16(static_cast<int16_t>(other)));
- return result;
- }
-
- inline VInt16 operator+(const VInt16 &other) const
- {
- VInt16 result;
- result.m_value = _mm_add_epi16(m_value, other.m_value);
- return result;
- }
-
- inline VInt16 operator|(const VInt16 &other) const
- {
- VInt16 result;
- result.m_value = _mm_or_si128(m_value, other.m_value);
- return result;
- }
-
- inline VInt16 operator&(const VInt16 &other) const
- {
- VInt16 result;
- result.m_value = _mm_and_si128(m_value, other.m_value);
- return result;
- }
-
- inline VInt16 operator-(const VInt16 &other) const
- {
- VInt16 result;
- result.m_value = _mm_sub_epi16(m_value, other.m_value);
- return result;
- }
-
- inline VInt16 operator<<(int bits) const
- {
- VInt16 result;
- result.m_value = _mm_slli_epi16(m_value, bits);
- return result;
- }
- };
-
- typedef VInt16<IntSubtype_Signed> SInt16;
- typedef VInt16<IntSubtype_UnsignedFull> UInt16;
- typedef VInt16<IntSubtype_UnsignedTruncated> UInt15;
- typedef VInt16<IntSubtype_Abstract> AInt16;
-
- template<int TSubtype>
- struct VInt32
- {
- __m128i m_values[2];
-
- inline VInt32 operator+(const VInt32& other) const
- {
- VInt32 result;
- result.m_values[0] = _mm_add_epi32(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_add_epi32(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline VInt32 operator-(const VInt32& other) const
- {
- VInt32 result;
- result.m_values[0] = _mm_sub_epi32(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_sub_epi32(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline VInt32 operator<<(const int other) const
- {
- VInt32 result;
- result.m_values[0] = _mm_slli_epi32(m_values[0], other);
- result.m_values[1] = _mm_slli_epi32(m_values[1], other);
- return result;
- }
- };
-
- typedef VInt32<IntSubtype_Signed> SInt32;
- typedef VInt32<IntSubtype_UnsignedTruncated> UInt31;
- typedef VInt32<IntSubtype_UnsignedFull> UInt32;
- typedef VInt32<IntSubtype_Abstract> AInt32;
-
- template<class TTargetType>
- struct LosslessCast
- {
-#ifdef CVTT_PERMIT_ALIASING
- template<int TSrcSubtype>
- static const TTargetType& Cast(const VInt32<TSrcSubtype> &src)
- {
- return reinterpret_cast<VInt32<TSubtype>&>(src);
- }
-
- template<int TSrcSubtype>
- static const TTargetType& Cast(const VInt16<TSrcSubtype> &src)
- {
- return reinterpret_cast<VInt16<TSubtype>&>(src);
- }
-#else
- template<int TSrcSubtype>
- static TTargetType Cast(const VInt32<TSrcSubtype> &src)
- {
- TTargetType result;
- result.m_values[0] = src.m_values[0];
- result.m_values[1] = src.m_values[1];
- return result;
- }
-
- template<int TSrcSubtype>
- static TTargetType Cast(const VInt16<TSrcSubtype> &src)
- {
- TTargetType result;
- result.m_value = src.m_value;
- return result;
- }
-#endif
- };
-
- struct Int64
- {
- __m128i m_values[4];
- };
-
- struct Float
- {
- __m128 m_values[2];
-
- inline Float operator+(const Float &other) const
- {
- Float result;
- result.m_values[0] = _mm_add_ps(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_add_ps(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline Float operator+(float other) const
- {
- Float result;
- result.m_values[0] = _mm_add_ps(m_values[0], _mm_set1_ps(other));
- result.m_values[1] = _mm_add_ps(m_values[1], _mm_set1_ps(other));
- return result;
- }
-
- inline Float operator-(const Float& other) const
- {
- Float result;
- result.m_values[0] = _mm_sub_ps(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_sub_ps(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline Float operator-() const
- {
- Float result;
- result.m_values[0] = _mm_sub_ps(_mm_setzero_ps(), m_values[0]);
- result.m_values[1] = _mm_sub_ps(_mm_setzero_ps(), m_values[1]);
- return result;
- }
-
- inline Float operator*(const Float& other) const
- {
- Float result;
- result.m_values[0] = _mm_mul_ps(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_mul_ps(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline Float operator*(float other) const
- {
- Float result;
- result.m_values[0] = _mm_mul_ps(m_values[0], _mm_set1_ps(other));
- result.m_values[1] = _mm_mul_ps(m_values[1], _mm_set1_ps(other));
- return result;
- }
-
- inline Float operator/(const Float &other) const
- {
- Float result;
- result.m_values[0] = _mm_div_ps(m_values[0], other.m_values[0]);
- result.m_values[1] = _mm_div_ps(m_values[1], other.m_values[1]);
- return result;
- }
-
- inline Float operator/(float other) const
- {
- Float result;
- result.m_values[0] = _mm_div_ps(m_values[0], _mm_set1_ps(other));
- result.m_values[1] = _mm_div_ps(m_values[1], _mm_set1_ps(other));
- return result;
- }
- };
-
- struct Int16CompFlag
- {
- __m128i m_value;
-
- inline Int16CompFlag operator&(const Int16CompFlag &other) const
- {
- Int16CompFlag result;
- result.m_value = _mm_and_si128(m_value, other.m_value);
- return result;
- }
-
- inline Int16CompFlag operator|(const Int16CompFlag &other) const
- {
- Int16CompFlag result;
- result.m_value = _mm_or_si128(m_value, other.m_value);
- return result;
- }
- };
-
- struct FloatCompFlag
- {
- __m128 m_values[2];
- };
-
- template<int TSubtype>
- static VInt16<TSubtype> AbstractAdd(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
- {
- VInt16<TSubtype> result;
- result.m_value = _mm_add_epi16(a.m_value, b.m_value);
- return result;
- }
-
- template<int TSubtype>
- static VInt16<TSubtype> AbstractSubtract(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
- {
- VInt16<TSubtype> result;
- result.m_value = _mm_sub_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static Float Select(const FloatCompFlag &flag, const Float &a, const Float &b)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_or_ps(_mm_and_ps(flag.m_values[i], a.m_values[i]), _mm_andnot_ps(flag.m_values[i], b.m_values[i]));
- return result;
- }
-
- template<int TSubtype>
- static VInt16<TSubtype> Select(const Int16CompFlag &flag, const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
- {
- VInt16<TSubtype> result;
- result.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, a.m_value), _mm_andnot_si128(flag.m_value, b.m_value));
- return result;
- }
-
- template<int TSubtype>
- static VInt16<TSubtype> SelectOrZero(const Int16CompFlag &flag, const VInt16<TSubtype> &a)
- {
- VInt16<TSubtype> result;
- result.m_value = _mm_and_si128(flag.m_value, a.m_value);
- return result;
- }
-
- template<int TSubtype>
- static void ConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src)
- {
- dest.m_value = _mm_or_si128(_mm_andnot_si128(flag.m_value, dest.m_value), _mm_and_si128(flag.m_value, src.m_value));
- }
-
- static SInt16 ConditionalNegate(const Int16CompFlag &flag, const SInt16 &v)
- {
- SInt16 result;
- result.m_value = _mm_add_epi16(_mm_xor_si128(flag.m_value, v.m_value), _mm_srli_epi16(flag.m_value, 15));
- return result;
- }
-
- template<int TSubtype>
- static void NotConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src)
- {
- dest.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, dest.m_value), _mm_andnot_si128(flag.m_value, src.m_value));
- }
-
- static void ConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src)
- {
- for (int i = 0; i < 2; i++)
- dest.m_values[i] = _mm_or_ps(_mm_andnot_ps(flag.m_values[i], dest.m_values[i]), _mm_and_ps(flag.m_values[i], src.m_values[i]));
- }
-
- static void NotConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src)
- {
- for (int i = 0; i < 2; i++)
- dest.m_values[i] = _mm_or_ps(_mm_and_ps(flag.m_values[i], dest.m_values[i]), _mm_andnot_ps(flag.m_values[i], src.m_values[i]));
- }
-
- static void MakeSafeDenominator(Float& v)
- {
- ConditionalSet(v, Equal(v, MakeFloatZero()), MakeFloat(1.0f));
- }
-
- static SInt16 TruncateToPrecisionSigned(const SInt16 &v, int precision)
- {
- int lostBits = 16 - precision;
- if (lostBits == 0)
- return v;
-
- SInt16 result;
- result.m_value = _mm_srai_epi16(_mm_slli_epi16(v.m_value, lostBits), lostBits);
- return result;
- }
-
- static UInt16 TruncateToPrecisionUnsigned(const UInt16 &v, int precision)
- {
- int lostBits = 16 - precision;
- if (lostBits == 0)
- return v;
-
- UInt16 result;
- result.m_value = _mm_srli_epi16(_mm_slli_epi16(v.m_value, lostBits), lostBits);
- return result;
- }
-
- static UInt16 Min(const UInt16 &a, const UInt16 &b)
- {
- __m128i bitFlip = _mm_set1_epi16(-32768);
-
- UInt16 result;
- result.m_value = _mm_xor_si128(_mm_min_epi16(_mm_xor_si128(a.m_value, bitFlip), _mm_xor_si128(b.m_value, bitFlip)), bitFlip);
- return result;
- }
-
- static SInt16 Min(const SInt16 &a, const SInt16 &b)
- {
- SInt16 result;
- result.m_value = _mm_min_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static UInt15 Min(const UInt15 &a, const UInt15 &b)
- {
- UInt15 result;
- result.m_value = _mm_min_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static Float Min(const Float &a, const Float &b)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_min_ps(a.m_values[i], b.m_values[i]);
- return result;
- }
-
- static UInt16 Max(const UInt16 &a, const UInt16 &b)
- {
- __m128i bitFlip = _mm_set1_epi16(-32768);
-
- UInt16 result;
- result.m_value = _mm_xor_si128(_mm_max_epi16(_mm_xor_si128(a.m_value, bitFlip), _mm_xor_si128(b.m_value, bitFlip)), bitFlip);
- return result;
- }
-
- static SInt16 Max(const SInt16 &a, const SInt16 &b)
- {
- SInt16 result;
- result.m_value = _mm_max_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static UInt15 Max(const UInt15 &a, const UInt15 &b)
- {
- UInt15 result;
- result.m_value = _mm_max_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static Float Max(const Float &a, const Float &b)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_max_ps(a.m_values[i], b.m_values[i]);
- return result;
- }
-
- static Float Clamp(const Float &v, float min, float max)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_max_ps(_mm_min_ps(v.m_values[i], _mm_set1_ps(max)), _mm_set1_ps(min));
- return result;
- }
-
- static Float Reciprocal(const Float &v)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_rcp_ps(v.m_values[i]);
- return result;
- }
-
- static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, UInt15 &chOut)
- {
- int16_t values[8];
- for (int i = 0; i < 8; i++)
- values[i] = inputBlocks[i].m_pixels[pxOffset][channel];
-
- chOut.m_value = _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]);
- }
-
- static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, SInt16 &chOut)
- {
- int16_t values[8];
- for (int i = 0; i < 8; i++)
- values[i] = inputBlocks[i].m_pixels[pxOffset][channel];
-
- chOut.m_value = _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]);
- }
-
- static Float MakeFloat(float v)
- {
- Float f;
- f.m_values[0] = f.m_values[1] = _mm_set1_ps(v);
- return f;
- }
-
- static Float MakeFloatZero()
- {
- Float f;
- f.m_values[0] = f.m_values[1] = _mm_setzero_ps();
- return f;
- }
-
- static UInt16 MakeUInt16(uint16_t v)
- {
- UInt16 result;
- result.m_value = _mm_set1_epi16(static_cast<short>(v));
- return result;
- }
-
- static SInt16 MakeSInt16(int16_t v)
- {
- SInt16 result;
- result.m_value = _mm_set1_epi16(static_cast<short>(v));
- return result;
- }
-
- static AInt16 MakeAInt16(int16_t v)
- {
- AInt16 result;
- result.m_value = _mm_set1_epi16(static_cast<short>(v));
- return result;
- }
-
- static UInt15 MakeUInt15(uint16_t v)
- {
- UInt15 result;
- result.m_value = _mm_set1_epi16(static_cast<short>(v));
- return result;
- }
-
- static SInt32 MakeSInt32(int32_t v)
- {
- SInt32 result;
- result.m_values[0] = _mm_set1_epi32(v);
- result.m_values[1] = _mm_set1_epi32(v);
- return result;
- }
-
- static UInt31 MakeUInt31(uint32_t v)
- {
- UInt31 result;
- result.m_values[0] = _mm_set1_epi32(v);
- result.m_values[1] = _mm_set1_epi32(v);
- return result;
- }
-
- static uint16_t Extract(const UInt16 &v, int offset)
- {
- return reinterpret_cast<const uint16_t*>(&v.m_value)[offset];
- }
-
- static int16_t Extract(const SInt16 &v, int offset)
- {
- return reinterpret_cast<const int16_t*>(&v.m_value)[offset];
- }
-
- static uint16_t Extract(const UInt15 &v, int offset)
- {
- return reinterpret_cast<const uint16_t*>(&v.m_value)[offset];
- }
-
- static int16_t Extract(const AInt16 &v, int offset)
- {
- return reinterpret_cast<const int16_t*>(&v.m_value)[offset];
- }
-
- static void PutUInt16(UInt16 &dest, int offset, uint16_t v)
- {
- reinterpret_cast<uint16_t*>(&dest)[offset] = v;
- }
-
- static void PutUInt15(UInt15 &dest, int offset, uint16_t v)
- {
- reinterpret_cast<uint16_t*>(&dest)[offset] = v;
- }
-
- static void PutSInt16(SInt16 &dest, int offset, int16_t v)
- {
- reinterpret_cast<int16_t*>(&dest)[offset] = v;
- }
-
- static float ExtractFloat(const Float& v, int offset)
- {
- return reinterpret_cast<const float*>(&v)[offset];
- }
-
- static void PutFloat(Float &dest, int offset, float v)
- {
- reinterpret_cast<float*>(&dest)[offset] = v;
- }
-
- static Int16CompFlag Less(const SInt16 &a, const SInt16 &b)
- {
- Int16CompFlag result;
- result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static Int16CompFlag Less(const UInt15 &a, const UInt15 &b)
- {
- Int16CompFlag result;
- result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static Int16CompFlag LessOrEqual(const UInt15 &a, const UInt15 &b)
- {
- Int16CompFlag result;
- result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static FloatCompFlag Less(const Float &a, const Float &b)
- {
- FloatCompFlag result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_cmplt_ps(a.m_values[i], b.m_values[i]);
- return result;
- }
-
- static FloatCompFlag LessOrEqual(const Float &a, const Float &b)
- {
- FloatCompFlag result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_cmple_ps(a.m_values[i], b.m_values[i]);
- return result;
- }
-
- template<int TSubtype>
- static Int16CompFlag Equal(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
- {
- Int16CompFlag result;
- result.m_value = _mm_cmpeq_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static FloatCompFlag Equal(const Float &a, const Float &b)
- {
- FloatCompFlag result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_cmpeq_ps(a.m_values[i], b.m_values[i]);
- return result;
- }
-
- static Float ToFloat(const UInt16 &v)
- {
- Float result;
- result.m_values[0] = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v.m_value, _mm_setzero_si128()));
- result.m_values[1] = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v.m_value, _mm_setzero_si128()));
- return result;
- }
-
- static UInt31 ToUInt31(const UInt16 &v)
- {
- UInt31 result;
- result.m_values[0] = _mm_unpacklo_epi16(v.m_value, _mm_setzero_si128());
- result.m_values[1] = _mm_unpackhi_epi16(v.m_value, _mm_setzero_si128());
- return result;
- }
-
- static SInt32 ToInt32(const UInt16 &v)
- {
- SInt32 result;
- result.m_values[0] = _mm_unpacklo_epi16(v.m_value, _mm_setzero_si128());
- result.m_values[1] = _mm_unpackhi_epi16(v.m_value, _mm_setzero_si128());
- return result;
- }
-
- static SInt32 ToInt32(const SInt16 &v)
- {
- SInt32 result;
- result.m_values[0] = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), v.m_value), 16);
- result.m_values[1] = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), v.m_value), 16);
- return result;
- }
-
- static Float ToFloat(const SInt16 &v)
- {
- Float result;
- result.m_values[0] = _mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), v.m_value), 16));
- result.m_values[1] = _mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), v.m_value), 16));
- return result;
- }
-
- static Float ToFloat(const UInt15 &v)
- {
- Float result;
- result.m_values[0] = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v.m_value, _mm_setzero_si128()));
- result.m_values[1] = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v.m_value, _mm_setzero_si128()));
- return result;
- }
-
- static Float ToFloat(const UInt31 &v)
- {
- Float result;
- result.m_values[0] = _mm_cvtepi32_ps(v.m_values[0]);
- result.m_values[1] = _mm_cvtepi32_ps(v.m_values[1]);
- return result;
- }
-
- static Int16CompFlag FloatFlagToInt16(const FloatCompFlag &v)
- {
- __m128i lo = _mm_castps_si128(v.m_values[0]);
- __m128i hi = _mm_castps_si128(v.m_values[1]);
-
- Int16CompFlag result;
- result.m_value = _mm_packs_epi32(lo, hi);
- return result;
- }
-
- static FloatCompFlag Int16FlagToFloat(const Int16CompFlag &v)
- {
- __m128i lo = _mm_unpacklo_epi16(v.m_value, v.m_value);
- __m128i hi = _mm_unpackhi_epi16(v.m_value, v.m_value);
-
- FloatCompFlag result;
- result.m_values[0] = _mm_castsi128_ps(lo);
- result.m_values[1] = _mm_castsi128_ps(hi);
- return result;
- }
-
- static Int16CompFlag MakeBoolInt16(bool b)
- {
- Int16CompFlag result;
- if (b)
- result.m_value = _mm_set1_epi16(-1);
- else
- result.m_value = _mm_setzero_si128();
- return result;
- }
-
- static FloatCompFlag MakeBoolFloat(bool b)
- {
- FloatCompFlag result;
- if (b)
- result.m_values[0] = result.m_values[1] = _mm_castsi128_ps(_mm_set1_epi32(-1));
- else
- result.m_values[0] = result.m_values[1] = _mm_setzero_ps();
- return result;
- }
-
- static Int16CompFlag AndNot(const Int16CompFlag &a, const Int16CompFlag &b)
- {
- Int16CompFlag result;
- result.m_value = _mm_andnot_si128(b.m_value, a.m_value);
- return result;
- }
-
- static UInt16 RoundAndConvertToU16(const Float &v, const void* /*roundingMode*/)
- {
- __m128i lo = _mm_cvtps_epi32(_mm_add_ps(v.m_values[0], _mm_set1_ps(-32768)));
- __m128i hi = _mm_cvtps_epi32(_mm_add_ps(v.m_values[1], _mm_set1_ps(-32768)));
-
- __m128i packed = _mm_packs_epi32(lo, hi);
-
- UInt16 result;
- result.m_value = _mm_xor_si128(packed, _mm_set1_epi16(-32768));
- return result;
- }
-
- static UInt15 RoundAndConvertToU15(const Float &v, const void* /*roundingMode*/)
- {
- __m128i lo = _mm_cvtps_epi32(v.m_values[0]);
- __m128i hi = _mm_cvtps_epi32(v.m_values[1]);
-
- __m128i packed = _mm_packs_epi32(lo, hi);
-
- UInt15 result;
- result.m_value = _mm_packs_epi32(lo, hi);
- return result;
- }
-
- static SInt16 RoundAndConvertToS16(const Float &v, const void* /*roundingMode*/)
- {
- __m128i lo = _mm_cvtps_epi32(v.m_values[0]);
- __m128i hi = _mm_cvtps_epi32(v.m_values[1]);
-
- __m128i packed = _mm_packs_epi32(lo, hi);
-
- SInt16 result;
- result.m_value = _mm_packs_epi32(lo, hi);
- return result;
- }
-
- static Float Sqrt(const Float &f)
- {
- Float result;
- for (int i = 0; i < 2; i++)
- result.m_values[i] = _mm_sqrt_ps(f.m_values[i]);
- return result;
- }
-
- static UInt16 Abs(const SInt16 &a)
- {
- __m128i signBitsXor = _mm_srai_epi16(a.m_value, 15);
- __m128i signBitsAdd = _mm_srli_epi16(a.m_value, 15);
-
- UInt16 result;
- result.m_value = _mm_add_epi16(_mm_xor_si128(a.m_value, signBitsXor), signBitsAdd);
- return result;
- }
-
- static Float Abs(const Float& a)
- {
- __m128 invMask = _mm_set1_ps(-0.0f);
-
- Float result;
- result.m_values[0] = _mm_andnot_ps(invMask, a.m_values[0]);
- result.m_values[1] = _mm_andnot_ps(invMask, a.m_values[1]);
- return result;
- }
-
- static UInt16 SqDiffUInt8(const UInt15 &a, const UInt15 &b)
- {
- __m128i diff = _mm_sub_epi16(a.m_value, b.m_value);
-
- UInt16 result;
- result.m_value = _mm_mullo_epi16(diff, diff);
- return result;
- }
-
- static Float SqDiffSInt16(const SInt16 &a, const SInt16 &b)
- {
- __m128i diffU = _mm_sub_epi16(_mm_max_epi16(a.m_value, b.m_value), _mm_min_epi16(a.m_value, b.m_value));
-
- __m128i mulHi = _mm_mulhi_epu16(diffU, diffU);
- __m128i mulLo = _mm_mullo_epi16(diffU, diffU);
- __m128i sqDiffHi = _mm_unpackhi_epi16(mulLo, mulHi);
- __m128i sqDiffLo = _mm_unpacklo_epi16(mulLo, mulHi);
-
- Float result;
- result.m_values[0] = _mm_cvtepi32_ps(sqDiffLo);
- result.m_values[1] = _mm_cvtepi32_ps(sqDiffHi);
-
- return result;
- }
-
- static Float TwosCLHalfToFloat(const SInt16 &v)
- {
- __m128i absV = _mm_add_epi16(_mm_xor_si128(v.m_value, _mm_srai_epi16(v.m_value, 15)), _mm_srli_epi16(v.m_value, 15));
-
- __m128i signBits = _mm_and_si128(v.m_value, _mm_set1_epi16(-32768));
- __m128i mantissa = _mm_and_si128(v.m_value, _mm_set1_epi16(0x03ff));
- __m128i exponent = _mm_and_si128(v.m_value, _mm_set1_epi16(0x7c00));
-
- __m128i isDenormal = _mm_cmpeq_epi16(exponent, _mm_setzero_si128());
-
- // Convert exponent to high-bits
- exponent = _mm_add_epi16(_mm_srli_epi16(exponent, 3), _mm_set1_epi16(14336));
-
- __m128i denormalCorrectionHigh = _mm_and_si128(isDenormal, _mm_or_si128(signBits, _mm_set1_epi16(14336)));
-
- __m128i highBits = _mm_or_si128(signBits, _mm_or_si128(exponent, _mm_srli_epi16(mantissa, 3)));
- __m128i lowBits = _mm_slli_epi16(mantissa, 13);
-
- __m128i flow = _mm_unpacklo_epi16(lowBits, highBits);
- __m128i fhigh = _mm_unpackhi_epi16(lowBits, highBits);
-
- __m128i correctionLow = _mm_unpacklo_epi16(_mm_setzero_si128(), denormalCorrectionHigh);
- __m128i correctionHigh = _mm_unpackhi_epi16(_mm_setzero_si128(), denormalCorrectionHigh);
-
- Float result;
- result.m_values[0] = _mm_sub_ps(_mm_castsi128_ps(flow), _mm_castsi128_ps(correctionLow));
- result.m_values[1] = _mm_sub_ps(_mm_castsi128_ps(fhigh), _mm_castsi128_ps(correctionHigh));
-
- return result;
- }
-
- static Float SqDiff2CLFloat(const SInt16 &a, const Float &b)
- {
- Float fa = TwosCLHalfToFloat(a);
-
- Float diff = fa - b;
- return diff * diff;
- }
-
- static Float SqDiff2CL(const SInt16 &a, const SInt16 &b)
- {
- Float fa = TwosCLHalfToFloat(a);
- Float fb = TwosCLHalfToFloat(b);
-
- Float diff = fa - fb;
- return diff * diff;
- }
-
- static Float SqDiff2CLFloat(const SInt16 &a, float aWeight, const Float &b)
- {
- Float fa = TwosCLHalfToFloat(a) * aWeight;
-
- Float diff = fa - b;
- return diff * diff;
- }
-
- static UInt16 RightShift(const UInt16 &v, int bits)
- {
- UInt16 result;
- result.m_value = _mm_srli_epi16(v.m_value, bits);
- return result;
- }
-
- static UInt31 RightShift(const UInt31 &v, int bits)
- {
- UInt31 result;
- result.m_values[0] = _mm_srli_epi32(v.m_values[0], bits);
- result.m_values[1] = _mm_srli_epi32(v.m_values[1], bits);
- return result;
- }
-
- static SInt16 RightShift(const SInt16 &v, int bits)
- {
- SInt16 result;
- result.m_value = _mm_srai_epi16(v.m_value, bits);
- return result;
- }
-
- static UInt15 RightShift(const UInt15 &v, int bits)
- {
- UInt15 result;
- result.m_value = _mm_srli_epi16(v.m_value, bits);
- return result;
- }
-
- static SInt32 RightShift(const SInt32 &v, int bits)
- {
- SInt32 result;
- result.m_values[0] = _mm_srai_epi32(v.m_values[0], bits);
- result.m_values[1] = _mm_srai_epi32(v.m_values[1], bits);
- return result;
- }
-
- static SInt16 ToSInt16(const SInt32 &v)
- {
- SInt16 result;
- result.m_value = _mm_packs_epi32(v.m_values[0], v.m_values[1]);
- return result;
- }
-
- static UInt16 ToUInt16(const UInt32 &v)
- {
- __m128i low = _mm_srai_epi32(_mm_slli_epi32(v.m_values[0], 16), 16);
- __m128i high = _mm_srai_epi32(_mm_slli_epi32(v.m_values[1], 16), 16);
-
- UInt16 result;
- result.m_value = _mm_packs_epi32(low, high);
- return result;
- }
-
- static UInt16 ToUInt16(const UInt31 &v)
- {
- __m128i low = _mm_srai_epi32(_mm_slli_epi32(v.m_values[0], 16), 16);
- __m128i high = _mm_srai_epi32(_mm_slli_epi32(v.m_values[1], 16), 16);
-
- UInt16 result;
- result.m_value = _mm_packs_epi32(low, high);
- return result;
- }
-
- static UInt15 ToUInt15(const UInt31 &v)
- {
- UInt15 result;
- result.m_value = _mm_packs_epi32(v.m_values[0], v.m_values[1]);
- return result;
- }
-
- static SInt32 XMultiply(const SInt16 &a, const SInt16 &b)
- {
- __m128i high = _mm_mulhi_epi16(a.m_value, b.m_value);
- __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
-
- SInt32 result;
- result.m_values[0] = _mm_unpacklo_epi16(low, high);
- result.m_values[1] = _mm_unpackhi_epi16(low, high);
- return result;
- }
-
- static SInt32 XMultiply(const SInt16 &a, const UInt15 &b)
- {
- __m128i high = _mm_mulhi_epi16(a.m_value, b.m_value);
- __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
-
- SInt32 result;
- result.m_values[0] = _mm_unpacklo_epi16(low, high);
- result.m_values[1] = _mm_unpackhi_epi16(low, high);
- return result;
- }
-
- static SInt32 XMultiply(const UInt15 &a, const SInt16 &b)
- {
- return XMultiply(b, a);
- }
-
- static UInt32 XMultiply(const UInt16 &a, const UInt16 &b)
- {
- __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
- __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
-
- UInt32 result;
- result.m_values[0] = _mm_unpacklo_epi16(low, high);
- result.m_values[1] = _mm_unpackhi_epi16(low, high);
- return result;
- }
-
- static UInt16 CompactMultiply(const UInt16 &a, const UInt15 &b)
- {
- UInt16 result;
- result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static UInt16 CompactMultiply(const UInt15 &a, const UInt15 &b)
- {
- UInt16 result;
- result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
- return result;
- }
-
- static UInt31 XMultiply(const UInt15 &a, const UInt15 &b)
- {
- __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
- __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
-
- UInt31 result;
- result.m_values[0] = _mm_unpacklo_epi16(low, high);
- result.m_values[1] = _mm_unpackhi_epi16(low, high);
- return result;
- }
-
- static UInt31 XMultiply(const UInt16 &a, const UInt15 &b)
- {
- __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
- __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
-
- UInt31 result;
- result.m_values[0] = _mm_unpacklo_epi16(low, high);
- result.m_values[1] = _mm_unpackhi_epi16(low, high);
- return result;
- }
-
- static UInt31 XMultiply(const UInt15 &a, const UInt16 &b)
- {
- return XMultiply(b, a);
- }
-
- static bool AnySet(const Int16CompFlag &v)
- {
- return _mm_movemask_epi8(v.m_value) != 0;
- }
-
- static bool AllSet(const Int16CompFlag &v)
- {
- return _mm_movemask_epi8(v.m_value) == 0xffff;
- }
-
- static bool AnySet(const FloatCompFlag &v)
- {
- return _mm_movemask_ps(v.m_values[0]) != 0 || _mm_movemask_ps(v.m_values[1]) != 0;
- }
-
- static bool AllSet(const FloatCompFlag &v)
- {
- return _mm_movemask_ps(v.m_values[0]) == 0xf && _mm_movemask_ps(v.m_values[1]) == 0xf;
- }
- };
-
-#else
- // Scalar version
- struct ParallelMath
- {
- struct RoundTowardZeroForScope
- {
- };
-
- struct RoundTowardNearestForScope
- {
- };
-
- struct RoundUpForScope
- {
- };
-
- struct RoundDownForScope
- {
- };
-
- static const int ParallelSize = 1;
-
- enum Int16Subtype
- {
- IntSubtype_Signed,
- IntSubtype_UnsignedFull,
- IntSubtype_UnsignedTruncated,
- IntSubtype_Abstract,
- };
-
- typedef int32_t SInt16;
- typedef int32_t UInt15;
- typedef int32_t UInt16;
- typedef int32_t AInt16;
-
- typedef int32_t SInt32;
- typedef int32_t UInt31;
- typedef int32_t UInt32;
- typedef int32_t AInt32;
-
- typedef int32_t ScalarUInt16;
- typedef int32_t ScalarSInt16;
-
- typedef float Float;
-
- template<class TTargetType>
- struct LosslessCast
- {
- static const int32_t& Cast(const int32_t &src)
- {
- return src;
- }
- };
-
- typedef bool Int16CompFlag;
- typedef bool FloatCompFlag;
-
- static int32_t AbstractAdd(const int32_t &a, const int32_t &b)
- {
- return a + b;
- }
-
- static int32_t AbstractSubtract(const int32_t &a, const int32_t &b)
- {
- return a - b;
- }
-
- static float Select(bool flag, float a, float b)
- {
- return flag ? a : b;
- }
-
- static int32_t Select(bool flag, int32_t a, int32_t b)
- {
- return flag ? a : b;
- }
-
- static int32_t SelectOrZero(bool flag, int32_t a)
- {
- return flag ? a : 0;
- }
-
- static void ConditionalSet(int32_t& dest, bool flag, int32_t src)
- {
- if (flag)
- dest = src;
- }
-
- static int32_t ConditionalNegate(bool flag, int32_t v)
- {
- return (flag) ? -v : v;
- }
-
- static void NotConditionalSet(int32_t& dest, bool flag, int32_t src)
- {
- if (!flag)
- dest = src;
- }
-
- static void ConditionalSet(float& dest, bool flag, float src)
- {
- if (flag)
- dest = src;
- }
-
- static void NotConditionalSet(float& dest, bool flag, float src)
- {
- if (!flag)
- dest = src;
- }
-
- static void MakeSafeDenominator(float& v)
- {
- if (v == 0.0f)
- v = 1.0f;
- }
-
- static int32_t SignedRightShift(int32_t v, int bits)
- {
- return v >> bits;
- }
-
- static int32_t TruncateToPrecisionSigned(int32_t v, int precision)
- {
- v = (v << (32 - precision)) & 0xffffffff;
- return SignedRightShift(v, 32 - precision);
- }
-
- static int32_t TruncateToPrecisionUnsigned(int32_t v, int precision)
- {
- return v & ((1 << precision) - 1);
- }
-
- static int32_t Min(int32_t a, int32_t b)
- {
- if (a < b)
- return a;
- return b;
- }
-
- static float Min(float a, float b)
- {
- if (a < b)
- return a;
- return b;
- }
-
- static int32_t Max(int32_t a, int32_t b)
- {
- if (a > b)
- return a;
- return b;
- }
-
- static float Max(float a, float b)
- {
- if (a > b)
- return a;
- return b;
- }
-
- static float Abs(float a)
- {
- return fabsf(a);
- }
-
- static int32_t Abs(int32_t a)
- {
- if (a < 0)
- return -a;
- return a;
- }
-
- static float Clamp(float v, float min, float max)
- {
- if (v < min)
- return min;
- if (v > max)
- return max;
- return v;
- }
-
- static float Reciprocal(float v)
- {
- return 1.0f / v;
- }
-
- static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, int32_t& chOut)
- {
- chOut = inputBlocks[0].m_pixels[pxOffset][channel];
- }
-
- static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, int32_t& chOut)
- {
- chOut = inputBlocks[0].m_pixels[pxOffset][channel];
- }
-
- static float MakeFloat(float v)
- {
- return v;
- }
-
- static float MakeFloatZero()
- {
- return 0.0f;
- }
-
- static int32_t MakeUInt16(uint16_t v)
- {
- return v;
- }
-
- static int32_t MakeSInt16(int16_t v)
- {
- return v;
- }
-
- static int32_t MakeAInt16(int16_t v)
- {
- return v;
- }
-
- static int32_t MakeUInt15(uint16_t v)
- {
- return v;
- }
-
- static int32_t MakeSInt32(int32_t v)
- {
- return v;
- }
-
- static int32_t MakeUInt31(int32_t v)
- {
- return v;
- }
-
- static int32_t Extract(int32_t v, int offset)
- {
- UNREFERENCED_PARAMETER(offset);
- return v;
- }
-
- static void PutUInt16(int32_t &dest, int offset, ParallelMath::ScalarUInt16 v)
- {
- UNREFERENCED_PARAMETER(offset);
- dest = v;
- }
-
- static void PutUInt15(int32_t &dest, int offset, ParallelMath::ScalarUInt16 v)
- {
- UNREFERENCED_PARAMETER(offset);
- dest = v;
- }
-
- static void PutSInt16(int32_t &dest, int offset, ParallelMath::ScalarSInt16 v)
- {
- UNREFERENCED_PARAMETER(offset);
- dest = v;
- }
-
- static float ExtractFloat(float v, int offset)
- {
- UNREFERENCED_PARAMETER(offset);
- return v;
- }
-
- static void PutFloat(float &dest, int offset, float v)
- {
- UNREFERENCED_PARAMETER(offset);
- dest = v;
- }
-
- static bool Less(int32_t a, int32_t b)
- {
- return a < b;
- }
-
- static bool Less(float a, float b)
- {
- return a < b;
- }
-
- static bool LessOrEqual(int32_t a, int32_t b)
- {
- return a < b;
- }
-
- static bool LessOrEqual(float a, float b)
- {
- return a < b;
- }
-
- static bool Equal(int32_t a, int32_t b)
- {
- return a == b;
- }
-
- static bool Equal(float a, float b)
- {
- return a == b;
- }
-
- static float ToFloat(int32_t v)
- {
- return static_cast<float>(v);
- }
-
- static int32_t ToUInt31(int32_t v)
- {
- return v;
- }
-
- static int32_t ToInt32(int32_t v)
- {
- return v;
- }
-
- static bool FloatFlagToInt16(bool v)
- {
- return v;
- }
-
- static bool Int16FlagToFloat(bool v)
- {
- return v;
- }
-
- static bool MakeBoolInt16(bool b)
- {
- return b;
- }
-
- static bool MakeBoolFloat(bool b)
- {
- return b;
- }
-
- static bool AndNot(bool a, bool b)
- {
- return a && !b;
- }
-
- static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundTowardZeroForScope *rtz)
- {
- UNREFERENCED_PARAMETER(rtz);
- return static_cast<int>(v);
- }
-
- static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundUpForScope *ru)
- {
- UNREFERENCED_PARAMETER(ru);
- return static_cast<int>(ceilf(v));
- }
-
- static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundDownForScope *rd)
- {
- UNREFERENCED_PARAMETER(rd);
- return static_cast<int>(floorf(v));
- }
-
- static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- UNREFERENCED_PARAMETER(rtn);
- return static_cast<int>(floorf(v + 0.5f));
- }
-
- template<class TRoundMode>
- static int32_t RoundAndConvertToU16(float v, const TRoundMode *roundingMode)
- {
- return RoundAndConvertToInt(v, roundingMode);
- }
-
- template<class TRoundMode>
- static int32_t RoundAndConvertToU15(float v, const TRoundMode *roundingMode)
- {
- return RoundAndConvertToInt(v, roundingMode);
- }
-
- template<class TRoundMode>
- static int32_t RoundAndConvertToS16(float v, const TRoundMode *roundingMode)
- {
- return RoundAndConvertToInt(v, roundingMode);
- }
-
- static float Sqrt(float f)
- {
- return sqrtf(f);
- }
-
- static int32_t SqDiffUInt8(int32_t a, int32_t b)
- {
- int32_t delta = a - b;
- return delta * delta;
- }
-
- static int32_t SqDiffInt16(int32_t a, int32_t b)
- {
- int32_t delta = a - b;
- return delta * delta;
- }
-
- static int32_t SqDiffSInt16(int32_t a, int32_t b)
- {
- int32_t delta = a - b;
- return delta * delta;
- }
-
- static float TwosCLHalfToFloat(int32_t v)
- {
- int32_t absV = (v < 0) ? -v : v;
-
- int32_t signBits = (absV & -32768);
- int32_t mantissa = (absV & 0x03ff);
- int32_t exponent = (absV & 0x7c00);
-
- bool isDenormal = (exponent == 0);
-
- // Convert exponent to high-bits
- exponent = (exponent >> 3) + 14336;
-
- int32_t denormalCorrection = (isDenormal ? (signBits | 14336) : 0) << 16;
-
- int32_t fBits = ((exponent | signBits) << 16) | (mantissa << 13);
-
- float f, correction;
- memcpy(&f, &fBits, 4);
- memcpy(&correction, &denormalCorrection, 4);
-
- return f - correction;
- }
-
- static Float SqDiff2CLFloat(const SInt16 &a, const Float &b)
- {
- Float fa = TwosCLHalfToFloat(a);
-
- Float diff = fa - b;
- return diff * diff;
- }
-
- static Float SqDiff2CL(const SInt16 &a, const SInt16 &b)
- {
- Float fa = TwosCLHalfToFloat(a);
- Float fb = TwosCLHalfToFloat(b);
-
- Float diff = fa - fb;
- return diff * diff;
- }
-
- static Float SqDiff2CLFloat(const SInt16 &a, float aWeight, const Float &b)
- {
- Float fa = TwosCLHalfToFloat(a) * aWeight;
-
- Float diff = fa - b;
- return diff * diff;
- }
-
- static int32_t RightShift(int32_t v, int bits)
- {
- return SignedRightShift(v, bits);
- }
-
- static int32_t ToSInt16(int32_t v)
- {
- return v;
- }
-
- static int32_t ToUInt16(int32_t v)
- {
- return v;
- }
-
- static int32_t ToUInt15(int32_t v)
- {
- return v;
- }
-
- static int32_t XMultiply(int32_t a, int32_t b)
- {
- return a * b;
- }
-
- static int32_t CompactMultiply(int32_t a, int32_t b)
- {
- return a * b;
- }
-
- static bool AnySet(bool v)
- {
- return v;
- }
-
- static bool AllSet(bool v)
- {
- return v;
- }
- };
-
-#endif
-
- namespace Internal
- {
- namespace BC7Data
- {
- enum AlphaMode
- {
- AlphaMode_Combined,
- AlphaMode_Separate,
- AlphaMode_None,
- };
-
- enum PBitMode
- {
- PBitMode_PerEndpoint,
- PBitMode_PerSubset,
- PBitMode_None
- };
-
- struct BC7ModeInfo
- {
- PBitMode m_pBitMode;
- AlphaMode m_alphaMode;
- int m_rgbBits;
- int m_alphaBits;
- int m_partitionBits;
- int m_numSubsets;
- int m_indexBits;
- int m_alphaIndexBits;
- bool m_hasIndexSelector;
- };
-
- BC7ModeInfo g_modes[] =
- {
- { PBitMode_PerEndpoint, AlphaMode_None, 4, 0, 4, 3, 3, 0, false }, // 0
- { PBitMode_PerSubset, AlphaMode_None, 6, 0, 6, 2, 3, 0, false }, // 1
- { PBitMode_None, AlphaMode_None, 5, 0, 6, 3, 2, 0, false }, // 2
- { PBitMode_PerEndpoint, AlphaMode_None, 7, 0, 6, 2, 2, 0, false }, // 3 (Mode reference has an error, P-bit is really per-endpoint)
-
- { PBitMode_None, AlphaMode_Separate, 5, 6, 0, 1, 2, 3, true }, // 4
- { PBitMode_None, AlphaMode_Separate, 7, 8, 0, 1, 2, 2, false }, // 5
- { PBitMode_PerEndpoint, AlphaMode_Combined, 7, 7, 0, 1, 4, 0, false }, // 6
- { PBitMode_PerEndpoint, AlphaMode_Combined, 5, 5, 6, 2, 2, 0, false } // 7
- };
-
- const int g_weight2[] = { 0, 21, 43, 64 };
- const int g_weight3[] = { 0, 9, 18, 27, 37, 46, 55, 64 };
- const int g_weight4[] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
-
- const int *g_weightTables[] =
- {
- NULL,
- NULL,
- g_weight2,
- g_weight3,
- g_weight4
- };
-
- struct BC6HModeInfo
- {
- uint16_t m_modeID;
- bool m_partitioned;
- bool m_transformed;
- int m_aPrec;
- int m_bPrec[3];
- };
-
- // [partitioned][precision]
- bool g_hdrModesExistForPrecision[2][17] =
- {
- //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
- { false, false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, true },
- { false, false, false, false, false, false, true, true, true, true, true, true, false, false, false, false, false },
- };
-
- BC6HModeInfo g_hdrModes[] =
- {
- { 0x00, true, true, 10,{ 5, 5, 5 } },
- { 0x01, true, true, 7,{ 6, 6, 6 } },
- { 0x02, true, true, 11,{ 5, 4, 4 } },
- { 0x06, true, true, 11,{ 4, 5, 4 } },
- { 0x0a, true, true, 11,{ 4, 4, 5 } },
- { 0x0e, true, true, 9,{ 5, 5, 5 } },
- { 0x12, true, true, 8,{ 6, 5, 5 } },
- { 0x16, true, true, 8,{ 5, 6, 5 } },
- { 0x1a, true, true, 8,{ 5, 5, 6 } },
- { 0x1e, true, false, 6,{ 6, 6, 6 } },
- { 0x03, false, false, 10,{ 10, 10, 10 } },
- { 0x07, false, true, 11,{ 9, 9, 9 } },
- { 0x0b, false, true, 12,{ 8, 8, 8 } },
- { 0x0f, false, true, 16,{ 4, 4, 4 } },
- };
-
- const int g_maxHDRPrecision = 16;
-
- static const size_t g_numHDRModes = sizeof(g_hdrModes) / sizeof(g_hdrModes[0]);
-
- static uint16_t g_partitionMap[64] =
- {
- 0xCCCC, 0x8888, 0xEEEE, 0xECC8,
- 0xC880, 0xFEEC, 0xFEC8, 0xEC80,
- 0xC800, 0xFFEC, 0xFE80, 0xE800,
- 0xFFE8, 0xFF00, 0xFFF0, 0xF000,
- 0xF710, 0x008E, 0x7100, 0x08CE,
- 0x008C, 0x7310, 0x3100, 0x8CCE,
- 0x088C, 0x3110, 0x6666, 0x366C,
- 0x17E8, 0x0FF0, 0x718E, 0x399C,
- 0xaaaa, 0xf0f0, 0x5a5a, 0x33cc,
- 0x3c3c, 0x55aa, 0x9696, 0xa55a,
- 0x73ce, 0x13c8, 0x324c, 0x3bdc,
- 0x6996, 0xc33c, 0x9966, 0x660,
- 0x272, 0x4e4, 0x4e40, 0x2720,
- 0xc936, 0x936c, 0x39c6, 0x639c,
- 0x9336, 0x9cc6, 0x817e, 0xe718,
- 0xccf0, 0xfcc, 0x7744, 0xee22,
- };
-
- static uint32_t g_partitionMap2[64] =
- {
- 0xaa685050, 0x6a5a5040, 0x5a5a4200, 0x5450a0a8,
- 0xa5a50000, 0xa0a05050, 0x5555a0a0, 0x5a5a5050,
- 0xaa550000, 0xaa555500, 0xaaaa5500, 0x90909090,
- 0x94949494, 0xa4a4a4a4, 0xa9a59450, 0x2a0a4250,
- 0xa5945040, 0x0a425054, 0xa5a5a500, 0x55a0a0a0,
- 0xa8a85454, 0x6a6a4040, 0xa4a45000, 0x1a1a0500,
- 0x0050a4a4, 0xaaa59090, 0x14696914, 0x69691400,
- 0xa08585a0, 0xaa821414, 0x50a4a450, 0x6a5a0200,
- 0xa9a58000, 0x5090a0a8, 0xa8a09050, 0x24242424,
- 0x00aa5500, 0x24924924, 0x24499224, 0x50a50a50,
- 0x500aa550, 0xaaaa4444, 0x66660000, 0xa5a0a5a0,
- 0x50a050a0, 0x69286928, 0x44aaaa44, 0x66666600,
- 0xaa444444, 0x54a854a8, 0x95809580, 0x96969600,
- 0xa85454a8, 0x80959580, 0xaa141414, 0x96960000,
- 0xaaaa1414, 0xa05050a0, 0xa0a5a5a0, 0x96000000,
- 0x40804080, 0xa9a8a9a8, 0xaaaaaa44, 0x2a4a5254,
- };
-
- static int g_fixupIndexes2[64] =
- {
- 15,15,15,15,
- 15,15,15,15,
- 15,15,15,15,
- 15,15,15,15,
- 15, 2, 8, 2,
- 2, 8, 8,15,
- 2, 8, 2, 2,
- 8, 8, 2, 2,
-
- 15,15, 6, 8,
- 2, 8,15,15,
- 2, 8, 2, 2,
- 2,15,15, 6,
- 6, 2, 6, 8,
- 15,15, 2, 2,
- 15,15,15,15,
- 15, 2, 2,15,
- };
-
- static int g_fixupIndexes3[64][2] =
- {
- { 3,15 },{ 3, 8 },{ 15, 8 },{ 15, 3 },
- { 8,15 },{ 3,15 },{ 15, 3 },{ 15, 8 },
- { 8,15 },{ 8,15 },{ 6,15 },{ 6,15 },
- { 6,15 },{ 5,15 },{ 3,15 },{ 3, 8 },
- { 3,15 },{ 3, 8 },{ 8,15 },{ 15, 3 },
- { 3,15 },{ 3, 8 },{ 6,15 },{ 10, 8 },
- { 5, 3 },{ 8,15 },{ 8, 6 },{ 6,10 },
- { 8,15 },{ 5,15 },{ 15,10 },{ 15, 8 },
-
- { 8,15 },{ 15, 3 },{ 3,15 },{ 5,10 },
- { 6,10 },{ 10, 8 },{ 8, 9 },{ 15,10 },
- { 15, 6 },{ 3,15 },{ 15, 8 },{ 5,15 },
- { 15, 3 },{ 15, 6 },{ 15, 6 },{ 15, 8 },
- { 3,15 },{ 15, 3 },{ 5,15 },{ 5,15 },
- { 5,15 },{ 8,15 },{ 5,15 },{ 10,15 },
- { 5,15 },{ 10,15 },{ 8,15 },{ 13,15 },
- { 15, 3 },{ 12,15 },{ 3,15 },{ 3, 8 },
- };
-
- static const unsigned char g_fragments[] =
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0, 16
- 0, 1, 2, 3, // 16, 4
- 0, 1, 4, // 20, 3
- 0, 1, 2, 4, // 23, 4
- 2, 3, 7, // 27, 3
- 1, 2, 3, 7, // 30, 4
- 0, 1, 2, 3, 4, 5, 6, 7, // 34, 8
- 0, 1, 4, 8, // 42, 4
- 0, 1, 2, 4, 5, 8, // 46, 6
- 0, 1, 2, 3, 4, 5, 6, 8, // 52, 8
- 1, 4, 5, 6, 9, // 60, 5
- 2, 5, 6, 7, 10, // 65, 5
- 5, 6, 9, 10, // 70, 4
- 2, 3, 7, 11, // 74, 4
- 1, 2, 3, 6, 7, 11, // 78, 6
- 0, 1, 2, 3, 5, 6, 7, 11, // 84, 8
- 0, 1, 2, 3, 8, 9, 10, 11, // 92, 8
- 2, 3, 6, 7, 8, 9, 10, 11, // 100, 8
- 4, 5, 6, 7, 8, 9, 10, 11, // 108, 8
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 116, 12
- 0, 4, 8, 12, // 128, 4
- 0, 2, 3, 4, 6, 7, 8, 12, // 132, 8
- 0, 1, 2, 4, 5, 8, 9, 12, // 140, 8
- 0, 1, 2, 3, 4, 5, 6, 8, 9, 12, // 148, 10
- 3, 6, 7, 8, 9, 12, // 158, 6
- 3, 5, 6, 7, 8, 9, 10, 12, // 164, 8
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, // 172, 12
- 0, 1, 2, 5, 6, 7, 11, 12, // 184, 8
- 5, 8, 9, 10, 13, // 192, 5
- 8, 12, 13, // 197, 3
- 4, 8, 12, 13, // 200, 4
- 2, 3, 6, 9, 12, 13, // 204, 6
- 0, 1, 2, 3, 8, 9, 12, 13, // 210, 8
- 0, 1, 4, 5, 8, 9, 12, 13, // 218, 8
- 2, 3, 6, 7, 8, 9, 12, 13, // 226, 8
- 2, 3, 5, 6, 9, 10, 12, 13, // 234, 8
- 0, 3, 6, 7, 9, 10, 12, 13, // 242, 8
- 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, // 250, 12
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, // 262, 13
- 2, 3, 4, 7, 8, 11, 12, 13, // 275, 8
- 1, 2, 6, 7, 8, 11, 12, 13, // 283, 8
- 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, // 291, 10
- 2, 3, 4, 5, 10, 11, 12, 13, // 301, 8
- 0, 1, 6, 7, 10, 11, 12, 13, // 309, 8
- 6, 9, 10, 11, 14, // 317, 5
- 0, 2, 4, 6, 8, 10, 12, 14, // 322, 8
- 1, 3, 5, 7, 8, 10, 12, 14, // 330, 8
- 1, 3, 4, 6, 9, 11, 12, 14, // 338, 8
- 0, 2, 5, 7, 9, 11, 12, 14, // 346, 8
- 0, 3, 4, 5, 8, 9, 13, 14, // 354, 8
- 2, 3, 4, 7, 8, 9, 13, 14, // 362, 8
- 1, 2, 5, 6, 9, 10, 13, 14, // 370, 8
- 0, 3, 4, 7, 9, 10, 13, 14, // 378, 8
- 0, 3, 5, 6, 8, 11, 13, 14, // 386, 8
- 1, 2, 4, 7, 8, 11, 13, 14, // 394, 8
- 0, 1, 4, 7, 10, 11, 13, 14, // 402, 8
- 0, 3, 6, 7, 10, 11, 13, 14, // 410, 8
- 8, 12, 13, 14, // 418, 4
- 1, 2, 3, 7, 8, 12, 13, 14, // 422, 8
- 4, 8, 9, 12, 13, 14, // 430, 6
- 0, 4, 5, 8, 9, 12, 13, 14, // 436, 8
- 1, 2, 3, 6, 7, 8, 9, 12, 13, 14, // 444, 10
- 2, 6, 8, 9, 10, 12, 13, 14, // 454, 8
- 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, // 462, 12
- 0, 7, 9, 10, 11, 12, 13, 14, // 474, 8
- 1, 2, 3, 4, 5, 6, 8, 15, // 482, 8
- 3, 7, 11, 15, // 490, 4
- 0, 1, 3, 4, 5, 7, 11, 15, // 494, 8
- 0, 4, 5, 10, 11, 15, // 502, 6
- 1, 2, 3, 6, 7, 10, 11, 15, // 508, 8
- 0, 1, 2, 3, 5, 6, 7, 10, 11, 15, // 516, 10
- 0, 4, 5, 6, 9, 10, 11, 15, // 526, 8
- 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 15, // 534, 12
- 1, 2, 4, 5, 8, 9, 12, 15, // 546, 8
- 2, 3, 5, 6, 8, 9, 12, 15, // 554, 8
- 0, 3, 5, 6, 9, 10, 12, 15, // 562, 8
- 1, 2, 4, 7, 9, 10, 12, 15, // 570, 8
- 1, 2, 5, 6, 8, 11, 12, 15, // 578, 8
- 0, 3, 4, 7, 8, 11, 12, 15, // 586, 8
- 0, 1, 5, 6, 10, 11, 12, 15, // 594, 8
- 1, 2, 6, 7, 10, 11, 12, 15, // 602, 8
- 1, 3, 4, 6, 8, 10, 13, 15, // 610, 8
- 0, 2, 5, 7, 8, 10, 13, 15, // 618, 8
- 0, 2, 4, 6, 9, 11, 13, 15, // 626, 8
- 1, 3, 5, 7, 9, 11, 13, 15, // 634, 8
- 0, 1, 2, 3, 4, 5, 7, 8, 12, 13, 15, // 642, 11
- 2, 3, 4, 5, 8, 9, 14, 15, // 653, 8
- 0, 1, 6, 7, 8, 9, 14, 15, // 661, 8
- 0, 1, 5, 10, 14, 15, // 669, 6
- 0, 3, 4, 5, 9, 10, 14, 15, // 675, 8
- 0, 1, 5, 6, 9, 10, 14, 15, // 683, 8
- 11, 14, 15, // 691, 3
- 7, 11, 14, 15, // 694, 4
- 1, 2, 4, 5, 8, 11, 14, 15, // 698, 8
- 0, 1, 4, 7, 8, 11, 14, 15, // 706, 8
- 0, 1, 4, 5, 10, 11, 14, 15, // 714, 8
- 2, 3, 6, 7, 10, 11, 14, 15, // 722, 8
- 4, 5, 6, 7, 10, 11, 14, 15, // 730, 8
- 0, 1, 4, 5, 7, 8, 10, 11, 14, 15, // 738, 10
- 0, 1, 2, 3, 5, 6, 7, 9, 10, 11, 14, 15, // 748, 12
- 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 14, 15, // 760, 13
- 0, 1, 2, 3, 4, 6, 7, 11, 12, 14, 15, // 773, 11
- 3, 4, 8, 9, 10, 13, 14, 15, // 784, 8
- 11, 13, 14, 15, // 792, 4
- 0, 1, 2, 4, 11, 13, 14, 15, // 796, 8
- 0, 1, 2, 4, 5, 10, 11, 13, 14, 15, // 804, 10
- 7, 10, 11, 13, 14, 15, // 814, 6
- 3, 6, 7, 10, 11, 13, 14, 15, // 820, 8
- 1, 5, 9, 10, 11, 13, 14, 15, // 828, 8
- 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, // 836, 12
- 12, 13, 14, 15, // 848, 4
- 0, 1, 2, 3, 12, 13, 14, 15, // 852, 8
- 0, 1, 4, 5, 12, 13, 14, 15, // 860, 8
- 4, 5, 6, 7, 12, 13, 14, 15, // 868, 8
- 4, 8, 9, 10, 12, 13, 14, 15, // 876, 8
- 0, 4, 5, 8, 9, 10, 12, 13, 14, 15, // 884, 10
- 0, 1, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, // 894, 12
- 0, 1, 2, 3, 4, 7, 8, 11, 12, 13, 14, 15, // 906, 12
- 0, 1, 3, 4, 8, 9, 11, 12, 13, 14, 15, // 918, 11
- 0, 2, 3, 7, 8, 10, 11, 12, 13, 14, 15, // 929, 11
- 7, 9, 10, 11, 12, 13, 14, 15, // 940, 8
- 3, 6, 7, 9, 10, 11, 12, 13, 14, 15, // 948, 10
- 2, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, // 958, 12
- 8, 9, 10, 11, 12, 13, 14, 15, // 970, 8
- 0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, // 978, 12
- 0, 1, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, // 990, 13
- 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1003, 12
- 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1015, 13
- 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1028, 12
- 0, 2, // 1040, 2
- 1, 3, // 1042, 2
- 0, 1, 4, 5, // 1044, 4
- 0, 1, 2, 4, 5, // 1048, 5
- 2, 3, 6, // 1053, 3
- 0, 2, 4, 6, // 1056, 4
- 1, 2, 5, 6, // 1060, 4
- 0, 1, 2, 3, 5, 6, // 1064, 6
- 0, 1, 2, 4, 5, 6, // 1070, 6
- 0, 1, 2, 3, 4, 5, 6, // 1076, 7
- 0, 3, 4, 7, // 1083, 4
- 0, 1, 2, 3, 4, 7, // 1087, 6
- 1, 3, 5, 7, // 1093, 4
- 2, 3, 6, 7, // 1097, 4
- 1, 2, 3, 6, 7, // 1101, 5
- 1, 2, 3, 5, 6, 7, // 1106, 6
- 0, 1, 2, 3, 5, 6, 7, // 1112, 7
- 4, 5, 6, 7, // 1119, 4
- 0, 8, // 1123, 2
- 0, 1, 4, 5, 8, // 1125, 5
- 0, 1, 8, 9, // 1130, 4
- 4, 5, 8, 9, // 1134, 4
- 0, 1, 4, 5, 8, 9, // 1138, 6
- 2, 6, 8, 9, // 1144, 4
- 6, 7, 8, 9, // 1148, 4
- 0, 2, 4, 6, 8, 10, // 1152, 6
- 1, 2, 5, 6, 9, 10, // 1158, 6
- 0, 3, 4, 7, 9, 10, // 1164, 6
- 0, 1, 2, 8, 9, 10, // 1170, 6
- 4, 5, 6, 8, 9, 10, // 1176, 6
- 3, 11, // 1182, 2
- 2, 3, 6, 7, 11, // 1184, 5
- 0, 3, 8, 11, // 1189, 4
- 0, 3, 4, 7, 8, 11, // 1193, 6
- 1, 3, 5, 7, 9, 11, // 1199, 6
- 2, 3, 10, 11, // 1205, 4
- 1, 5, 10, 11, // 1209, 4
- 4, 5, 10, 11, // 1213, 4
- 6, 7, 10, 11, // 1217, 4
- 2, 3, 6, 7, 10, 11, // 1221, 6
- 1, 2, 3, 9, 10, 11, // 1227, 6
- 5, 6, 7, 9, 10, 11, // 1233, 6
- 8, 9, 10, 11, // 1239, 4
- 4, 12, // 1243, 2
- 0, 1, 2, 3, 4, 5, 8, 12, // 1245, 8
- 8, 9, 12, // 1253, 3
- 0, 4, 5, 8, 9, 12, // 1256, 6
- 0, 1, 4, 5, 8, 9, 12, // 1262, 7
- 2, 3, 5, 6, 8, 9, 12, // 1269, 7
- 1, 5, 9, 13, // 1276, 4
- 6, 7, 9, 13, // 1280, 4
- 1, 4, 7, 10, 13, // 1284, 5
- 1, 6, 8, 11, 13, // 1289, 5
- 0, 1, 12, 13, // 1294, 4
- 4, 5, 12, 13, // 1298, 4
- 0, 1, 6, 7, 12, 13, // 1302, 6
- 0, 1, 4, 8, 12, 13, // 1308, 6
- 8, 9, 12, 13, // 1314, 4
- 4, 8, 9, 12, 13, // 1318, 5
- 4, 5, 8, 9, 12, 13, // 1323, 6
- 0, 4, 5, 8, 9, 12, 13, // 1329, 7
- 0, 1, 6, 10, 12, 13, // 1336, 6
- 3, 6, 7, 9, 10, 12, 13, // 1342, 7
- 0, 1, 10, 11, 12, 13, // 1349, 6
- 2, 4, 7, 9, 14, // 1355, 5
- 4, 5, 10, 14, // 1360, 4
- 2, 6, 10, 14, // 1364, 4
- 2, 5, 8, 11, 14, // 1368, 5
- 0, 2, 12, 14, // 1373, 4
- 8, 10, 12, 14, // 1377, 4
- 4, 6, 8, 10, 12, 14, // 1381, 6
- 13, 14, // 1387, 2
- 9, 10, 13, 14, // 1389, 4
- 5, 6, 9, 10, 13, 14, // 1393, 6
- 0, 1, 2, 12, 13, 14, // 1399, 6
- 4, 5, 6, 12, 13, 14, // 1405, 6
- 8, 9, 12, 13, 14, // 1411, 5
- 8, 9, 10, 12, 13, 14, // 1416, 6
- 7, 15, // 1422, 2
- 0, 5, 10, 15, // 1424, 4
- 0, 1, 2, 3, 6, 7, 11, 15, // 1428, 8
- 10, 11, 15, // 1436, 3
- 0, 1, 5, 6, 10, 11, 15, // 1439, 7
- 3, 6, 7, 10, 11, 15, // 1446, 6
- 12, 15, // 1452, 2
- 0, 3, 12, 15, // 1454, 4
- 4, 7, 12, 15, // 1458, 4
- 0, 3, 6, 9, 12, 15, // 1462, 6
- 0, 3, 5, 10, 12, 15, // 1468, 6
- 8, 11, 12, 15, // 1474, 4
- 5, 6, 8, 11, 12, 15, // 1478, 6
- 4, 7, 8, 11, 12, 15, // 1484, 6
- 1, 3, 13, 15, // 1490, 4
- 9, 11, 13, 15, // 1494, 4
- 5, 7, 9, 11, 13, 15, // 1498, 6
- 2, 3, 14, 15, // 1504, 4
- 2, 3, 4, 5, 14, 15, // 1508, 6
- 6, 7, 14, 15, // 1514, 4
- 2, 3, 5, 9, 14, 15, // 1518, 6
- 2, 3, 8, 9, 14, 15, // 1524, 6
- 10, 14, 15, // 1530, 3
- 0, 4, 5, 9, 10, 14, 15, // 1533, 7
- 2, 3, 7, 11, 14, 15, // 1540, 6
- 10, 11, 14, 15, // 1546, 4
- 7, 10, 11, 14, 15, // 1550, 5
- 6, 7, 10, 11, 14, 15, // 1555, 6
- 1, 2, 3, 13, 14, 15, // 1561, 6
- 5, 6, 7, 13, 14, 15, // 1567, 6
- 10, 11, 13, 14, 15, // 1573, 5
- 9, 10, 11, 13, 14, 15, // 1578, 6
- 0, 4, 8, 9, 12, 13, 14, 15, // 1584, 8
- 9, 10, 12, 13, 14, 15, // 1592, 6
- 8, 11, 12, 13, 14, 15, // 1598, 6
- 3, 7, 10, 11, 12, 13, 14, 15, // 1604, 8
- };
- static const int g_shapeRanges[][2] =
- {
- { 0, 16 },{ 16, 4 },{ 20, 3 },{ 23, 4 },{ 27, 3 },{ 30, 4 },{ 34, 8 },{ 42, 4 },{ 46, 6 },{ 52, 8 },{ 60, 5 },
- { 65, 5 },{ 70, 4 },{ 74, 4 },{ 78, 6 },{ 84, 8 },{ 92, 8 },{ 100, 8 },{ 108, 8 },{ 116, 12 },{ 128, 4 },{ 132, 8 },
- { 140, 8 },{ 148, 10 },{ 158, 6 },{ 164, 8 },{ 172, 12 },{ 184, 8 },{ 192, 5 },{ 197, 3 },{ 200, 4 },{ 204, 6 },{ 210, 8 },
- { 218, 8 },{ 226, 8 },{ 234, 8 },{ 242, 8 },{ 250, 12 },{ 262, 13 },{ 275, 8 },{ 283, 8 },{ 291, 10 },{ 301, 8 },{ 309, 8 },
- { 317, 5 },{ 322, 8 },{ 330, 8 },{ 338, 8 },{ 346, 8 },{ 354, 8 },{ 362, 8 },{ 370, 8 },{ 378, 8 },{ 386, 8 },{ 394, 8 },
- { 402, 8 },{ 410, 8 },{ 418, 4 },{ 422, 8 },{ 430, 6 },{ 436, 8 },{ 444, 10 },{ 454, 8 },{ 462, 12 },{ 474, 8 },{ 482, 8 },
- { 490, 4 },{ 494, 8 },{ 502, 6 },{ 508, 8 },{ 516, 10 },{ 526, 8 },{ 534, 12 },{ 546, 8 },{ 554, 8 },{ 562, 8 },{ 570, 8 },
- { 578, 8 },{ 586, 8 },{ 594, 8 },{ 602, 8 },{ 610, 8 },{ 618, 8 },{ 626, 8 },{ 634, 8 },{ 642, 11 },{ 653, 8 },{ 661, 8 },
- { 669, 6 },{ 675, 8 },{ 683, 8 },{ 691, 3 },{ 694, 4 },{ 698, 8 },{ 706, 8 },{ 714, 8 },{ 722, 8 },{ 730, 8 },{ 738, 10 },
- { 748, 12 },{ 760, 13 },{ 773, 11 },{ 784, 8 },{ 792, 4 },{ 796, 8 },{ 804, 10 },{ 814, 6 },{ 820, 8 },{ 828, 8 },{ 836, 12 },
- { 848, 4 },{ 852, 8 },{ 860, 8 },{ 868, 8 },{ 876, 8 },{ 884, 10 },{ 894, 12 },{ 906, 12 },{ 918, 11 },{ 929, 11 },{ 940, 8 },
- { 948, 10 },{ 958, 12 },{ 970, 8 },{ 978, 12 },{ 990, 13 },{ 1003, 12 },{ 1015, 13 },{ 1028, 12 },{ 1040, 2 },{ 1042, 2 },{ 1044, 4 },
- { 1048, 5 },{ 1053, 3 },{ 1056, 4 },{ 1060, 4 },{ 1064, 6 },{ 1070, 6 },{ 1076, 7 },{ 1083, 4 },{ 1087, 6 },{ 1093, 4 },{ 1097, 4 },
- { 1101, 5 },{ 1106, 6 },{ 1112, 7 },{ 1119, 4 },{ 1123, 2 },{ 1125, 5 },{ 1130, 4 },{ 1134, 4 },{ 1138, 6 },{ 1144, 4 },{ 1148, 4 },
- { 1152, 6 },{ 1158, 6 },{ 1164, 6 },{ 1170, 6 },{ 1176, 6 },{ 1182, 2 },{ 1184, 5 },{ 1189, 4 },{ 1193, 6 },{ 1199, 6 },{ 1205, 4 },
- { 1209, 4 },{ 1213, 4 },{ 1217, 4 },{ 1221, 6 },{ 1227, 6 },{ 1233, 6 },{ 1239, 4 },{ 1243, 2 },{ 1245, 8 },{ 1253, 3 },{ 1256, 6 },
- { 1262, 7 },{ 1269, 7 },{ 1276, 4 },{ 1280, 4 },{ 1284, 5 },{ 1289, 5 },{ 1294, 4 },{ 1298, 4 },{ 1302, 6 },{ 1308, 6 },{ 1314, 4 },
- { 1318, 5 },{ 1323, 6 },{ 1329, 7 },{ 1336, 6 },{ 1342, 7 },{ 1349, 6 },{ 1355, 5 },{ 1360, 4 },{ 1364, 4 },{ 1368, 5 },{ 1373, 4 },
- { 1377, 4 },{ 1381, 6 },{ 1387, 2 },{ 1389, 4 },{ 1393, 6 },{ 1399, 6 },{ 1405, 6 },{ 1411, 5 },{ 1416, 6 },{ 1422, 2 },{ 1424, 4 },
- { 1428, 8 },{ 1436, 3 },{ 1439, 7 },{ 1446, 6 },{ 1452, 2 },{ 1454, 4 },{ 1458, 4 },{ 1462, 6 },{ 1468, 6 },{ 1474, 4 },{ 1478, 6 },
- { 1484, 6 },{ 1490, 4 },{ 1494, 4 },{ 1498, 6 },{ 1504, 4 },{ 1508, 6 },{ 1514, 4 },{ 1518, 6 },{ 1524, 6 },{ 1530, 3 },{ 1533, 7 },
- { 1540, 6 },{ 1546, 4 },{ 1550, 5 },{ 1555, 6 },{ 1561, 6 },{ 1567, 6 },{ 1573, 5 },{ 1578, 6 },{ 1584, 8 },{ 1592, 6 },{ 1598, 6 },
- { 1604, 8 },
- };
- static const int g_shapes1[][2] =
- {
- { 0, 16 }
- };
- static const int g_shapes2[64][2] =
- {
- { 33, 96 },{ 63, 66 },{ 20, 109 },{ 22, 107 },{ 37, 92 },{ 7, 122 },{ 8, 121 },{ 23, 106 },
- { 38, 91 },{ 2, 127 },{ 9, 120 },{ 26, 103 },{ 3, 126 },{ 6, 123 },{ 1, 128 },{ 19, 110 },
- { 15, 114 },{ 124, 5 },{ 72, 57 },{ 115, 14 },{ 125, 4 },{ 70, 59 },{ 100, 29 },{ 60, 69 },
- { 116, 13 },{ 99, 30 },{ 78, 51 },{ 94, 35 },{ 104, 25 },{ 111, 18 },{ 71, 58 },{ 90, 39 },
- { 45, 84 },{ 16, 113 },{ 82, 47 },{ 95, 34 },{ 87, 42 },{ 83, 46 },{ 53, 76 },{ 48, 81 },
- { 68, 61 },{ 105, 24 },{ 98, 31 },{ 88, 41 },{ 75, 54 },{ 43, 86 },{ 52, 77 },{ 117, 12 },
- { 119, 10 },{ 118, 11 },{ 85, 44 },{ 101, 28 },{ 36, 93 },{ 55, 74 },{ 89, 40 },{ 79, 50 },
- { 56, 73 },{ 49, 80 },{ 64, 65 },{ 27, 102 },{ 32, 97 },{ 112, 17 },{ 67, 62 },{ 21, 108 },
- };
- static const int g_shapes3[64][3] =
- {
- { 148, 160, 240 },{ 132, 212, 205 },{ 136, 233, 187 },{ 175, 237, 143 },{ 6, 186, 232 },{ 33, 142, 232 },{ 131, 123, 142 },{ 131, 96, 186 },
- { 6, 171, 110 },{ 1, 18, 110 },{ 1, 146, 123 },{ 33, 195, 66 },{ 20, 51, 66 },{ 20, 178, 96 },{ 2, 177, 106 },{ 211, 4, 59 },
- { 8, 191, 91 },{ 230, 14, 29 },{ 1, 188, 234 },{ 151, 110, 168 },{ 20, 144, 238 },{ 137, 66, 206 },{ 173, 179, 232 },{ 209, 194, 186 },
- { 239, 165, 142 },{ 131, 152, 242 },{ 214, 54, 12 },{ 140, 219, 201 },{ 190, 150, 231 },{ 156, 135, 241 },{ 185, 227, 167 },{ 145, 210, 59 },
- { 138, 174, 106 },{ 189, 229, 14 },{ 176, 133, 106 },{ 78, 178, 195 },{ 111, 146, 171 },{ 216, 180, 196 },{ 217, 181, 193 },{ 184, 228, 166 },
- { 192, 225, 153 },{ 134, 141, 123 },{ 6, 222, 198 },{ 149, 183, 96 },{ 33, 226, 164 },{ 161, 215, 51 },{ 197, 221, 18 },{ 1, 223, 199 },
- { 154, 163, 110 },{ 20, 236, 169 },{ 157, 204, 66 },{ 1, 202, 220 },{ 20, 170, 235 },{ 203, 158, 66 },{ 162, 155, 110 },{ 6, 201, 218 },
- { 139, 135, 123 },{ 33, 167, 224 },{ 182, 150, 96 },{ 19, 200, 213 },{ 63, 207, 159 },{ 147, 172, 109 },{ 129, 130, 128 },{ 208, 14, 59 },
- };
-
- static const int g_shapeList1[] =
- {
- 0,
- };
-
- static const int g_shapeList1Collapse[] =
- {
- 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1,
- };
- static const int g_shapeList2[] =
- {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
- 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
- 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
- 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
- 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
- 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
- 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
- 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
- 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
- 122, 123, 124, 125, 126, 127, 128,
- };
- static const int g_shapeList2Collapse[] =
- {
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
- 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
- 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
- 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
- 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
- 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1,
- };
-
- static const int g_shapeList12[] =
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
- 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
- 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
- 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128,
- };
-
- static const int g_shapeList12Collapse[] =
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
- 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
- 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
- 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1,
- };
-
- static const int g_shapeList3[] =
- {
- 1, 2, 4, 6, 8, 12, 14, 18, 19, 20, 29,
- 33, 51, 54, 59, 63, 66, 78, 91, 96, 106, 109,
- 110, 111, 123, 128, 129, 130, 131, 132, 133, 134, 135,
- 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
- 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
- 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
- 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
- 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
- 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
- 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
- 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
- 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
- 235, 236, 237, 238, 239, 240, 241, 242,
- };
-
- static const int g_shapeList3Collapse[] =
- {
- -1, 0, 1, -1, 2, -1, 3, -1, 4, -1, -1,
- -1, 5, -1, 6, -1, -1, -1, 7, 8, 9, -1,
- -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, -1,
- 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, 13,
- -1, -1, -1, -1, 14, -1, -1, -1, 15, -1, -1,
- 16, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 18, -1, -1, -1, -1, 19, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 20, -1, -1, 21,
- 22, 23, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 24, -1, -1, -1, -1, 25, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
- 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
- 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
- 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
- 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
- 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
- 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
- 139,
- };
-
- static const int g_shapeList3Short[] =
- {
- 1, 2, 4, 6, 18, 20, 33, 51, 59, 66, 96,
- 106, 110, 123, 131, 132, 136, 142, 143, 146, 148, 160,
- 171, 175, 177, 178, 186, 187, 195, 205, 211, 212, 232,
- 233, 237, 240,
- };
-
- static const int g_shapeList3ShortCollapse[] =
- {
- -1, 0, 1, -1, 2, -1, 3, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4, -1, 5, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1,
- 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 11, -1, -1, -1,
- 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 13, -1, -1, -1, -1, -1, -1, -1, 14,
- 15, -1, -1, -1, 16, -1, -1, -1, -1, -1, 17,
- 18, -1, -1, 19, -1, 20, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 21, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, 23,
- -1, 24, 25, -1, -1, -1, -1, -1, -1, -1, 26,
- 27, -1, -1, -1, -1, -1, -1, -1, 28, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
- -1, -1, 30, 31, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 32, 33, -1, -1, -1, 34, -1, -1, 35, -1,
- -1,
- };
-
- static const int g_shapeListAll[] =
- {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
- 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
- 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
- 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
- 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
- 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
- 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
- 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
- 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
- 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
- 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
- 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
- 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
- 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
- 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
- 242,
- };
-
- static const int g_numShapes1 = sizeof(g_shapeList1) / sizeof(g_shapeList1[0]);
- static const int g_numShapes2 = sizeof(g_shapeList2) / sizeof(g_shapeList2[0]);
- static const int g_numShapes12 = sizeof(g_shapeList12) / sizeof(g_shapeList12[0]);
- static const int g_numShapes3 = sizeof(g_shapeList3) / sizeof(g_shapeList3[0]);
- static const int g_numShapes3Short = sizeof(g_shapeList3Short) / sizeof(g_shapeList3Short[0]);
- static const int g_numShapesAll = sizeof(g_shapeListAll) / sizeof(g_shapeListAll[0]);
- static const int g_numFragments = sizeof(g_fragments) / sizeof(g_fragments[0]);
-
- static const int g_maxFragmentsPerMode = (g_numShapes2 > g_numShapes3) ? g_numShapes2 : g_numShapes3;
- }
-
- namespace BC6HData
- {
- enum EField
- {
- NA, // N/A
- M, // Mode
- D, // Shape
- RW,
- RX,
- RY,
- RZ,
- GW,
- GX,
- GY,
- GZ,
- BW,
- BX,
- BY,
- BZ,
- };
-
- struct ModeDescriptor
- {
- EField m_eField;
- uint8_t m_uBit;
- };
-
- const ModeDescriptor g_modeDescriptors[14][82] =
- {
- { // Mode 1 (0x00) - 10 5 5 5
- { M, 0 },{ M, 1 },{ GY, 4 },{ BY, 4 },{ BZ, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 2 (0x01) - 7 6 6 6
- { M, 0 },{ M, 1 },{ GY, 5 },{ GZ, 4 },{ GZ, 5 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 3 (0x02) - 11 5 4 4
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RW,10 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 4 (0x06) - 11 4 5 4
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 },
- { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GW,10 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,10 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 0 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ GY, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 5 (0x0a) - 11 4 4 5
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,10 },
- { BY, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,10 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BW,10 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ BZ, 1 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ BZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 6 (0x0e) - 9 5 5 5
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 7 (0x12) - 8 6 5 5
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ GZ, 4 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 3 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 8 (0x16) - 8 5 6 5
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 0 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ GZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BZ, 1 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 9 (0x1a) - 8 5 5 6
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ BY, 5 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { GZ, 4 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { BZ, 0 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { BZ, 2 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ BZ, 3 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 10 (0x1e) - 6 6 6 6
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ GZ, 4 },{ BZ, 0 },{ BZ, 1 },{ BY, 4 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GY, 5 },{ BY, 5 },{ BZ, 2 },{ GY, 4 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ GZ, 5 },{ BZ, 3 },{ BZ, 5 },{ BZ, 4 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ GY, 0 },{ GY, 1 },{ GY, 2 },{ GY, 3 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GZ, 0 },{ GZ, 1 },{ GZ, 2 },{ GZ, 3 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BY, 0 },{ BY, 1 },{ BY, 2 },{ BY, 3 },{ RY, 0 },{ RY, 1 },{ RY, 2 },{ RY, 3 },{ RY, 4 },
- { RY, 5 },{ RZ, 0 },{ RZ, 1 },{ RZ, 2 },{ RZ, 3 },{ RZ, 4 },{ RZ, 5 },{ D, 0 },{ D, 1 },{ D, 2 },
- { D, 3 },{ D, 4 },
- },
-
- { // Mode 11 (0x03) - 10 10
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RX, 9 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GX, 9 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BX, 9 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },
- },
-
- { // Mode 12 (0x07) - 11 9
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ RX, 6 },{ RX, 7 },{ RX, 8 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GX, 6 },{ GX, 7 },{ GX, 8 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BX, 6 },{ BX, 7 },{ BX, 8 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },
- },
-
- { // Mode 13 (0x0b) - 12 8
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RX, 4 },
- { RX, 5 },{ RX, 6 },{ RX, 7 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GX, 4 },
- { GX, 5 },{ GX, 6 },{ GX, 7 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BX, 4 },
- { BX, 5 },{ BX, 6 },{ BX, 7 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },
- },
-
- { // Mode 14 (0x0f) - 16 4
- { M, 0 },{ M, 1 },{ M, 2 },{ M, 3 },{ M, 4 },{ RW, 0 },{ RW, 1 },{ RW, 2 },{ RW, 3 },{ RW, 4 },
- { RW, 5 },{ RW, 6 },{ RW, 7 },{ RW, 8 },{ RW, 9 },{ GW, 0 },{ GW, 1 },{ GW, 2 },{ GW, 3 },{ GW, 4 },
- { GW, 5 },{ GW, 6 },{ GW, 7 },{ GW, 8 },{ GW, 9 },{ BW, 0 },{ BW, 1 },{ BW, 2 },{ BW, 3 },{ BW, 4 },
- { BW, 5 },{ BW, 6 },{ BW, 7 },{ BW, 8 },{ BW, 9 },{ RX, 0 },{ RX, 1 },{ RX, 2 },{ RX, 3 },{ RW,15 },
- { RW,14 },{ RW,13 },{ RW,12 },{ RW,11 },{ RW,10 },{ GX, 0 },{ GX, 1 },{ GX, 2 },{ GX, 3 },{ GW,15 },
- { GW,14 },{ GW,13 },{ GW,12 },{ GW,11 },{ GW,10 },{ BX, 0 },{ BX, 1 },{ BX, 2 },{ BX, 3 },{ BW,15 },
- { BW,14 },{ BW,13 },{ BW,12 },{ BW,11 },{ BW,10 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },{ NA, 0 },
- { NA, 0 },{ NA, 0 },
- },
- };
- }
-
- struct PackingVector
- {
- uint32_t m_vector[4];
- int m_offset;
-
- void Init()
- {
- for (int i = 0; i < 4; i++)
- m_vector[i] = 0;
-
- m_offset = 0;
- }
-
- inline void Pack(ParallelMath::ScalarUInt16 value, int bits)
- {
- int vOffset = m_offset >> 5;
- int bitOffset = m_offset & 0x1f;
-
- m_vector[vOffset] |= (static_cast<uint32_t>(value) << bitOffset) & static_cast<uint32_t>(0xffffffff);
-
- int overflowBits = bitOffset + bits - 32;
- if (overflowBits > 0)
- m_vector[vOffset + 1] |= (static_cast<uint32_t>(value) >> (bits - overflowBits));
-
- m_offset += bits;
- }
-
- inline void Flush(uint8_t* output)
- {
- assert(m_offset == 128);
-
- for (int v = 0; v < 4; v++)
- {
- uint32_t chunk = m_vector[v];
- for (int b = 0; b < 4; b++)
- output[v * 4 + b] = static_cast<uint8_t>((chunk >> (b * 8)) & 0xff);
- }
- }
- };
-
-
- struct UnpackingVector
- {
- uint32_t m_vector[4];
-
- void Init(const uint8_t *bytes)
- {
- for (int i = 0; i < 4; i++)
- m_vector[i] = 0;
-
- for (int b = 0; b < 16; b++)
- m_vector[b / 4] |= (bytes[b] << ((b % 4) * 8));
- }
-
- inline ParallelMath::ScalarUInt16 Unpack(int bits)
- {
- uint32_t bitMask = (1 << bits) - 1;
-
- ParallelMath::ScalarUInt16 result = static_cast<ParallelMath::ScalarUInt16>(m_vector[0] & bitMask);
-
- for (int i = 0; i < 4; i++)
- {
- m_vector[i] >>= bits;
- if (i != 3)
- m_vector[i] |= (m_vector[i + 1] & bitMask) << (32 - bits);
- }
-
- return result;
- }
- };
-
- void ComputeTweakFactors(int tweak, int range, float *outFactors)
- {
- int totalUnits = range - 1;
- int minOutsideUnits = ((tweak >> 1) & 1);
- int maxOutsideUnits = (tweak & 1);
- int insideUnits = totalUnits - minOutsideUnits - maxOutsideUnits;
-
- outFactors[0] = -static_cast<float>(minOutsideUnits) / static_cast<float>(insideUnits);
- outFactors[1] = static_cast<float>(maxOutsideUnits) / static_cast<float>(insideUnits) + 1.0f;
- }
-
- ParallelMath::Float ScaleHDRValue(const ParallelMath::Float &v, bool isSigned)
- {
- if (isSigned)
- {
- ParallelMath::Float offset = ParallelMath::Select(ParallelMath::Less(v, ParallelMath::MakeFloatZero()), ParallelMath::MakeFloat(-30.0f), ParallelMath::MakeFloat(30.0f));
- return (v * 32.0f + offset) / 31.0f;
- }
- else
- return (v * 64.0f + 30.0f) / 31.0f;
- }
-
- ParallelMath::SInt16 UnscaleHDRValueSigned(const ParallelMath::SInt16 &v)
- {
-#ifdef CVTT_ENABLE_ASSERTS
- for (int i = 0; i < ParallelMath::ParallelSize; i++)
- assert(ParallelMath::Extract(v, i) != -32768)
-#endif
-
- ParallelMath::Int16CompFlag negative = ParallelMath::Less(v, ParallelMath::MakeSInt16(0));
- ParallelMath::UInt15 absComp = ParallelMath::LosslessCast<ParallelMath::UInt15>::Cast(ParallelMath::Select(negative, ParallelMath::SInt16(ParallelMath::MakeSInt16(0) - v), v));
-
- ParallelMath::UInt31 multiplied = ParallelMath::XMultiply(absComp, ParallelMath::MakeUInt15(31));
- ParallelMath::UInt31 shifted = ParallelMath::RightShift(multiplied, 5);
- ParallelMath::UInt15 absCompScaled = ParallelMath::ToUInt15(shifted);
- ParallelMath::SInt16 signBits = ParallelMath::SelectOrZero(negative, ParallelMath::MakeSInt16(-32768));
-
- return ParallelMath::LosslessCast<ParallelMath::SInt16>::Cast(absCompScaled) | signBits;
- }
-
- ParallelMath::UInt15 UnscaleHDRValueUnsigned(const ParallelMath::UInt16 &v)
- {
- return ParallelMath::ToUInt15(ParallelMath::RightShift(ParallelMath::XMultiply(v, ParallelMath::MakeUInt15(31)), 6));
- }
-
- void UnscaleHDREndpoints(const ParallelMath::AInt16 inEP[2][3], ParallelMath::AInt16 outEP[2][3], bool isSigned)
- {
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- if (isSigned)
- outEP[epi][ch] = ParallelMath::LosslessCast<ParallelMath::AInt16>::Cast(UnscaleHDRValueSigned(ParallelMath::LosslessCast<ParallelMath::SInt16>::Cast(inEP[epi][ch])));
- else
- outEP[epi][ch] = ParallelMath::LosslessCast<ParallelMath::AInt16>::Cast(UnscaleHDRValueUnsigned(ParallelMath::LosslessCast<ParallelMath::UInt16>::Cast(inEP[epi][ch])));
- }
- }
- }
-
- template<int TVectorSize>
- class UnfinishedEndpoints
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::SInt32 MSInt32;
-
- UnfinishedEndpoints()
- {
- }
-
- UnfinishedEndpoints(const MFloat *base, const MFloat *offset)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- m_base[ch] = base[ch];
- for (int ch = 0; ch < TVectorSize; ch++)
- m_offset[ch] = offset[ch];
- }
-
- UnfinishedEndpoints(const UnfinishedEndpoints& other)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- m_base[ch] = other.m_base[ch];
- for (int ch = 0; ch < TVectorSize; ch++)
- m_offset[ch] = other.m_offset[ch];
- }
-
- void FinishHDRUnsigned(int tweak, int range, MSInt16 *outEP0, MSInt16 *outEP1, ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- float tweakFactors[2];
- ComputeTweakFactors(tweak, range, tweakFactors);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MUInt15 channelEPs[2];
- for (int epi = 0; epi < 2; epi++)
- {
- MFloat f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[epi], 0.0f, 31743.0f);
- channelEPs[epi] = ParallelMath::RoundAndConvertToU15(f, roundingMode);
- }
-
- outEP0[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(channelEPs[0]);
- outEP1[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(channelEPs[1]);
- }
- }
-
- void FinishHDRSigned(int tweak, int range, MSInt16* outEP0, MSInt16* outEP1, ParallelMath::RoundTowardNearestForScope* roundingMode)
- {
- float tweakFactors[2];
- ComputeTweakFactors(tweak, range, tweakFactors);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MSInt16 channelEPs[2];
- for (int epi = 0; epi < 2; epi++)
- {
- MFloat f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[epi], -31743.0f, 31743.0f);
- channelEPs[epi] = ParallelMath::RoundAndConvertToS16(f, roundingMode);
- }
-
- outEP0[ch] = channelEPs[0];
- outEP1[ch] = channelEPs[1];
- }
- }
-
- void FinishLDR(int tweak, int range, MUInt15* outEP0, MUInt15* outEP1)
- {
- ParallelMath::RoundTowardNearestForScope roundingMode;
-
- float tweakFactors[2];
- ComputeTweakFactors(tweak, range, tweakFactors);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MFloat ep0f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[0], 0.0f, 255.0f);
- MFloat ep1f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[1], 0.0f, 255.0f);
- outEP0[ch] = ParallelMath::RoundAndConvertToU15(ep0f, &roundingMode);
- outEP1[ch] = ParallelMath::RoundAndConvertToU15(ep1f, &roundingMode);
- }
- }
-
- template<int TNewVectorSize>
- UnfinishedEndpoints<TNewVectorSize> ExpandTo(float filler)
- {
- MFloat newBase[TNewVectorSize];
- MFloat newOffset[TNewVectorSize];
-
- for (int ch = 0; ch < TNewVectorSize && ch < TVectorSize; ch++)
- {
- newBase[ch] = m_base[ch];
- newOffset[ch] = m_offset[ch];
- }
-
- MFloat fillerV = ParallelMath::MakeFloat(filler);
-
- for (int ch = TVectorSize; ch < TNewVectorSize; ch++)
- {
- newBase[ch] = fillerV;
- newOffset[ch] = ParallelMath::MakeFloatZero();
- }
-
- return UnfinishedEndpoints<TNewVectorSize>(newBase, newOffset);
- }
-
- private:
- MFloat m_base[TVectorSize];
- MFloat m_offset[TVectorSize];
- };
-
- template<int TMatrixSize>
- class PackedCovarianceMatrix
- {
- public:
- // 0: xx,
- // 1: xy, yy
- // 3: xz, yz, zz
- // 6: xw, yw, zw, ww
- // ... etc.
- static const int PyramidSize = (TMatrixSize * (TMatrixSize + 1)) / 2;
-
- typedef ParallelMath::Float MFloat;
-
- PackedCovarianceMatrix()
- {
- for (int i = 0; i < PyramidSize; i++)
- m_values[i] = ParallelMath::MakeFloatZero();
- }
-
- void Add(const ParallelMath::Float *vec, const ParallelMath::Float &weight)
- {
- int index = 0;
- for (int row = 0; row < TMatrixSize; row++)
- {
- for (int col = 0; col <= row; col++)
- {
- m_values[index] = m_values[index] + vec[row] * vec[col] * weight;
- index++;
- }
- }
- }
-
- void Product(MFloat *outVec, const MFloat *inVec)
- {
- for (int row = 0; row < TMatrixSize; row++)
- {
- MFloat sum = ParallelMath::MakeFloatZero();
-
- int index = (row * (row + 1)) >> 1;
- for (int col = 0; col < TMatrixSize; col++)
- {
- sum = sum + inVec[col] * m_values[index];
- if (col >= row)
- index += col + 1;
- else
- index++;
- }
-
- outVec[row] = sum;
- }
- }
-
- private:
- ParallelMath::Float m_values[PyramidSize];
- };
-
- static const int NumEndpointSelectorPasses = 3;
-
- template<int TVectorSize, int TIterationCount>
- class EndpointSelector
- {
- public:
- typedef ParallelMath::Float MFloat;
-
- EndpointSelector()
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- m_centroid[ch] = ParallelMath::MakeFloatZero();
- m_direction[ch] = ParallelMath::MakeFloatZero();
- }
- m_weightTotal = ParallelMath::MakeFloatZero();
- m_minDist = ParallelMath::MakeFloat(FLT_MAX);
- m_maxDist = ParallelMath::MakeFloat(-FLT_MAX);
- }
-
- void ContributePass(const MFloat *value, int pass, const MFloat &weight)
- {
- if (pass == 0)
- ContributeCentroid(value, weight);
- else if (pass == 1)
- ContributeDirection(value, weight);
- else if (pass == 2)
- ContributeMinMax(value);
- }
-
- void FinishPass(int pass)
- {
- if (pass == 0)
- FinishCentroid();
- else if (pass == 1)
- FinishDirection();
- }
-
- UnfinishedEndpoints<TVectorSize> GetEndpoints(const float channelWeights[TVectorSize]) const
- {
- MFloat unweightedBase[TVectorSize];
- MFloat unweightedOffset[TVectorSize];
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MFloat min = m_centroid[ch] + m_direction[ch] * m_minDist;
- MFloat max = m_centroid[ch] + m_direction[ch] * m_maxDist;
-
- float safeWeight = channelWeights[ch];
- if (safeWeight == 0.f)
- safeWeight = 1.0f;
-
- unweightedBase[ch] = min / channelWeights[ch];
- unweightedOffset[ch] = (max - min) / channelWeights[ch];
- }
-
- return UnfinishedEndpoints<TVectorSize>(unweightedBase, unweightedOffset);
- }
-
- private:
- void ContributeCentroid(const MFloat *value, const MFloat &weight)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- m_centroid[ch] = m_centroid[ch] + value[ch] * weight;
- m_weightTotal = m_weightTotal + weight;
- }
-
- void FinishCentroid()
- {
- MFloat denom = m_weightTotal;
- ParallelMath::MakeSafeDenominator(denom);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- m_centroid[ch] = m_centroid[ch] / denom;
- }
-
- void ContributeDirection(const MFloat *value, const MFloat &weight)
- {
- MFloat diff[TVectorSize];
- for (int ch = 0; ch < TVectorSize; ch++)
- diff[ch] = value[ch] - m_centroid[ch];
-
- m_covarianceMatrix.Add(diff, weight);
- }
-
- void FinishDirection()
- {
- MFloat approx[TVectorSize];
- for (int ch = 0; ch < TVectorSize; ch++)
- approx[ch] = ParallelMath::MakeFloat(1.0f);
-
- for (int i = 0; i < TIterationCount; i++)
- {
- MFloat product[TVectorSize];
- m_covarianceMatrix.Product(product, approx);
-
- MFloat largestComponent = product[0];
- for (int ch = 1; ch < TVectorSize; ch++)
- largestComponent = ParallelMath::Max(largestComponent, product[ch]);
-
- // product = largestComponent*newApprox
- ParallelMath::MakeSafeDenominator(largestComponent);
- for (int ch = 0; ch < TVectorSize; ch++)
- approx[ch] = product[ch] / largestComponent;
- }
-
- // Normalize
- MFloat approxLen = ParallelMath::MakeFloatZero();
- for (int ch = 0; ch < TVectorSize; ch++)
- approxLen = approxLen + approx[ch] * approx[ch];
-
- approxLen = ParallelMath::Sqrt(approxLen);
-
- ParallelMath::MakeSafeDenominator(approxLen);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- m_direction[ch] = approx[ch] / approxLen;
- }
-
- void ContributeMinMax(const MFloat *value)
- {
- MFloat dist = ParallelMath::MakeFloatZero();
- for (int ch = 0; ch < TVectorSize; ch++)
- dist = dist + m_direction[ch] * (value[ch] - m_centroid[ch]);
-
- m_minDist = ParallelMath::Min(m_minDist, dist);
- m_maxDist = ParallelMath::Max(m_maxDist, dist);
- }
-
- ParallelMath::Float m_centroid[TVectorSize];
- ParallelMath::Float m_direction[TVectorSize];
- PackedCovarianceMatrix<TVectorSize> m_covarianceMatrix;
- ParallelMath::Float m_weightTotal;
-
- ParallelMath::Float m_minDist;
- ParallelMath::Float m_maxDist;
- };
-
- static const ParallelMath::UInt16 g_weightReciprocals[] =
- {
- ParallelMath::MakeUInt16(0), // -1
- ParallelMath::MakeUInt16(0), // 0
- ParallelMath::MakeUInt16(32768), // 1
- ParallelMath::MakeUInt16(16384), // 2
- ParallelMath::MakeUInt16(10923), // 3
- ParallelMath::MakeUInt16(8192), // 4
- ParallelMath::MakeUInt16(6554), // 5
- ParallelMath::MakeUInt16(5461), // 6
- ParallelMath::MakeUInt16(4681), // 7
- ParallelMath::MakeUInt16(4096), // 8
- ParallelMath::MakeUInt16(3641), // 9
- ParallelMath::MakeUInt16(3277), // 10
- ParallelMath::MakeUInt16(2979), // 11
- ParallelMath::MakeUInt16(2731), // 12
- ParallelMath::MakeUInt16(2521), // 13
- ParallelMath::MakeUInt16(2341), // 14
- ParallelMath::MakeUInt16(2185), // 15
- };
-
- template<int TVectorSize>
- class IndexSelector
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::AInt16 MAInt16;
- typedef ParallelMath::SInt32 MSInt32;
- typedef ParallelMath::UInt31 MUInt31;
-
- template<class TInterpolationEPType, class TColorEPType>
- void Init(const float *channelWeights, const TInterpolationEPType interpolationEndPoints[2][TVectorSize], const TColorEPType colorSpaceEndpoints[2][TVectorSize], int range)
- {
- // In BC6H, the interpolation endpoints are higher-precision than the endpoints in color space.
- // We need to select indexes using the color-space endpoints.
-
- m_isUniform = true;
- for (int ch = 1; ch < TVectorSize; ch++)
- {
- if (channelWeights[ch] != channelWeights[0])
- m_isUniform = false;
- }
-
- // To work with channel weights, we need something where:
- // pxDiff = px - ep[0]
- // epDiff = ep[1] - ep[0]
- //
- // weightedEPDiff = epDiff * channelWeights
- // normalizedWeightedAxis = weightedEPDiff / len(weightedEPDiff)
- // normalizedIndex = dot(pxDiff * channelWeights, normalizedWeightedAxis) / len(weightedEPDiff)
- // index = normalizedIndex * maxValue
- //
- // Equivalent to:
- // axis = channelWeights * maxValue * epDiff * channelWeights / lenSquared(epDiff * channelWeights)
- // index = dot(axis, pxDiff)
-
- for (int ep = 0; ep < 2; ep++)
- for (int ch = 0; ch < TVectorSize; ch++)
- m_endPoint[ep][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(interpolationEndPoints[ep][ch]);
-
- m_range = range;
- m_maxValue = static_cast<float>(range - 1);
-
- MFloat epDiffWeighted[TVectorSize];
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- m_origin[ch] = ParallelMath::ToFloat(colorSpaceEndpoints[0][ch]);
- MFloat opposingOriginCh = ParallelMath::ToFloat(colorSpaceEndpoints[1][ch]);
- epDiffWeighted[ch] = (opposingOriginCh - m_origin[ch]) * channelWeights[ch];
- }
-
- MFloat lenSquared = epDiffWeighted[0] * epDiffWeighted[0];
- for (int ch = 1; ch < TVectorSize; ch++)
- lenSquared = lenSquared + epDiffWeighted[ch] * epDiffWeighted[ch];
-
- ParallelMath::MakeSafeDenominator(lenSquared);
-
- MFloat maxValueDividedByLengthSquared = ParallelMath::MakeFloat(m_maxValue) / lenSquared;
-
- for (int ch = 0; ch < TVectorSize; ch++)
- m_axis[ch] = epDiffWeighted[ch] * channelWeights[ch] * maxValueDividedByLengthSquared;
- }
-
- template<bool TSigned>
- void Init(const float channelWeights[TVectorSize], const MUInt15 endPoints[2][TVectorSize], int range)
- {
- MAInt16 converted[2][TVectorSize];
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < TVectorSize; ch++)
- converted[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(endPoints[epi][ch]);
-
- Init<MUInt15, MUInt15>(channelWeights, endPoints, endPoints, range);
- }
-
- void ReconstructLDR_BC7(const MUInt15 &index, MUInt15* pixel, int numRealChannels)
- {
- MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
-
- for (int ch = 0; ch < numRealChannels; ch++)
- {
- MUInt15 ep0f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply((ParallelMath::MakeUInt15(64) - weight), ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[0][ch])));
- MUInt15 ep1f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(weight, ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[1][ch])));
- pixel[ch] = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ep0f + ep1f + ParallelMath::MakeUInt15(32), 6));
- }
- }
-
- void ReconstructLDRPrecise(const MUInt15 &index, MUInt15* pixel, int numRealChannels)
- {
- MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 64, 7));
-
- for (int ch = 0; ch < numRealChannels; ch++)
- {
- MUInt15 ep0f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply((ParallelMath::MakeUInt15(256) - weight), ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[0][ch])));
- MUInt15 ep1f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(weight, ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[1][ch])));
- pixel[ch] = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ep0f + ep1f + ParallelMath::MakeUInt15(128), 8));
- }
- }
-
- void ReconstructLDR_BC7(const MUInt15 &index, MUInt15* pixel)
- {
- ReconstructLDR_BC7(index, pixel, TVectorSize);
- }
-
- void ReconstructLDRPrecise(const MUInt15 &index, MUInt15* pixel)
- {
- ReconstructLDRPrecise(index, pixel, TVectorSize);
- }
-
- MUInt15 SelectIndexLDR(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope* rtn) const
- {
- MFloat dist = (pixel[0] - m_origin[0]) * m_axis[0];
- for (int ch = 1; ch < TVectorSize; ch++)
- dist = dist + (pixel[ch] - m_origin[ch]) * m_axis[ch];
-
- return ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(dist, 0.0f, m_maxValue), rtn);
- }
-
- protected:
- MAInt16 m_endPoint[2][TVectorSize];
-
- private:
- MFloat m_origin[TVectorSize];
- MFloat m_axis[TVectorSize];
- int m_range;
- float m_maxValue;
- bool m_isUniform;
- };
-
-
- template<int TVectorSize>
- class IndexSelectorHDR : public IndexSelector<TVectorSize>
- {
- public:
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt31 MUInt31;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::SInt32 MSInt32;
- typedef ParallelMath::Float MFloat;
-
- private:
-
- MUInt15 InvertSingle(const MUInt15& anIndex) const
- {
- MUInt15 inverted = m_maxValueMinusOne - anIndex;
- return ParallelMath::Select(m_isInverted, inverted, anIndex);
- }
-
- void ReconstructHDRSignedUninverted(const MUInt15 &index, MSInt16* pixel) const
- {
- MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MSInt16 ep0 = ParallelMath::LosslessCast<MSInt16>::Cast(this->m_endPoint[0][ch]);
- MSInt16 ep1 = ParallelMath::LosslessCast<MSInt16>::Cast(this->m_endPoint[1][ch]);
-
- MSInt32 pixel32 = ParallelMath::XMultiply((ParallelMath::MakeUInt15(64) - weight), ep0) + ParallelMath::XMultiply(weight, ep1);
-
- pixel32 = ParallelMath::RightShift(pixel32 + ParallelMath::MakeSInt32(32), 6);
-
- pixel[ch] = UnscaleHDRValueSigned(ParallelMath::ToSInt16(pixel32));
- }
- }
-
- void ReconstructHDRUnsignedUninverted(const MUInt15 &index, MSInt16* pixel) const
- {
- MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MUInt16 ep0 = ParallelMath::LosslessCast<MUInt16>::Cast(this->m_endPoint[0][ch]);
- MUInt16 ep1 = ParallelMath::LosslessCast<MUInt16>::Cast(this->m_endPoint[1][ch]);
-
- MUInt31 pixel31 = ParallelMath::XMultiply((ParallelMath::MakeUInt15(64) - weight), ep0) + ParallelMath::XMultiply(weight, ep1);
-
- pixel31 = ParallelMath::RightShift(pixel31 + ParallelMath::MakeUInt31(32), 6);
-
- pixel[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(UnscaleHDRValueUnsigned(ParallelMath::ToUInt16(pixel31)));
- }
- }
-
- MFloat ErrorForInterpolatorComponent(int index, int ch, const MFloat *pixel) const
- {
- MFloat diff = pixel[ch] - m_reconstructedInterpolators[index][ch];
- return diff * diff;
- }
-
- MFloat ErrorForInterpolator(int index, const MFloat *pixel) const
- {
- MFloat error = ErrorForInterpolatorComponent(index, 0, pixel);
- for (int ch = 1; ch < TVectorSize; ch++)
- error = error + ErrorForInterpolatorComponent(index, ch, pixel);
- return error;
- }
-
- public:
-
- void InitHDR(int range, bool isSigned, bool fastIndexing, const float *channelWeights)
- {
- assert(range <= 16);
-
- m_range = range;
-
- m_isInverted = ParallelMath::MakeBoolInt16(false);
- m_maxValueMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(range - 1));
-
- if (!fastIndexing)
- {
- for (int i = 0; i < range; i++)
- {
- MSInt16 recon2CL[TVectorSize];
-
- if (isSigned)
- ReconstructHDRSignedUninverted(ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), recon2CL);
- else
- ReconstructHDRUnsignedUninverted(ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), recon2CL);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- m_reconstructedInterpolators[i][ch] = ParallelMath::TwosCLHalfToFloat(recon2CL[ch]) * channelWeights[ch];
- }
- }
- }
-
- void ReconstructHDRSigned(const MUInt15 &index, MSInt16* pixel) const
- {
- ReconstructHDRSignedUninverted(InvertSingle(index), pixel);
- }
-
- void ReconstructHDRUnsigned(const MUInt15 &index, MSInt16* pixel) const
- {
- ReconstructHDRUnsignedUninverted(InvertSingle(index), pixel);
- }
-
- void ConditionalInvert(const ParallelMath::Int16CompFlag &invert)
- {
- m_isInverted = invert;
- }
-
- MUInt15 SelectIndexHDRSlow(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope*) const
- {
- MUInt15 index = ParallelMath::MakeUInt15(0);
-
- MFloat bestError = ErrorForInterpolator(0, pixel);
- for (int i = 1; i < m_range; i++)
- {
- MFloat error = ErrorForInterpolator(i, pixel);
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
- ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(errorBetter), ParallelMath::MakeUInt15(static_cast<uint16_t>(i)));
- bestError = ParallelMath::Min(bestError, error);
- }
-
- return InvertSingle(index);
- }
-
- MUInt15 SelectIndexHDRFast(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope* rtn) const
- {
- return InvertSingle(this->SelectIndexLDR(pixel, rtn));
- }
-
- private:
- MFloat m_reconstructedInterpolators[16][TVectorSize];
- ParallelMath::Int16CompFlag m_isInverted;
- MUInt15 m_maxValueMinusOne;
- int m_range;
- };
-
- // Solve for a, b where v = a*t + b
- // This allows endpoints to be mapped to where T=0 and T=1
- // Least squares from totals:
- // a = (tv - t*v/w)/(tt - t*t/w)
- // b = (v - a*t)/w
- template<int TVectorSize>
- class EndpointRefiner
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::AInt16 MAInt16;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::SInt32 MSInt32;
-
- MFloat m_tv[TVectorSize];
- MFloat m_v[TVectorSize];
- MFloat m_tt;
- MFloat m_t;
- MFloat m_w;
- int m_wu;
-
- float m_rcpMaxIndex;
- float m_channelWeights[TVectorSize];
- float m_rcpChannelWeights[TVectorSize];
-
- void Init(int indexRange, const float channelWeights[TVectorSize])
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- m_tv[ch] = ParallelMath::MakeFloatZero();
- m_v[ch] = ParallelMath::MakeFloatZero();
- }
- m_tt = ParallelMath::MakeFloatZero();
- m_t = ParallelMath::MakeFloatZero();
- m_w = ParallelMath::MakeFloatZero();
-
- m_rcpMaxIndex = 1.0f / static_cast<float>(indexRange - 1);
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- m_channelWeights[ch] = channelWeights[ch];
- m_rcpChannelWeights[ch] = 1.0f;
- if (m_channelWeights[ch] != 0.0f)
- m_rcpChannelWeights[ch] = 1.0f / channelWeights[ch];
- }
-
- m_wu = 0;
- }
-
- void ContributePW(const MFloat *pwFloatPixel, const MUInt15 &index, const MFloat &weight)
- {
- MFloat t = ParallelMath::ToFloat(index) * m_rcpMaxIndex;
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MFloat v = pwFloatPixel[ch] * weight;
-
- m_tv[ch] = m_tv[ch] + t * v;
- m_v[ch] = m_v[ch] + v;
- }
- m_tt = m_tt + weight * t * t;
- m_t = m_t + weight * t;
- m_w = m_w + weight;
- }
-
- void ContributeUnweightedPW(const MFloat *pwFloatPixel, const MUInt15 &index, int numRealChannels)
- {
- MFloat t = ParallelMath::ToFloat(index) * m_rcpMaxIndex;
-
- for (int ch = 0; ch < numRealChannels; ch++)
- {
- MFloat v = pwFloatPixel[ch];
-
- m_tv[ch] = m_tv[ch] + t * v;
- m_v[ch] = m_v[ch] + v;
- }
- m_tt = m_tt + t * t;
- m_t = m_t + t;
- m_wu++;
- }
-
- void ContributeUnweightedPW(const MFloat *floatPixel, const MUInt15 &index)
- {
- ContributeUnweightedPW(floatPixel, index, TVectorSize);
- }
-
- void GetRefinedEndpoints(MFloat endPoint[2][TVectorSize])
- {
- // a = (tv - t*v/w)/(tt - t*t/w)
- // b = (v - a*t)/w
- MFloat w = m_w + ParallelMath::MakeFloat(static_cast<float>(m_wu));
-
- ParallelMath::MakeSafeDenominator(w);
- MFloat wRcp = ParallelMath::Reciprocal(w);
-
- MFloat adenom = (m_tt * w - m_t * m_t) * wRcp;
-
- ParallelMath::FloatCompFlag adenomZero = ParallelMath::Equal(adenom, ParallelMath::MakeFloatZero());
- ParallelMath::ConditionalSet(adenom, adenomZero, ParallelMath::MakeFloat(1.0f));
-
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- /*
- if (adenom == 0.0)
- p1 = p2 = er.v / er.w;
- else
- {
- float4 a = (er.tv - er.t*er.v / er.w) / adenom;
- float4 b = (er.v - a * er.t) / er.w;
- p1 = b;
- p2 = a + b;
- }
- */
-
- MFloat a = (m_tv[ch] - m_t * m_v[ch] * wRcp) / adenom;
- MFloat b = (m_v[ch] - a * m_t) * wRcp;
-
- MFloat p1 = b;
- MFloat p2 = a + b;
-
- ParallelMath::ConditionalSet(p1, adenomZero, (m_v[ch] * wRcp));
- ParallelMath::ConditionalSet(p2, adenomZero, p1);
-
- // Unweight
- float inverseWeight = m_rcpChannelWeights[ch];
-
- endPoint[0][ch] = p1 * inverseWeight;
- endPoint[1][ch] = p2 * inverseWeight;
- }
- }
-
- void GetRefinedEndpointsLDR(MUInt15 endPoint[2][TVectorSize], int numRealChannels, const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- MFloat floatEndPoint[2][TVectorSize];
- GetRefinedEndpoints(floatEndPoint);
-
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < TVectorSize; ch++)
- endPoint[epi][ch] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(floatEndPoint[epi][ch], 0.0f, 255.0f), roundingMode);
- }
-
- void GetRefinedEndpointsLDR(MUInt15 endPoint[2][TVectorSize], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- GetRefinedEndpointsLDR(endPoint, TVectorSize, roundingMode);
- }
-
- void GetRefinedEndpointsHDR(MSInt16 endPoint[2][TVectorSize], bool isSigned, const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- MFloat floatEndPoint[2][TVectorSize];
- GetRefinedEndpoints(floatEndPoint);
-
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- {
- MFloat f = floatEndPoint[epi][ch];
- if (isSigned)
- endPoint[epi][ch] = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RoundAndConvertToS16(ParallelMath::Clamp(f, -31743.0f, 31743.0f), roundingMode));
- else
- endPoint[epi][ch] = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(f, 0.0f, 31743.0f), roundingMode));
- }
- }
- }
- };
-
- template<int TVectorSize>
- class AggregatedError
- {
- public:
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt31 MUInt31;
- typedef ParallelMath::Float MFloat;
-
- AggregatedError()
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- m_errorUnweighted[ch] = ParallelMath::MakeUInt31(0);
- }
-
- void Add(const MUInt16 &channelErrorUnweighted, int ch)
- {
- m_errorUnweighted[ch] = m_errorUnweighted[ch] + ParallelMath::ToUInt31(channelErrorUnweighted);
- }
-
- MFloat Finalize(uint32_t flags, const float channelWeightsSq[TVectorSize]) const
- {
- if (flags & cvtt::Flags::Uniform)
- {
- MUInt31 total = m_errorUnweighted[0];
- for (int ch = 1; ch < TVectorSize; ch++)
- total = total + m_errorUnweighted[ch];
- return ParallelMath::ToFloat(total);
- }
- else
- {
- MFloat total = ParallelMath::ToFloat(m_errorUnweighted[0]) * channelWeightsSq[0];
- for (int ch = 1; ch < TVectorSize; ch++)
- total = total + ParallelMath::ToFloat(m_errorUnweighted[ch]) * channelWeightsSq[ch];
- return total;
- }
- }
-
- private:
- MUInt31 m_errorUnweighted[TVectorSize];
- };
-
- class BCCommon
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::AInt16 MAInt16;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::SInt32 MSInt32;
-
- static int TweakRoundsForRange(int range)
- {
- if (range == 3)
- return 3;
- return 4;
- }
-
- template<int TVectorSize>
- static void ComputeErrorLDR(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], int numRealChannels, AggregatedError<TVectorSize> &aggError)
- {
- for (int ch = 0; ch < numRealChannels; ch++)
- aggError.Add(ParallelMath::SqDiffUInt8(reconstructed[ch], original[ch]), ch);
- }
-
- template<int TVectorSize>
- static void ComputeErrorLDR(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], AggregatedError<TVectorSize> &aggError)
- {
- ComputeErrorLDR<TVectorSize>(flags, reconstructed, original, TVectorSize, aggError);
- }
-
- template<int TVectorSize>
- static MFloat ComputeErrorLDRSimple(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], int numRealChannels, const float *channelWeightsSq)
- {
- AggregatedError<TVectorSize> aggError;
- ComputeErrorLDR<TVectorSize>(flags, reconstructed, original, numRealChannels, aggError);
- return aggError.Finalize(flags, channelWeightsSq);
- }
-
- template<int TVectorSize>
- static MFloat ComputeErrorHDRFast(uint32_t flags, const MSInt16 reconstructed[TVectorSize], const MSInt16 original[TVectorSize], const float channelWeightsSq[TVectorSize])
- {
- MFloat error = ParallelMath::MakeFloatZero();
- if (flags & Flags::Uniform)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- error = error + ParallelMath::SqDiffSInt16(reconstructed[ch], original[ch]);
- }
- else
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- error = error + ParallelMath::SqDiffSInt16(reconstructed[ch], original[ch]) * ParallelMath::MakeFloat(channelWeightsSq[ch]);
- }
-
- return error;
- }
-
- template<int TVectorSize>
- static MFloat ComputeErrorHDRSlow(uint32_t flags, const MSInt16 reconstructed[TVectorSize], const MSInt16 original[TVectorSize], const float channelWeightsSq[TVectorSize])
- {
- MFloat error = ParallelMath::MakeFloatZero();
- if (flags & Flags::Uniform)
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- error = error + ParallelMath::SqDiff2CL(reconstructed[ch], original[ch]);
- }
- else
- {
- for (int ch = 0; ch < TVectorSize; ch++)
- error = error + ParallelMath::SqDiff2CL(reconstructed[ch], original[ch]) * ParallelMath::MakeFloat(channelWeightsSq[ch]);
- }
-
- return error;
- }
-
- template<int TChannelCount>
- static void PreWeightPixelsLDR(MFloat preWeightedPixels[16][TChannelCount], const MUInt15 pixels[16][TChannelCount], const float channelWeights[TChannelCount])
- {
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < TChannelCount; ch++)
- preWeightedPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]) * channelWeights[ch];
- }
- }
-
- template<int TChannelCount>
- static void PreWeightPixelsHDR(MFloat preWeightedPixels[16][TChannelCount], const MSInt16 pixels[16][TChannelCount], const float channelWeights[TChannelCount])
- {
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < TChannelCount; ch++)
- preWeightedPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]) * channelWeights[ch];
- }
- }
- };
-
- class BC7Computer
- {
- public:
- static const int MaxTweakRounds = 4;
-
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::SInt32 MSInt32;
- typedef ParallelMath::Float MFloat;
-
- struct WorkInfo
- {
- MUInt15 m_mode;
- MFloat m_error;
- MUInt15 m_ep[3][2][4];
- MUInt15 m_indexes[16];
- MUInt15 m_indexes2[16];
-
- union
- {
- MUInt15 m_partition;
- struct IndexSelectorAndRotation
- {
- MUInt15 m_indexSelector;
- MUInt15 m_rotation;
- } m_isr;
- } m_u;
- };
-
- static void TweakAlpha(const MUInt15 original[2], int tweak, int range, MUInt15 result[2])
- {
- ParallelMath::RoundTowardNearestForScope roundingMode;
-
- float tf[2];
- ComputeTweakFactors(tweak, range, tf);
-
- MFloat base = ParallelMath::ToFloat(original[0]);
- MFloat offs = ParallelMath::ToFloat(original[1]) - base;
-
- result[0] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(base + offs * tf[0], 0.0f, 255.0f), &roundingMode);
- result[1] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(base + offs * tf[1], 0.0f, 255.0f), &roundingMode);
- }
-
- static void Quantize(MUInt15* color, int bits, int channels, const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- float maxColor = static_cast<float>((1 << bits) - 1);
-
- for (int i = 0; i < channels; i++)
- color[i] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(ParallelMath::ToFloat(color[i]) * ParallelMath::MakeFloat(1.0f / 255.0f) * maxColor, 0.f, 255.f), roundingMode);
- }
-
- static void QuantizeP(MUInt15* color, int bits, uint16_t p, int channels, const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- uint16_t pShift = static_cast<uint16_t>(1 << (7 - bits));
- MUInt15 pShiftV = ParallelMath::MakeUInt15(pShift);
-
- float maxColorF = static_cast<float>(255 - (1 << (7 - bits)));
-
- float maxQuantized = static_cast<float>((1 << bits) - 1);
-
- for (int ch = 0; ch < channels; ch++)
- {
- MUInt15 clr = color[ch];
- if (p)
- clr = ParallelMath::Max(clr, pShiftV) - pShiftV;
-
- MFloat rerangedColor = ParallelMath::ToFloat(clr) * maxQuantized / maxColorF;
-
- clr = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(rerangedColor, 0.0f, maxQuantized), roundingMode) << 1;
- if (p)
- clr = clr | ParallelMath::MakeUInt15(1);
-
- color[ch] = clr;
- }
- }
-
- static void Unquantize(MUInt15* color, int bits, int channels)
- {
- for (int ch = 0; ch < channels; ch++)
- {
- MUInt15 clr = color[ch];
- clr = clr << (8 - bits);
- color[ch] = clr | ParallelMath::RightShift(clr, bits);
- }
- }
-
- static void CompressEndpoints0(MUInt15 ep[2][4], uint16_t p[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- QuantizeP(ep[j], 4, p[j], 3, roundingMode);
- Unquantize(ep[j], 5, 3);
- ep[j][3] = ParallelMath::MakeUInt15(255);
- }
- }
-
- static void CompressEndpoints1(MUInt15 ep[2][4], uint16_t p, const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- QuantizeP(ep[j], 6, p, 3, roundingMode);
- Unquantize(ep[j], 7, 3);
- ep[j][3] = ParallelMath::MakeUInt15(255);
- }
- }
-
- static void CompressEndpoints2(MUInt15 ep[2][4], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- Quantize(ep[j], 5, 3, roundingMode);
- Unquantize(ep[j], 5, 3);
- ep[j][3] = ParallelMath::MakeUInt15(255);
- }
- }
-
- static void CompressEndpoints3(MUInt15 ep[2][4], uint16_t p[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- QuantizeP(ep[j], 7, p[j], 3, roundingMode);
- ep[j][3] = ParallelMath::MakeUInt15(255);
- }
- }
-
- static void CompressEndpoints4(MUInt15 epRGB[2][3], MUInt15 epA[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- Quantize(epRGB[j], 5, 3, roundingMode);
- Unquantize(epRGB[j], 5, 3);
-
- Quantize(epA + j, 6, 1, roundingMode);
- Unquantize(epA + j, 6, 1);
- }
- }
-
- static void CompressEndpoints5(MUInt15 epRGB[2][3], MUInt15 epA[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- Quantize(epRGB[j], 7, 3, roundingMode);
- Unquantize(epRGB[j], 7, 3);
- }
-
- // Alpha is full precision
- (void)epA;
- }
-
- static void CompressEndpoints6(MUInt15 ep[2][4], uint16_t p[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- QuantizeP(ep[j], 7, p[j], 4, roundingMode);
- }
-
- static void CompressEndpoints7(MUInt15 ep[2][4], uint16_t p[2], const ParallelMath::RoundTowardNearestForScope *roundingMode)
- {
- for (int j = 0; j < 2; j++)
- {
- QuantizeP(ep[j], 5, p[j], 4, roundingMode);
- Unquantize(ep[j], 6, 4);
- }
- }
-
- struct SinglePlaneTemporaries
- {
- UnfinishedEndpoints<3> unfinishedRGB[BC7Data::g_numShapesAll];
- UnfinishedEndpoints<4> unfinishedRGBA[BC7Data::g_numShapes12];
-
- MUInt15 fragmentBestIndexes[BC7Data::g_numFragments];
- MUInt15 shapeBestEP[BC7Data::g_maxFragmentsPerMode][2][4];
- MFloat shapeBestError[BC7Data::g_maxFragmentsPerMode];
- };
-
- static void TrySingleColorRGBAMultiTable(uint32_t flags, const MUInt15 pixels[16][4], const MFloat average[4], int numRealChannels, const uint8_t *fragmentStart, int shapeLength, const MFloat &staticAlphaError, const ParallelMath::Int16CompFlag punchThroughInvalid[4], MFloat& shapeBestError, MUInt15 shapeBestEP[2][4], MUInt15 *fragmentBestIndexes, const float *channelWeightsSq, const cvtt::Tables::BC7SC::Table*const* tables, int numTables, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- MFloat bestAverageError = ParallelMath::MakeFloat(FLT_MAX);
-
- MUInt15 intAverage[4];
- for (int ch = 0; ch < 4; ch++)
- intAverage[ch] = ParallelMath::RoundAndConvertToU15(average[ch], rtn);
-
- MUInt15 eps[2][4];
- MUInt15 reconstructed[4];
- MUInt15 index = ParallelMath::MakeUInt15(0);
-
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- eps[epi][ch] = ParallelMath::MakeUInt15(0);
- eps[epi][3] = ParallelMath::MakeUInt15(255);
- }
-
- for (int ch = 0; ch < 3; ch++)
- reconstructed[ch] = ParallelMath::MakeUInt15(0);
- reconstructed[3] = ParallelMath::MakeUInt15(255);
-
- // Depending on the target index and parity bits, there are multiple valid solid colors.
- // We want to find the one closest to the actual average.
- MFloat epsAverageDiff = ParallelMath::MakeFloat(FLT_MAX);
- for (int t = 0; t < numTables; t++)
- {
- const cvtt::Tables::BC7SC::Table& table = *(tables[t]);
-
- ParallelMath::Int16CompFlag pti = punchThroughInvalid[table.m_pBits];
-
- MUInt15 candidateReconstructed[4];
- MUInt15 candidateEPs[2][4];
-
- for (int i = 0; i < ParallelMath::ParallelSize; i++)
- {
- for (int ch = 0; ch < numRealChannels; ch++)
- {
- ParallelMath::ScalarUInt16 avgValue = ParallelMath::Extract(intAverage[ch], i);
- assert(avgValue >= 0 && avgValue <= 255);
-
- const cvtt::Tables::BC7SC::TableEntry &entry = table.m_entries[avgValue];
-
- ParallelMath::PutUInt15(candidateEPs[0][ch], i, entry.m_min);
- ParallelMath::PutUInt15(candidateEPs[1][ch], i, entry.m_max);
- ParallelMath::PutUInt15(candidateReconstructed[ch], i, entry.m_actualColor);
- }
- }
-
- MFloat avgError = ParallelMath::MakeFloatZero();
- for (int ch = 0; ch < numRealChannels; ch++)
- {
- MFloat delta = ParallelMath::ToFloat(candidateReconstructed[ch]) - average[ch];
- avgError = avgError + delta * delta * channelWeightsSq[ch];
- }
-
- ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(avgError, bestAverageError));
- better = ParallelMath::AndNot(pti, better); // Mask out punch-through invalidations
-
- if (ParallelMath::AnySet(better))
- {
- ParallelMath::ConditionalSet(bestAverageError, ParallelMath::Int16FlagToFloat(better), avgError);
-
- MUInt15 candidateIndex = ParallelMath::MakeUInt15(table.m_index);
-
- ParallelMath::ConditionalSet(index, better, candidateIndex);
-
- for (int ch = 0; ch < numRealChannels; ch++)
- ParallelMath::ConditionalSet(reconstructed[ch], better, candidateReconstructed[ch]);
-
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < numRealChannels; ch++)
- ParallelMath::ConditionalSet(eps[epi][ch], better, candidateEPs[epi][ch]);
- }
- }
-
- AggregatedError<4> aggError;
- for (int pxi = 0; pxi < shapeLength; pxi++)
- {
- int px = fragmentStart[pxi];
-
- BCCommon::ComputeErrorLDR<4>(flags, reconstructed, pixels[px], numRealChannels, aggError);
- }
-
- MFloat error = aggError.Finalize(flags, channelWeightsSq) + staticAlphaError;
-
- ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, shapeBestError));
- if (ParallelMath::AnySet(better))
- {
- shapeBestError = ParallelMath::Min(shapeBestError, error);
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < numRealChannels; ch++)
- ParallelMath::ConditionalSet(shapeBestEP[epi][ch], better, eps[epi][ch]);
- }
-
- for (int pxi = 0; pxi < shapeLength; pxi++)
- ParallelMath::ConditionalSet(fragmentBestIndexes[pxi], better, index);
- }
- }
-
-
- static void TrySinglePlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], int numTweakRounds, int numRefineRounds, WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- if (numRefineRounds < 1)
- numRefineRounds = 1;
-
- if (numTweakRounds < 1)
- numTweakRounds = 1;
- else if (numTweakRounds > MaxTweakRounds)
- numTweakRounds = MaxTweakRounds;
-
- float channelWeightsSq[4];
-
- for (int ch = 0; ch < 4; ch++)
- channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
-
- SinglePlaneTemporaries temps;
-
- MUInt15 maxAlpha = ParallelMath::MakeUInt15(0);
- MUInt15 minAlpha = ParallelMath::MakeUInt15(255);
- ParallelMath::Int16CompFlag isPunchThrough = ParallelMath::MakeBoolInt16(true);
- for (int px = 0; px < 16; px++)
- {
- MUInt15 a = pixels[px][3];
- maxAlpha = ParallelMath::Max(maxAlpha, a);
- minAlpha = ParallelMath::Min(minAlpha, a);
-
- isPunchThrough = (isPunchThrough & (ParallelMath::Equal(a, ParallelMath::MakeUInt15(0)) | ParallelMath::Equal(a, ParallelMath::MakeUInt15(255))));
- }
-
- ParallelMath::Int16CompFlag blockHasNonMaxAlpha = ParallelMath::Less(minAlpha, ParallelMath::MakeUInt15(255));
- ParallelMath::Int16CompFlag blockHasNonZeroAlpha = ParallelMath::Less(ParallelMath::MakeUInt15(0), maxAlpha);
-
- bool anyBlockHasAlpha = ParallelMath::AnySet(blockHasNonMaxAlpha);
-
- // Try RGB modes if any block has a min alpha 251 or higher
- bool allowRGBModes = ParallelMath::AnySet(ParallelMath::Less(ParallelMath::MakeUInt15(250), minAlpha));
-
- // Try mode 7 if any block has alpha.
- // Mode 7 is almost never selected for RGB blocks because mode 4 has very accurate 7.7.7.1 endpoints
- // and its parity bit doesn't affect alpha, meaning mode 7 can only be better in extremely specific
- // situations, and only by at most 1 unit of error per pixel.
- bool allowMode7 = anyBlockHasAlpha;
-
- MFloat preWeightedPixels[16][4];
-
- BCCommon::PreWeightPixelsLDR<4>(preWeightedPixels, pixels, channelWeights);
-
- const int *rgbInitialEPCollapseList = NULL;
-
- // Get initial RGB endpoints
- if (allowRGBModes)
- {
- const int *shapeList;
- int numShapesToEvaluate;
-
- if (flags & Flags::BC7_EnablePartitioning)
- {
- if (flags & Flags::BC7_Enable3Subsets)
- {
- shapeList = BC7Data::g_shapeListAll;
- rgbInitialEPCollapseList = BC7Data::g_shapeListAll;
- numShapesToEvaluate = BC7Data::g_numShapesAll;
- }
- else
- {
- shapeList = BC7Data::g_shapeList12;
- rgbInitialEPCollapseList = BC7Data::g_shapeList12Collapse;
- numShapesToEvaluate = BC7Data::g_numShapes12;
- }
- }
- else
- {
- shapeList = BC7Data::g_shapeList1;
- rgbInitialEPCollapseList = BC7Data::g_shapeList1Collapse;
- numShapesToEvaluate = BC7Data::g_numShapes1;
- }
-
- for (int shapeIter = 0; shapeIter < numShapesToEvaluate; shapeIter++)
- {
- int shape = shapeList[shapeIter];
-
- int shapeStart = BC7Data::g_shapeRanges[shape][0];
- int shapeSize = BC7Data::g_shapeRanges[shape][1];
-
- EndpointSelector<3, 8> epSelector;
-
- for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
- {
- for (int spx = 0; spx < shapeSize; spx++)
- {
- int px = BC7Data::g_fragments[shapeStart + spx];
- epSelector.ContributePass(preWeightedPixels[px], epPass, ParallelMath::MakeFloat(1.0f));
- }
- epSelector.FinishPass(epPass);
- }
- temps.unfinishedRGB[shapeIter] = epSelector.GetEndpoints(channelWeights);
- }
- }
-
- const int *rgbaInitialEPCollapseList = BC7Data::g_shapeList12Collapse;
-
- // Get initial RGBA endpoints
- {
- const int *shapeList = BC7Data::g_shapeList12;
- int numShapesToEvaluate = BC7Data::g_numShapes12;
-
- for (int shapeIter = 0; shapeIter < numShapesToEvaluate; shapeIter++)
- {
- int shape = shapeList[shapeIter];
-
- if (anyBlockHasAlpha || !allowRGBModes)
- {
- int shapeStart = BC7Data::g_shapeRanges[shape][0];
- int shapeSize = BC7Data::g_shapeRanges[shape][1];
-
- EndpointSelector<4, 8> epSelector;
-
- for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
- {
- for (int spx = 0; spx < shapeSize; spx++)
- {
- int px = BC7Data::g_fragments[shapeStart + spx];
- epSelector.ContributePass(preWeightedPixels[px], epPass, ParallelMath::MakeFloat(1.0f));
- }
- epSelector.FinishPass(epPass);
- }
- temps.unfinishedRGBA[shapeIter] = epSelector.GetEndpoints(channelWeights);
- }
- else
- {
- temps.unfinishedRGBA[shapeIter] = temps.unfinishedRGB[rgbInitialEPCollapseList[shape]].ExpandTo<4>(255);
- }
- }
- }
-
- for (uint16_t mode = 0; mode <= 7; mode++)
- {
- if (!(flags & Flags::BC7_EnablePartitioning) && BC7Data::g_modes[mode].m_numSubsets != 1)
- continue;
-
- if (!(flags & Flags::BC7_Enable3Subsets) && BC7Data::g_modes[mode].m_numSubsets == 3)
- continue;
-
- if (mode == 4 || mode == 5)
- continue;
-
- if (mode < 4 && !allowRGBModes)
- continue;
-
- if (mode == 7 && !allowMode7)
- continue;
-
- bool isRGB = (mode < 4);
-
- unsigned int numPartitions = 1 << BC7Data::g_modes[mode].m_partitionBits;
- int numSubsets = BC7Data::g_modes[mode].m_numSubsets;
- int indexPrec = BC7Data::g_modes[mode].m_indexBits;
-
- int parityBitMax = 1;
- if (BC7Data::g_modes[mode].m_pBitMode == BC7Data::PBitMode_PerEndpoint)
- parityBitMax = 4;
- else if (BC7Data::g_modes[mode].m_pBitMode == BC7Data::PBitMode_PerSubset)
- parityBitMax = 2;
-
- int numRealChannels = isRGB ? 3 : 4;
-
- int numShapes;
- const int *shapeList;
- const int *shapeCollapseList;
-
- if (numSubsets == 1)
- {
- numShapes = BC7Data::g_numShapes1;
- shapeList = BC7Data::g_shapeList1;
- shapeCollapseList = BC7Data::g_shapeList1Collapse;
- }
- else if (numSubsets == 2)
- {
- numShapes = BC7Data::g_numShapes2;
- shapeList = BC7Data::g_shapeList2;
- shapeCollapseList = BC7Data::g_shapeList2Collapse;
- }
- else
- {
- assert(numSubsets == 3);
- if (numPartitions == 16)
- {
- numShapes = BC7Data::g_numShapes3Short;
- shapeList = BC7Data::g_shapeList3Short;
- shapeCollapseList = BC7Data::g_shapeList3ShortCollapse;
- }
- else
- {
- assert(numPartitions == 64);
- numShapes = BC7Data::g_numShapes3;
- shapeList = BC7Data::g_shapeList3;
- shapeCollapseList = BC7Data::g_shapeList3Collapse;
- }
- }
-
- for (int slot = 0; slot < BC7Data::g_maxFragmentsPerMode; slot++)
- temps.shapeBestError[slot] = ParallelMath::MakeFloat(FLT_MAX);
-
- for (int shapeIter = 0; shapeIter < numShapes; shapeIter++)
- {
- int shape = shapeList[shapeIter];
- int shapeStart = BC7Data::g_shapeRanges[shape][0];
- int shapeLength = BC7Data::g_shapeRanges[shape][1];
- int shapeCollapsedEvalIndex = shapeCollapseList[shape];
-
- AggregatedError<1> alphaAggError;
- if (isRGB && anyBlockHasAlpha)
- {
- MUInt15 filledAlpha[1] = { ParallelMath::MakeUInt15(255) };
-
- for (int pxi = 0; pxi < shapeLength; pxi++)
- {
- int px = BC7Data::g_fragments[shapeStart + pxi];
- MUInt15 original[1] = { pixels[px][3] };
- BCCommon::ComputeErrorLDR<1>(flags, filledAlpha, original, alphaAggError);
- }
- }
-
- float alphaWeightsSq[1] = { channelWeightsSq[3] };
- MFloat staticAlphaError = alphaAggError.Finalize(flags, alphaWeightsSq);
-
- assert(shapeCollapsedEvalIndex >= 0);
-
- MUInt15 tweakBaseEP[MaxTweakRounds][2][4];
-
- for (int tweak = 0; tweak < numTweakRounds; tweak++)
- {
- if (isRGB)
- {
- temps.unfinishedRGB[rgbInitialEPCollapseList[shape]].FinishLDR(tweak, 1 << indexPrec, tweakBaseEP[tweak][0], tweakBaseEP[tweak][1]);
- tweakBaseEP[tweak][0][3] = tweakBaseEP[tweak][1][3] = ParallelMath::MakeUInt15(255);
- }
- else
- {
- temps.unfinishedRGBA[rgbaInitialEPCollapseList[shape]].FinishLDR(tweak, 1 << indexPrec, tweakBaseEP[tweak][0], tweakBaseEP[tweak][1]);
- }
- }
-
- ParallelMath::Int16CompFlag punchThroughInvalid[4];
- for (int pIter = 0; pIter < parityBitMax; pIter++)
- {
- punchThroughInvalid[pIter] = ParallelMath::MakeBoolInt16(false);
-
- if ((flags & Flags::BC7_RespectPunchThrough) && (mode == 6 || mode == 7))
- {
- // Modes 6 and 7 have parity bits that affect alpha
- if (pIter == 0)
- punchThroughInvalid[pIter] = (isPunchThrough & blockHasNonZeroAlpha);
- else if (pIter == parityBitMax - 1)
- punchThroughInvalid[pIter] = (isPunchThrough & blockHasNonMaxAlpha);
- else
- punchThroughInvalid[pIter] = isPunchThrough;
- }
- }
-
- for (int pIter = 0; pIter < parityBitMax; pIter++)
- {
- if (ParallelMath::AllSet(punchThroughInvalid[pIter]))
- continue;
-
- bool needPunchThroughCheck = ParallelMath::AnySet(punchThroughInvalid[pIter]);
-
- for (int tweak = 0; tweak < numTweakRounds; tweak++)
- {
- uint16_t p[2];
- p[0] = (pIter & 1);
- p[1] = ((pIter >> 1) & 1);
-
- MUInt15 ep[2][4];
-
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 4; ch++)
- ep[epi][ch] = tweakBaseEP[tweak][epi][ch];
-
- for (int refine = 0; refine < numRefineRounds; refine++)
- {
- switch (mode)
- {
- case 0:
- CompressEndpoints0(ep, p, rtn);
- break;
- case 1:
- CompressEndpoints1(ep, p[0], rtn);
- break;
- case 2:
- CompressEndpoints2(ep, rtn);
- break;
- case 3:
- CompressEndpoints3(ep, p, rtn);
- break;
- case 6:
- CompressEndpoints6(ep, p, rtn);
- break;
- case 7:
- CompressEndpoints7(ep, p, rtn);
- break;
- default:
- assert(false);
- break;
- };
-
- MFloat shapeError = ParallelMath::MakeFloatZero();
-
- IndexSelector<4> indexSelector;
- indexSelector.Init<false>(channelWeights, ep, 1 << indexPrec);
-
- EndpointRefiner<4> epRefiner;
- epRefiner.Init(1 << indexPrec, channelWeights);
-
- MUInt15 indexes[16];
-
- AggregatedError<4> aggError;
- for (int pxi = 0; pxi < shapeLength; pxi++)
- {
- int px = BC7Data::g_fragments[shapeStart + pxi];
-
- MUInt15 index;
- MUInt15 reconstructed[4];
-
- index = indexSelector.SelectIndexLDR(floatPixels[px], rtn);
- indexSelector.ReconstructLDR_BC7(index, reconstructed, numRealChannels);
-
- if (flags & cvtt::Flags::BC7_FastIndexing)
- BCCommon::ComputeErrorLDR<4>(flags, reconstructed, pixels[px], numRealChannels, aggError);
- else
- {
- MFloat error = BCCommon::ComputeErrorLDRSimple<4>(flags, reconstructed, pixels[px], numRealChannels, channelWeightsSq);
-
- MUInt15 altIndexes[2];
- altIndexes[0] = ParallelMath::Max(index, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
- altIndexes[1] = ParallelMath::Min(index + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << indexPrec) - 1)));
-
- for (int ii = 0; ii < 2; ii++)
- {
- indexSelector.ReconstructLDR_BC7(altIndexes[ii], reconstructed, numRealChannels);
-
- MFloat altError = BCCommon::ComputeErrorLDRSimple<4>(flags, reconstructed, pixels[px], numRealChannels, channelWeightsSq);
- ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altError, error));
- error = ParallelMath::Min(error, altError);
- ParallelMath::ConditionalSet(index, better, altIndexes[ii]);
- }
-
- shapeError = shapeError + error;
- }
-
- if (refine != numRefineRounds - 1)
- epRefiner.ContributeUnweightedPW(preWeightedPixels[px], index, numRealChannels);
-
- indexes[pxi] = index;
- }
-
- if (flags & cvtt::Flags::BC7_FastIndexing)
- shapeError = aggError.Finalize(flags, channelWeightsSq);
-
- if (isRGB)
- shapeError = shapeError + staticAlphaError;
-
- ParallelMath::FloatCompFlag shapeErrorBetter;
- ParallelMath::Int16CompFlag shapeErrorBetter16;
-
- shapeErrorBetter = ParallelMath::Less(shapeError, temps.shapeBestError[shapeCollapsedEvalIndex]);
- shapeErrorBetter16 = ParallelMath::FloatFlagToInt16(shapeErrorBetter);
-
- if (ParallelMath::AnySet(shapeErrorBetter16))
- {
- bool punchThroughOK = true;
- if (needPunchThroughCheck)
- {
- shapeErrorBetter16 = ParallelMath::AndNot(punchThroughInvalid[pIter], shapeErrorBetter16);
- shapeErrorBetter = ParallelMath::Int16FlagToFloat(shapeErrorBetter16);
-
- if (!ParallelMath::AnySet(shapeErrorBetter16))
- punchThroughOK = false;
- }
-
- if (punchThroughOK)
- {
- ParallelMath::ConditionalSet(temps.shapeBestError[shapeCollapsedEvalIndex], shapeErrorBetter, shapeError);
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < numRealChannels; ch++)
- ParallelMath::ConditionalSet(temps.shapeBestEP[shapeCollapsedEvalIndex][epi][ch], shapeErrorBetter16, ep[epi][ch]);
-
- for (int pxi = 0; pxi < shapeLength; pxi++)
- ParallelMath::ConditionalSet(temps.fragmentBestIndexes[shapeStart + pxi], shapeErrorBetter16, indexes[pxi]);
- }
- }
-
- if (refine != numRefineRounds - 1)
- epRefiner.GetRefinedEndpointsLDR(ep, numRealChannels, rtn);
- } // refine
- } // tweak
- } // p
-
- if (flags & cvtt::Flags::BC7_TrySingleColor)
- {
- MUInt15 total[4];
- for (int ch = 0; ch < 4; ch++)
- total[ch] = ParallelMath::MakeUInt15(0);
-
- for (int pxi = 0; pxi < shapeLength; pxi++)
- {
- int px = BC7Data::g_fragments[shapeStart + pxi];
- for (int ch = 0; ch < 4; ch++)
- total[ch] = total[ch] + pixels[pxi][ch];
- }
-
- MFloat rcpShapeLength = ParallelMath::MakeFloat(1.0f / static_cast<float>(shapeLength));
- MFloat average[4];
- for (int ch = 0; ch < 4; ch++)
- average[ch] = ParallelMath::ToFloat(total[ch]) * rcpShapeLength;
-
- const uint8_t *fragment = BC7Data::g_fragments + shapeStart;
- MFloat &shapeBestError = temps.shapeBestError[shapeCollapsedEvalIndex];
- MUInt15(&shapeBestEP)[2][4] = temps.shapeBestEP[shapeCollapsedEvalIndex];
- MUInt15 *fragmentBestIndexes = temps.fragmentBestIndexes + shapeStart;
-
- const cvtt::Tables::BC7SC::Table **scTables = NULL;
- int numSCTables = 0;
-
- switch (mode)
- {
- case 0:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode0_p00_i1,
- &cvtt::Tables::BC7SC::g_mode0_p00_i2,
- &cvtt::Tables::BC7SC::g_mode0_p00_i3,
- &cvtt::Tables::BC7SC::g_mode0_p01_i1,
- &cvtt::Tables::BC7SC::g_mode0_p01_i2,
- &cvtt::Tables::BC7SC::g_mode0_p01_i3,
- &cvtt::Tables::BC7SC::g_mode0_p10_i1,
- &cvtt::Tables::BC7SC::g_mode0_p10_i2,
- &cvtt::Tables::BC7SC::g_mode0_p10_i3,
- &cvtt::Tables::BC7SC::g_mode0_p11_i1,
- &cvtt::Tables::BC7SC::g_mode0_p11_i2,
- &cvtt::Tables::BC7SC::g_mode0_p11_i3,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- case 1:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode1_p0_i1,
- &cvtt::Tables::BC7SC::g_mode1_p0_i2,
- &cvtt::Tables::BC7SC::g_mode1_p0_i3,
- &cvtt::Tables::BC7SC::g_mode1_p1_i1,
- &cvtt::Tables::BC7SC::g_mode1_p1_i2,
- &cvtt::Tables::BC7SC::g_mode1_p1_i3,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- case 2:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode2,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- case 3:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode3_p0,
- &cvtt::Tables::BC7SC::g_mode3_p1,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- case 6:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode6_p0_i1,
- &cvtt::Tables::BC7SC::g_mode6_p0_i2,
- &cvtt::Tables::BC7SC::g_mode6_p0_i3,
- &cvtt::Tables::BC7SC::g_mode6_p0_i4,
- &cvtt::Tables::BC7SC::g_mode6_p0_i5,
- &cvtt::Tables::BC7SC::g_mode6_p0_i6,
- &cvtt::Tables::BC7SC::g_mode6_p0_i7,
- &cvtt::Tables::BC7SC::g_mode6_p1_i1,
- &cvtt::Tables::BC7SC::g_mode6_p1_i2,
- &cvtt::Tables::BC7SC::g_mode6_p1_i3,
- &cvtt::Tables::BC7SC::g_mode6_p1_i4,
- &cvtt::Tables::BC7SC::g_mode6_p1_i5,
- &cvtt::Tables::BC7SC::g_mode6_p1_i6,
- &cvtt::Tables::BC7SC::g_mode6_p1_i7,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- case 7:
- {
- const cvtt::Tables::BC7SC::Table *tables[] =
- {
- &cvtt::Tables::BC7SC::g_mode7_p00,
- &cvtt::Tables::BC7SC::g_mode7_p01,
- &cvtt::Tables::BC7SC::g_mode7_p10,
- &cvtt::Tables::BC7SC::g_mode7_p11,
- };
- scTables = tables;
- numSCTables = sizeof(tables) / sizeof(tables[0]);
- }
- break;
- default:
- assert(false);
- break;
- }
-
- TrySingleColorRGBAMultiTable(flags, pixels, average, numRealChannels, fragment, shapeLength, staticAlphaError, punchThroughInvalid, shapeBestError, shapeBestEP, fragmentBestIndexes, channelWeightsSq, scTables, numSCTables, rtn);
- }
- } // shapeIter
-
- for (uint16_t partition = 0; partition < numPartitions; partition++)
- {
- const int *partitionShapes;
- if (numSubsets == 1)
- partitionShapes = BC7Data::g_shapes1[partition];
- else if (numSubsets == 2)
- partitionShapes = BC7Data::g_shapes2[partition];
- else
- {
- assert(numSubsets == 3);
- partitionShapes = BC7Data::g_shapes3[partition];
- }
-
- MFloat totalError = ParallelMath::MakeFloatZero();
- for (int subset = 0; subset < numSubsets; subset++)
- totalError = totalError + temps.shapeBestError[shapeCollapseList[partitionShapes[subset]]];
-
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(totalError, work.m_error);
- ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
-
- if (ParallelMath::AnySet(errorBetter16))
- {
- for (int subset = 0; subset < numSubsets; subset++)
- {
- int shape = partitionShapes[subset];
- int shapeStart = BC7Data::g_shapeRanges[shape][0];
- int shapeLength = BC7Data::g_shapeRanges[shape][1];
- int shapeCollapsedEvalIndex = shapeCollapseList[shape];
-
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 4; ch++)
- ParallelMath::ConditionalSet(work.m_ep[subset][epi][ch], errorBetter16, temps.shapeBestEP[shapeCollapsedEvalIndex][epi][ch]);
-
- for (int pxi = 0; pxi < shapeLength; pxi++)
- {
- int px = BC7Data::g_fragments[shapeStart + pxi];
- ParallelMath::ConditionalSet(work.m_indexes[px], errorBetter16, temps.fragmentBestIndexes[shapeStart + pxi]);
- }
- }
-
- work.m_error = ParallelMath::Min(totalError, work.m_error);
- ParallelMath::ConditionalSet(work.m_mode, errorBetter16, ParallelMath::MakeUInt15(mode));
- ParallelMath::ConditionalSet(work.m_u.m_partition, errorBetter16, ParallelMath::MakeUInt15(partition));
- }
- }
- }
- }
-
- static void TryDualPlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], int numTweakRounds, int numRefineRounds, WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- // TODO: These error calculations are not optimal for weight-by-alpha, but this routine needs to be mostly rewritten for that.
- // The alpha/color solutions are co-dependent in that case, but a good way to solve it would probably be to
- // solve the alpha channel first, then solve the RGB channels, which in turn breaks down into two cases:
- // - Separate alpha channel, then weighted RGB
- // - Alpha+2 other channels, then the independent channel
-
- if (!(flags & Flags::BC7_EnableDualPlane))
- return;
-
- if (numRefineRounds < 1)
- numRefineRounds = 1;
-
- if (numTweakRounds < 1)
- numTweakRounds = 1;
- else if (numTweakRounds > MaxTweakRounds)
- numTweakRounds = MaxTweakRounds;
-
- float channelWeightsSq[4];
- for (int ch = 0; ch < 4; ch++)
- channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
-
- for (uint16_t mode = 4; mode <= 5; mode++)
- {
- for (uint16_t rotation = 0; rotation < 4; rotation++)
- {
- int alphaChannel = (rotation + 3) & 3;
- int redChannel = (rotation == 1) ? 3 : 0;
- int greenChannel = (rotation == 2) ? 3 : 1;
- int blueChannel = (rotation == 3) ? 3 : 2;
-
- MUInt15 rotatedRGB[16][3];
- MFloat floatRotatedRGB[16][3];
-
- for (int px = 0; px < 16; px++)
- {
- rotatedRGB[px][0] = pixels[px][redChannel];
- rotatedRGB[px][1] = pixels[px][greenChannel];
- rotatedRGB[px][2] = pixels[px][blueChannel];
-
- for (int ch = 0; ch < 3; ch++)
- floatRotatedRGB[px][ch] = ParallelMath::ToFloat(rotatedRGB[px][ch]);
- }
-
- uint16_t maxIndexSelector = (mode == 4) ? 2 : 1;
-
- float rotatedRGBWeights[3] = { channelWeights[redChannel], channelWeights[greenChannel], channelWeights[blueChannel] };
- float rotatedRGBWeightsSq[3] = { channelWeightsSq[redChannel], channelWeightsSq[greenChannel], channelWeightsSq[blueChannel] };
- float rotatedAlphaWeight[1] = { channelWeights[alphaChannel] };
- float rotatedAlphaWeightSq[1] = { channelWeightsSq[alphaChannel] };
-
- float uniformWeight[1] = { 1.0f }; // Since the alpha channel is independent, there's no need to bother with weights when doing refinement or selection, only error
-
- MFloat preWeightedRotatedRGB[16][3];
- BCCommon::PreWeightPixelsLDR<3>(preWeightedRotatedRGB, rotatedRGB, rotatedRGBWeights);
-
- for (uint16_t indexSelector = 0; indexSelector < maxIndexSelector; indexSelector++)
- {
- EndpointSelector<3, 8> rgbSelector;
-
- for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
- {
- for (int px = 0; px < 16; px++)
- rgbSelector.ContributePass(preWeightedRotatedRGB[px], epPass, ParallelMath::MakeFloat(1.0f));
-
- rgbSelector.FinishPass(epPass);
- }
-
- MUInt15 alphaRange[2];
-
- alphaRange[0] = alphaRange[1] = pixels[0][alphaChannel];
- for (int px = 1; px < 16; px++)
- {
- alphaRange[0] = ParallelMath::Min(pixels[px][alphaChannel], alphaRange[0]);
- alphaRange[1] = ParallelMath::Max(pixels[px][alphaChannel], alphaRange[1]);
- }
-
- int rgbPrec = 0;
- int alphaPrec = 0;
-
- if (mode == 4)
- {
- rgbPrec = indexSelector ? 3 : 2;
- alphaPrec = indexSelector ? 2 : 3;
- }
- else
- rgbPrec = alphaPrec = 2;
-
- UnfinishedEndpoints<3> unfinishedRGB = rgbSelector.GetEndpoints(rotatedRGBWeights);
-
- MFloat bestRGBError = ParallelMath::MakeFloat(FLT_MAX);
- MFloat bestAlphaError = ParallelMath::MakeFloat(FLT_MAX);
-
- MUInt15 bestRGBIndexes[16];
- MUInt15 bestAlphaIndexes[16];
- MUInt15 bestEP[2][4];
-
- for (int px = 0; px < 16; px++)
- bestRGBIndexes[px] = bestAlphaIndexes[px] = ParallelMath::MakeUInt15(0);
-
- for (int tweak = 0; tweak < numTweakRounds; tweak++)
- {
- MUInt15 rgbEP[2][3];
- MUInt15 alphaEP[2];
-
- unfinishedRGB.FinishLDR(tweak, 1 << rgbPrec, rgbEP[0], rgbEP[1]);
-
- TweakAlpha(alphaRange, tweak, 1 << alphaPrec, alphaEP);
-
- for (int refine = 0; refine < numRefineRounds; refine++)
- {
- if (mode == 4)
- CompressEndpoints4(rgbEP, alphaEP, rtn);
- else
- CompressEndpoints5(rgbEP, alphaEP, rtn);
-
-
- IndexSelector<1> alphaIndexSelector;
- IndexSelector<3> rgbIndexSelector;
-
- {
- MUInt15 alphaEPTemp[2][1] = { { alphaEP[0] },{ alphaEP[1] } };
- alphaIndexSelector.Init<false>(uniformWeight, alphaEPTemp, 1 << alphaPrec);
- }
- rgbIndexSelector.Init<false>(rotatedRGBWeights, rgbEP, 1 << rgbPrec);
-
- EndpointRefiner<3> rgbRefiner;
- EndpointRefiner<1> alphaRefiner;
-
- rgbRefiner.Init(1 << rgbPrec, rotatedRGBWeights);
- alphaRefiner.Init(1 << alphaPrec, uniformWeight);
-
- MFloat errorRGB = ParallelMath::MakeFloatZero();
- MFloat errorA = ParallelMath::MakeFloatZero();
-
- MUInt15 rgbIndexes[16];
- MUInt15 alphaIndexes[16];
-
- AggregatedError<3> rgbAggError;
- AggregatedError<1> alphaAggError;
-
- for (int px = 0; px < 16; px++)
- {
- MUInt15 rgbIndex = rgbIndexSelector.SelectIndexLDR(floatRotatedRGB[px], rtn);
- MUInt15 alphaIndex = alphaIndexSelector.SelectIndexLDR(floatPixels[px] + alphaChannel, rtn);
-
- MUInt15 reconstructedRGB[3];
- MUInt15 reconstructedAlpha[1];
-
- rgbIndexSelector.ReconstructLDR_BC7(rgbIndex, reconstructedRGB);
- alphaIndexSelector.ReconstructLDR_BC7(alphaIndex, reconstructedAlpha);
-
- if (flags & cvtt::Flags::BC7_FastIndexing)
- {
- BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], rgbAggError);
- BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, alphaAggError);
- }
- else
- {
- AggregatedError<3> baseRGBAggError;
- AggregatedError<1> baseAlphaAggError;
-
- BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], baseRGBAggError);
- BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, baseAlphaAggError);
-
- MFloat rgbError = baseRGBAggError.Finalize(flags, rotatedRGBWeightsSq);
- MFloat alphaError = baseAlphaAggError.Finalize(flags, rotatedAlphaWeightSq);
-
- MUInt15 altRGBIndexes[2];
- MUInt15 altAlphaIndexes[2];
-
- altRGBIndexes[0] = ParallelMath::Max(rgbIndex, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
- altRGBIndexes[1] = ParallelMath::Min(rgbIndex + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << rgbPrec) - 1)));
-
- altAlphaIndexes[0] = ParallelMath::Max(alphaIndex, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
- altAlphaIndexes[1] = ParallelMath::Min(alphaIndex + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << alphaPrec) - 1)));
-
- for (int ii = 0; ii < 2; ii++)
- {
- rgbIndexSelector.ReconstructLDR_BC7(altRGBIndexes[ii], reconstructedRGB);
- alphaIndexSelector.ReconstructLDR_BC7(altAlphaIndexes[ii], reconstructedAlpha);
-
- AggregatedError<3> altRGBAggError;
- AggregatedError<1> altAlphaAggError;
-
- BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], altRGBAggError);
- BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, altAlphaAggError);
-
- MFloat altRGBError = altRGBAggError.Finalize(flags, rotatedRGBWeightsSq);
- MFloat altAlphaError = altAlphaAggError.Finalize(flags, rotatedAlphaWeightSq);
-
- ParallelMath::Int16CompFlag rgbBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altRGBError, rgbError));
- ParallelMath::Int16CompFlag alphaBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altAlphaError, alphaError));
-
- rgbError = ParallelMath::Min(altRGBError, rgbError);
- alphaError = ParallelMath::Min(altAlphaError, alphaError);
-
- ParallelMath::ConditionalSet(rgbIndex, rgbBetter, altRGBIndexes[ii]);
- ParallelMath::ConditionalSet(alphaIndex, alphaBetter, altAlphaIndexes[ii]);
- }
-
- errorRGB = errorRGB + rgbError;
- errorA = errorA + alphaError;
- }
-
- if (refine != numRefineRounds - 1)
- {
- rgbRefiner.ContributeUnweightedPW(preWeightedRotatedRGB[px], rgbIndex);
- alphaRefiner.ContributeUnweightedPW(floatPixels[px] + alphaChannel, alphaIndex);
- }
-
- if (flags & Flags::BC7_FastIndexing)
- {
- errorRGB = rgbAggError.Finalize(flags, rotatedRGBWeightsSq);
- errorA = rgbAggError.Finalize(flags, rotatedAlphaWeightSq);
- }
-
- rgbIndexes[px] = rgbIndex;
- alphaIndexes[px] = alphaIndex;
- }
-
- ParallelMath::FloatCompFlag rgbBetter = ParallelMath::Less(errorRGB, bestRGBError);
- ParallelMath::FloatCompFlag alphaBetter = ParallelMath::Less(errorA, bestAlphaError);
-
- ParallelMath::Int16CompFlag rgbBetterInt16 = ParallelMath::FloatFlagToInt16(rgbBetter);
- ParallelMath::Int16CompFlag alphaBetterInt16 = ParallelMath::FloatFlagToInt16(alphaBetter);
-
- if (ParallelMath::AnySet(rgbBetterInt16))
- {
- bestRGBError = ParallelMath::Min(errorRGB, bestRGBError);
-
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestRGBIndexes[px], rgbBetterInt16, rgbIndexes[px]);
-
- for (int ep = 0; ep < 2; ep++)
- {
- for (int ch = 0; ch < 3; ch++)
- ParallelMath::ConditionalSet(bestEP[ep][ch], rgbBetterInt16, rgbEP[ep][ch]);
- }
- }
-
- if (ParallelMath::AnySet(alphaBetterInt16))
- {
- bestAlphaError = ParallelMath::Min(errorA, bestAlphaError);
-
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestAlphaIndexes[px], alphaBetterInt16, alphaIndexes[px]);
-
- for (int ep = 0; ep < 2; ep++)
- ParallelMath::ConditionalSet(bestEP[ep][3], alphaBetterInt16, alphaEP[ep]);
- }
-
- if (refine != numRefineRounds - 1)
- {
- rgbRefiner.GetRefinedEndpointsLDR(rgbEP, rtn);
-
- MUInt15 alphaEPTemp[2][1];
- alphaRefiner.GetRefinedEndpointsLDR(alphaEPTemp, rtn);
-
- for (int i = 0; i < 2; i++)
- alphaEP[i] = alphaEPTemp[i][0];
- }
- } // refine
- } // tweak
-
- MFloat combinedError = bestRGBError + bestAlphaError;
-
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(combinedError, work.m_error);
- ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
-
- work.m_error = ParallelMath::Min(combinedError, work.m_error);
-
- ParallelMath::ConditionalSet(work.m_mode, errorBetter16, ParallelMath::MakeUInt15(mode));
- ParallelMath::ConditionalSet(work.m_u.m_isr.m_rotation, errorBetter16, ParallelMath::MakeUInt15(rotation));
- ParallelMath::ConditionalSet(work.m_u.m_isr.m_indexSelector, errorBetter16, ParallelMath::MakeUInt15(indexSelector));
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::ConditionalSet(work.m_indexes[px], errorBetter16, indexSelector ? bestAlphaIndexes[px] : bestRGBIndexes[px]);
- ParallelMath::ConditionalSet(work.m_indexes2[px], errorBetter16, indexSelector ? bestRGBIndexes[px] : bestAlphaIndexes[px]);
- }
-
- for (int ep = 0; ep < 2; ep++)
- for (int ch = 0; ch < 4; ch++)
- ParallelMath::ConditionalSet(work.m_ep[0][ep][ch], errorBetter16, bestEP[ep][ch]);
- }
- }
- }
- }
-
- template<class T>
- static void Swap(T& a, T& b)
- {
- T temp = a;
- a = b;
- b = temp;
- }
-
- static void Pack(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, const float channelWeights[4], int numTweakRounds, int numRefineRounds)
- {
- MUInt15 pixels[16][4];
- MFloat floatPixels[16][4];
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 4; ch++)
- ParallelMath::ConvertLDRInputs(inputs, px, ch, pixels[px][ch]);
- }
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 4; ch++)
- floatPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]);
- }
-
- WorkInfo work;
- memset(&work, 0, sizeof(work));
-
- work.m_error = ParallelMath::MakeFloat(FLT_MAX);
-
- {
- ParallelMath::RoundTowardNearestForScope rtn;
- TrySinglePlane(flags, pixels, floatPixels, channelWeights, numTweakRounds, numRefineRounds, work, &rtn);
- TryDualPlane(flags, pixels, floatPixels, channelWeights, numTweakRounds, numRefineRounds, work, &rtn);
- }
-
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- PackingVector pv;
- pv.Init();
-
- ParallelMath::ScalarUInt16 mode = ParallelMath::Extract(work.m_mode, block);
- ParallelMath::ScalarUInt16 partition = ParallelMath::Extract(work.m_u.m_partition, block);
- ParallelMath::ScalarUInt16 indexSelector = ParallelMath::Extract(work.m_u.m_isr.m_indexSelector, block);
-
- const BC7Data::BC7ModeInfo& modeInfo = BC7Data::g_modes[mode];
-
- ParallelMath::ScalarUInt16 indexes[16];
- ParallelMath::ScalarUInt16 indexes2[16];
- ParallelMath::ScalarUInt16 endPoints[3][2][4];
-
- for (int i = 0; i < 16; i++)
- {
- indexes[i] = ParallelMath::Extract(work.m_indexes[i], block);
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- indexes2[i] = ParallelMath::Extract(work.m_indexes2[i], block);
- }
-
- for (int subset = 0; subset < 3; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- for (int ch = 0; ch < 4; ch++)
- endPoints[subset][ep][ch] = ParallelMath::Extract(work.m_ep[subset][ep][ch], block);
- }
- }
-
- int fixups[3] = { 0, 0, 0 };
-
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- {
- bool flipRGB = ((indexes[0] & (1 << (modeInfo.m_indexBits - 1))) != 0);
- bool flipAlpha = ((indexes2[0] & (1 << (modeInfo.m_alphaIndexBits - 1))) != 0);
-
- if (flipRGB)
- {
- uint16_t highIndex = (1 << modeInfo.m_indexBits) - 1;
- for (int px = 0; px < 16; px++)
- indexes[px] = highIndex - indexes[px];
- }
-
- if (flipAlpha)
- {
- uint16_t highIndex = (1 << modeInfo.m_alphaIndexBits) - 1;
- for (int px = 0; px < 16; px++)
- indexes2[px] = highIndex - indexes2[px];
- }
-
- if (indexSelector)
- Swap(flipRGB, flipAlpha);
-
- if (flipRGB)
- {
- for (int ch = 0; ch < 3; ch++)
- Swap(endPoints[0][0][ch], endPoints[0][1][ch]);
- }
- if (flipAlpha)
- Swap(endPoints[0][0][3], endPoints[0][1][3]);
-
- }
- else
- {
- if (modeInfo.m_numSubsets == 2)
- fixups[1] = BC7Data::g_fixupIndexes2[partition];
- else if (modeInfo.m_numSubsets == 3)
- {
- fixups[1] = BC7Data::g_fixupIndexes3[partition][0];
- fixups[2] = BC7Data::g_fixupIndexes3[partition][1];
- }
-
- bool flip[3] = { false, false, false };
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- flip[subset] = ((indexes[fixups[subset]] & (1 << (modeInfo.m_indexBits - 1))) != 0);
-
- if (flip[0] || flip[1] || flip[2])
- {
- uint16_t highIndex = (1 << modeInfo.m_indexBits) - 1;
- for (int px = 0; px < 16; px++)
- {
- int subset = 0;
- if (modeInfo.m_numSubsets == 2)
- subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
- else if (modeInfo.m_numSubsets == 3)
- subset = (BC7Data::g_partitionMap2[partition] >> (px * 2)) & 3;
-
- if (flip[subset])
- indexes[px] = highIndex - indexes[px];
- }
-
- int maxCH = (modeInfo.m_alphaMode == BC7Data::AlphaMode_Combined) ? 4 : 3;
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- if (flip[subset])
- for (int ch = 0; ch < maxCH; ch++)
- Swap(endPoints[subset][0][ch], endPoints[subset][1][ch]);
- }
- }
- }
-
- pv.Pack(static_cast<uint8_t>(1 << mode), mode + 1);
-
- if (modeInfo.m_partitionBits)
- pv.Pack(partition, modeInfo.m_partitionBits);
-
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- {
- ParallelMath::ScalarUInt16 rotation = ParallelMath::Extract(work.m_u.m_isr.m_rotation, block);
- pv.Pack(rotation, 2);
- }
-
- if (modeInfo.m_hasIndexSelector)
- pv.Pack(indexSelector, 1);
-
- // Encode RGB
- for (int ch = 0; ch < 3; ch++)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][ch];
- epPart >>= (8 - modeInfo.m_rgbBits);
-
- pv.Pack(epPart, modeInfo.m_rgbBits);
- }
- }
- }
-
- // Encode alpha
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][3];
- epPart >>= (8 - modeInfo.m_alphaBits);
-
- pv.Pack(epPart, modeInfo.m_alphaBits);
- }
- }
- }
-
- // Encode parity bits
- if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerSubset)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- ParallelMath::ScalarUInt16 epPart = endPoints[subset][0][0];
- epPart >>= (7 - modeInfo.m_rgbBits);
- epPart &= 1;
-
- pv.Pack(epPart, 1);
- }
- }
- else if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerEndpoint)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][0];
- epPart >>= (7 - modeInfo.m_rgbBits);
- epPart &= 1;
-
- pv.Pack(epPart, 1);
- }
- }
- }
-
- // Encode indexes
- for (int px = 0; px < 16; px++)
- {
- int bits = modeInfo.m_indexBits;
- if ((px == 0) || (px == fixups[1]) || (px == fixups[2]))
- bits--;
-
- pv.Pack(indexes[px], bits);
- }
-
- // Encode secondary indexes
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- {
- for (int px = 0; px < 16; px++)
- {
- int bits = modeInfo.m_alphaIndexBits;
- if (px == 0)
- bits--;
-
- pv.Pack(indexes2[px], bits);
- }
- }
-
- pv.Flush(packedBlocks);
-
- packedBlocks += 16;
- }
- }
-
- static void UnpackOne(PixelBlockU8 &output, const uint8_t* packedBlock)
- {
- UnpackingVector pv;
- pv.Init(packedBlock);
-
- int mode = 8;
- for (int i = 0; i < 8; i++)
- {
- if (pv.Unpack(1) == 1)
- {
- mode = i;
- break;
- }
- }
-
- if (mode > 7)
- {
- for (int px = 0; px < 16; px++)
- for (int ch = 0; ch < 4; ch++)
- output.m_pixels[px][ch] = 0;
-
- return;
- }
-
- const BC7Data::BC7ModeInfo &modeInfo = BC7Data::g_modes[mode];
-
- int partition = 0;
- if (modeInfo.m_partitionBits)
- partition = pv.Unpack(modeInfo.m_partitionBits);
-
- int rotation = 0;
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- rotation = pv.Unpack(2);
-
- int indexSelector = 0;
- if (modeInfo.m_hasIndexSelector)
- indexSelector = pv.Unpack(1);
-
- // Resolve fixups
- int fixups[3] = { 0, 0, 0 };
-
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_Separate)
- {
- if (modeInfo.m_numSubsets == 2)
- fixups[1] = BC7Data::g_fixupIndexes2[partition];
- else if (modeInfo.m_numSubsets == 3)
- {
- fixups[1] = BC7Data::g_fixupIndexes3[partition][0];
- fixups[2] = BC7Data::g_fixupIndexes3[partition][1];
- }
- }
-
- int endPoints[3][2][4];
-
- // Decode RGB
- for (int ch = 0; ch < 3; ch++)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- endPoints[subset][ep][ch] = (pv.Unpack(modeInfo.m_rgbBits) << (8 - modeInfo.m_rgbBits));
- }
- }
-
- // Decode alpha
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- endPoints[subset][ep][3] = (pv.Unpack(modeInfo.m_alphaBits) << (8 - modeInfo.m_alphaBits));
- }
- }
- else
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- endPoints[subset][ep][3] = 255;
- }
- }
-
- int parityBits = 0;
-
- // Decode parity bits
- if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerSubset)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- int p = pv.Unpack(1);
-
- for (int ep = 0; ep < 2; ep++)
- {
- for (int ch = 0; ch < 3; ch++)
- endPoints[subset][ep][ch] |= p << (7 - modeInfo.m_rgbBits);
-
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- endPoints[subset][ep][3] |= p << (7 - modeInfo.m_alphaBits);
- }
- }
-
- parityBits = 1;
- }
- else if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerEndpoint)
- {
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- int p = pv.Unpack(1);
-
- for (int ch = 0; ch < 3; ch++)
- endPoints[subset][ep][ch] |= p << (7 - modeInfo.m_rgbBits);
-
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- endPoints[subset][ep][3] |= p << (7 - modeInfo.m_alphaBits);
- }
- }
-
- parityBits = 1;
- }
-
- // Fill endpoint bits
- for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
- {
- for (int ep = 0; ep < 2; ep++)
- {
- for (int ch = 0; ch < 3; ch++)
- endPoints[subset][ep][ch] |= (endPoints[subset][ep][ch] >> (modeInfo.m_rgbBits + parityBits));
-
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- endPoints[subset][ep][3] |= (endPoints[subset][ep][3] >> (modeInfo.m_alphaBits + parityBits));
- }
- }
-
- int indexes[16];
- int indexes2[16];
-
- // Decode indexes
- for (int px = 0; px < 16; px++)
- {
- int bits = modeInfo.m_indexBits;
- if ((px == 0) || (px == fixups[1]) || (px == fixups[2]))
- bits--;
-
- indexes[px] = pv.Unpack(bits);
- }
-
- // Decode secondary indexes
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- {
- for (int px = 0; px < 16; px++)
- {
- int bits = modeInfo.m_alphaIndexBits;
- if (px == 0)
- bits--;
-
- indexes2[px] = pv.Unpack(bits);
- }
- }
- else
- {
- for (int px = 0; px < 16; px++)
- indexes2[px] = 0;
- }
-
- const int *alphaWeights = BC7Data::g_weightTables[modeInfo.m_alphaIndexBits];
- const int *rgbWeights = BC7Data::g_weightTables[modeInfo.m_indexBits];
-
- // Decode each pixel
- for (int px = 0; px < 16; px++)
- {
- int rgbWeight = 0;
- int alphaWeight = 0;
-
- int rgbIndex = indexes[px];
-
- rgbWeight = rgbWeights[indexes[px]];
-
- if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Combined)
- alphaWeight = rgbWeight;
- else if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
- alphaWeight = alphaWeights[indexes2[px]];
-
- if (indexSelector == 1)
- {
- int temp = rgbWeight;
- rgbWeight = alphaWeight;
- alphaWeight = temp;
- }
-
- int pixel[4] = { 0, 0, 0, 255 };
-
- int subset = 0;
-
- if (modeInfo.m_numSubsets == 2)
- subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
- else if (modeInfo.m_numSubsets == 3)
- subset = (BC7Data::g_partitionMap2[partition] >> (px * 2)) & 3;
-
- for (int ch = 0; ch < 3; ch++)
- pixel[ch] = ((64 - rgbWeight) * endPoints[subset][0][ch] + rgbWeight * endPoints[subset][1][ch] + 32) >> 6;
-
- if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
- pixel[3] = ((64 - alphaWeight) * endPoints[subset][0][3] + alphaWeight * endPoints[subset][1][3] + 32) >> 6;
-
- if (rotation != 0)
- {
- int ch = rotation - 1;
- int temp = pixel[ch];
- pixel[ch] = pixel[3];
- pixel[3] = temp;
- }
-
- for (int ch = 0; ch < 4; ch++)
- output.m_pixels[px][ch] = static_cast<uint8_t>(pixel[ch]);
- }
- }
- };
-
- class BC6HComputer
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::AInt16 MAInt16;
- typedef ParallelMath::SInt32 MSInt32;
- typedef ParallelMath::UInt31 MUInt31;
-
- static const int MaxTweakRounds = 4;
- static const int MaxRefineRounds = 3;
-
- static MSInt16 QuantizeSingleEndpointElementSigned(const MSInt16 &elem2CL, int precision, const ParallelMath::RoundUpForScope* ru)
- {
- assert(ParallelMath::AllSet(ParallelMath::Less(elem2CL, ParallelMath::MakeSInt16(31744))));
- assert(ParallelMath::AllSet(ParallelMath::Less(ParallelMath::MakeSInt16(-31744), elem2CL)));
-
- // Expand to full range
- ParallelMath::Int16CompFlag isNegative = ParallelMath::Less(elem2CL, ParallelMath::MakeSInt16(0));
- MUInt15 absElem = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Select(isNegative, ParallelMath::MakeSInt16(0) - elem2CL, elem2CL));
-
- absElem = ParallelMath::RightShift(ParallelMath::RoundAndConvertToU15(ParallelMath::ToFloat(absElem) * 32.0f / 31.0f, ru), 16 - precision);
-
- MSInt16 absElemS16 = ParallelMath::LosslessCast<MSInt16>::Cast(absElem);
-
- return ParallelMath::Select(isNegative, ParallelMath::MakeSInt16(0) - absElemS16, absElemS16);
- }
-
- static MUInt15 QuantizeSingleEndpointElementUnsigned(const MUInt15 &elem, int precision, const ParallelMath::RoundUpForScope* ru)
- {
- MUInt16 expandedElem = ParallelMath::RoundAndConvertToU16(ParallelMath::Min(ParallelMath::ToFloat(elem) * 64.0f / 31.0f, ParallelMath::MakeFloat(65535.0f)), ru);
- return ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(expandedElem, 16 - precision));
- }
-
- static void UnquantizeSingleEndpointElementSigned(const MSInt16 &comp, int precision, MSInt16 &outUnquantized, MSInt16 &outUnquantizedFinished2CL)
- {
- MSInt16 zero = ParallelMath::MakeSInt16(0);
-
- ParallelMath::Int16CompFlag negative = ParallelMath::Less(comp, zero);
- MUInt15 absComp = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Select(negative, MSInt16(zero - comp), comp));
-
- MSInt16 unq;
- MUInt15 absUnq;
-
- if (precision >= 16)
- {
- unq = comp;
- absUnq = absComp;
- }
- else
- {
- MSInt16 maxCompMinusOne = ParallelMath::MakeSInt16(static_cast<int16_t>((1 << (precision - 1)) - 2));
- ParallelMath::Int16CompFlag isZero = ParallelMath::Equal(comp, zero);
- ParallelMath::Int16CompFlag isMax = ParallelMath::Less(maxCompMinusOne, comp);
-
- absUnq = (absComp << (16 - precision)) + ParallelMath::MakeUInt15(static_cast<uint16_t>(0x4000 >> (precision - 1)));
- ParallelMath::ConditionalSet(absUnq, isZero, ParallelMath::MakeUInt15(0));
- ParallelMath::ConditionalSet(absUnq, isMax, ParallelMath::MakeUInt15(0x7fff));
-
- unq = ParallelMath::ConditionalNegate(negative, ParallelMath::LosslessCast<MSInt16>::Cast(absUnq));
- }
-
- outUnquantized = unq;
-
- MUInt15 funq = ParallelMath::ToUInt15(ParallelMath::RightShift(ParallelMath::XMultiply(absUnq, ParallelMath::MakeUInt15(31)), 5));
-
- outUnquantizedFinished2CL = ParallelMath::ConditionalNegate(negative, ParallelMath::LosslessCast<MSInt16>::Cast(funq));
- }
-
- static void UnquantizeSingleEndpointElementUnsigned(const MUInt15 &comp, int precision, MUInt16 &outUnquantized, MUInt16 &outUnquantizedFinished)
- {
- MUInt16 unq = ParallelMath::LosslessCast<MUInt16>::Cast(comp);
- if (precision < 15)
- {
- MUInt15 zero = ParallelMath::MakeUInt15(0);
- MUInt15 maxCompMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << precision) - 2));
-
- ParallelMath::Int16CompFlag isZero = ParallelMath::Equal(comp, zero);
- ParallelMath::Int16CompFlag isMax = ParallelMath::Less(maxCompMinusOne, comp);
-
- unq = (ParallelMath::LosslessCast<MUInt16>::Cast(comp) << (16 - precision)) + ParallelMath::MakeUInt16(static_cast<uint16_t>(0x8000 >> precision));
-
- ParallelMath::ConditionalSet(unq, isZero, ParallelMath::MakeUInt16(0));
- ParallelMath::ConditionalSet(unq, isMax, ParallelMath::MakeUInt16(0xffff));
- }
-
- outUnquantized = unq;
- outUnquantizedFinished = ParallelMath::ToUInt16(ParallelMath::RightShift(ParallelMath::XMultiply(unq, ParallelMath::MakeUInt15(31)), 6));
- }
-
- static void QuantizeEndpointsSigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- MSInt16 unquantizedEP[2][3];
- MSInt16 finishedUnquantizedEP[2][3];
-
- {
- ParallelMath::RoundUpForScope ru;
-
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- MSInt16 qee = QuantizeSingleEndpointElementSigned(endPoints[epi][ch], precision, &ru);
- UnquantizeSingleEndpointElementSigned(qee, precision, unquantizedEP[epi][ch], finishedUnquantizedEP[epi][ch]);
- quantizedEndPoints[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(qee);
- }
- }
- }
-
- indexSelector.Init(channelWeights, unquantizedEP, finishedUnquantizedEP, indexRange);
- indexSelector.InitHDR(indexRange, true, fastIndexing, channelWeights);
-
- MUInt15 halfRangeMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange / 2) - 1);
-
- MUInt15 index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixelsColorSpace[fixupIndex], rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[fixupIndex], rtn);
-
- ParallelMath::Int16CompFlag invert = ParallelMath::Less(halfRangeMinusOne, index);
-
- if (ParallelMath::AnySet(invert))
- {
- ParallelMath::ConditionalSet(index, invert, MUInt15(ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange - 1)) - index));
-
- indexSelector.ConditionalInvert(invert);
-
- for (int ch = 0; ch < 3; ch++)
- {
- MAInt16 firstEP = quantizedEndPoints[0][ch];
- MAInt16 secondEP = quantizedEndPoints[1][ch];
-
- quantizedEndPoints[0][ch] = ParallelMath::Select(invert, secondEP, firstEP);
- quantizedEndPoints[1][ch] = ParallelMath::Select(invert, firstEP, secondEP);
- }
- }
-
- indexes[fixupIndex] = index;
- }
-
- static void QuantizeEndpointsUnsigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- MUInt16 unquantizedEP[2][3];
- MUInt16 finishedUnquantizedEP[2][3];
-
- {
- ParallelMath::RoundUpForScope ru;
-
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- MUInt15 qee = QuantizeSingleEndpointElementUnsigned(ParallelMath::LosslessCast<MUInt15>::Cast(endPoints[epi][ch]), precision, &ru);
- UnquantizeSingleEndpointElementUnsigned(qee, precision, unquantizedEP[epi][ch], finishedUnquantizedEP[epi][ch]);
- quantizedEndPoints[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(qee);
- }
- }
- }
-
- indexSelector.Init(channelWeights, unquantizedEP, finishedUnquantizedEP, indexRange);
- indexSelector.InitHDR(indexRange, false, fastIndexing, channelWeights);
-
- MUInt15 halfRangeMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange / 2) - 1);
-
- MUInt15 index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixelsColorSpace[fixupIndex], rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[fixupIndex], rtn);
-
- ParallelMath::Int16CompFlag invert = ParallelMath::Less(halfRangeMinusOne, index);
-
- if (ParallelMath::AnySet(invert))
- {
- ParallelMath::ConditionalSet(index, invert, MUInt15(ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange - 1)) - index));
-
- indexSelector.ConditionalInvert(invert);
-
- for (int ch = 0; ch < 3; ch++)
- {
- MAInt16 firstEP = quantizedEndPoints[0][ch];
- MAInt16 secondEP = quantizedEndPoints[1][ch];
-
- quantizedEndPoints[0][ch] = ParallelMath::Select(invert, secondEP, firstEP);
- quantizedEndPoints[1][ch] = ParallelMath::Select(invert, firstEP, secondEP);
- }
- }
-
- indexes[fixupIndex] = index;
- }
-
- static void EvaluatePartitionedLegality(const MAInt16 ep0[2][3], const MAInt16 ep1[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][2][3], ParallelMath::Int16CompFlag& outIsLegal)
- {
- ParallelMath::Int16CompFlag allLegal = ParallelMath::MakeBoolInt16(true);
-
- MAInt16 aSignificantMask = ParallelMath::MakeAInt16(static_cast<int16_t>((1 << aPrec) - 1));
-
- for (int ch = 0; ch < 3; ch++)
- {
- outEncodedEPs[0][0][ch] = ep0[0][ch];
- outEncodedEPs[0][1][ch] = ep0[1][ch];
- outEncodedEPs[1][0][ch] = ep1[0][ch];
- outEncodedEPs[1][1][ch] = ep1[1][ch];
-
- if (isTransformed)
- {
- for (int subset = 0; subset < 2; subset++)
- {
- for (int epi = 0; epi < 2; epi++)
- {
- if (epi == 0 && subset == 0)
- continue;
-
- MAInt16 bReduced = (outEncodedEPs[subset][epi][ch] & aSignificantMask);
-
- MSInt16 delta = ParallelMath::TruncateToPrecisionSigned(ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::AbstractSubtract(outEncodedEPs[subset][epi][ch], outEncodedEPs[0][0][ch])), bPrec[ch]);
-
- outEncodedEPs[subset][epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(delta);
-
- MAInt16 reconstructed = (ParallelMath::AbstractAdd(outEncodedEPs[subset][epi][ch], outEncodedEPs[0][0][ch]) & aSignificantMask);
- allLegal = allLegal & ParallelMath::Equal(reconstructed, bReduced);
- }
- }
- }
-
- if (!ParallelMath::AnySet(allLegal))
- break;
- }
-
- outIsLegal = allLegal;
- }
-
- static void EvaluateSingleLegality(const MAInt16 ep[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][3], ParallelMath::Int16CompFlag& outIsLegal)
- {
- ParallelMath::Int16CompFlag allLegal = ParallelMath::MakeBoolInt16(true);
-
- MAInt16 aSignificantMask = ParallelMath::MakeAInt16(static_cast<int16_t>((1 << aPrec) - 1));
-
- for (int ch = 0; ch < 3; ch++)
- {
- outEncodedEPs[0][ch] = ep[0][ch];
- outEncodedEPs[1][ch] = ep[1][ch];
-
- if (isTransformed)
- {
- MAInt16 bReduced = (outEncodedEPs[1][ch] & aSignificantMask);
-
- MSInt16 delta = ParallelMath::TruncateToPrecisionSigned(ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::AbstractSubtract(outEncodedEPs[1][ch], outEncodedEPs[0][ch])), bPrec[ch]);
-
- outEncodedEPs[1][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(delta);
-
- MAInt16 reconstructed = (ParallelMath::AbstractAdd(outEncodedEPs[1][ch], outEncodedEPs[0][ch]) & aSignificantMask);
- allLegal = allLegal & ParallelMath::Equal(reconstructed, bReduced);
- }
- }
-
- outIsLegal = allLegal;
- }
-
- static void Pack(uint32_t flags, const PixelBlockF16* inputs, uint8_t* packedBlocks, const float channelWeights[4], bool isSigned, int numTweakRounds, int numRefineRounds)
- {
- if (numTweakRounds < 1)
- numTweakRounds = 1;
- else if (numTweakRounds > MaxTweakRounds)
- numTweakRounds = MaxTweakRounds;
-
- if (numRefineRounds < 1)
- numRefineRounds = 1;
- else if (numRefineRounds > MaxRefineRounds)
- numRefineRounds = MaxRefineRounds;
-
- bool fastIndexing = ((flags & cvtt::Flags::BC6H_FastIndexing) != 0);
- float channelWeightsSq[3];
-
- ParallelMath::RoundTowardNearestForScope rtn;
-
- MSInt16 pixels[16][3];
- MFloat floatPixels2CL[16][3];
- MFloat floatPixelsLinearWeighted[16][3];
-
- MSInt16 low15Bits = ParallelMath::MakeSInt16(32767);
-
- for (int ch = 0; ch < 3; ch++)
- channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- MSInt16 pixelValue;
- ParallelMath::ConvertHDRInputs(inputs, px, ch, pixelValue);
-
- // Convert from sign+magnitude to 2CL
- if (isSigned)
- {
- ParallelMath::Int16CompFlag negative = ParallelMath::Less(pixelValue, ParallelMath::MakeSInt16(0));
- MSInt16 magnitude = (pixelValue & low15Bits);
- ParallelMath::ConditionalSet(pixelValue, negative, ParallelMath::MakeSInt16(0) - magnitude);
- pixelValue = ParallelMath::Max(pixelValue, ParallelMath::MakeSInt16(-31743));
- }
- else
- pixelValue = ParallelMath::Max(pixelValue, ParallelMath::MakeSInt16(0));
-
- pixelValue = ParallelMath::Min(pixelValue, ParallelMath::MakeSInt16(31743));
-
- pixels[px][ch] = pixelValue;
- floatPixels2CL[px][ch] = ParallelMath::ToFloat(pixelValue);
- floatPixelsLinearWeighted[px][ch] = ParallelMath::TwosCLHalfToFloat(pixelValue) * channelWeights[ch];
- }
- }
-
- MFloat preWeightedPixels[16][3];
-
- BCCommon::PreWeightPixelsHDR<3>(preWeightedPixels, pixels, channelWeights);
-
- MAInt16 bestEndPoints[2][2][3];
- MUInt15 bestIndexes[16];
- MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
- MUInt15 bestMode = ParallelMath::MakeUInt15(0);
- MUInt15 bestPartition = ParallelMath::MakeUInt15(0);
-
- for (int px = 0; px < 16; px++)
- bestIndexes[px] = ParallelMath::MakeUInt15(0);
-
- for (int subset = 0; subset < 2; subset++)
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 3; ch++)
- bestEndPoints[subset][epi][ch] = ParallelMath::MakeAInt16(0);
-
- UnfinishedEndpoints<3> partitionedUFEP[32][2];
- UnfinishedEndpoints<3> singleUFEP;
-
- // Generate UFEP for partitions
- for (int p = 0; p < 32; p++)
- {
- int partitionMask = BC7Data::g_partitionMap[p];
-
- EndpointSelector<3, 8> epSelectors[2];
-
- for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
- {
- for (int px = 0; px < 16; px++)
- {
- int subset = (partitionMask >> px) & 1;
- epSelectors[subset].ContributePass(preWeightedPixels[px], pass, ParallelMath::MakeFloat(1.0f));
- }
-
- for (int subset = 0; subset < 2; subset++)
- epSelectors[subset].FinishPass(pass);
- }
-
- for (int subset = 0; subset < 2; subset++)
- partitionedUFEP[p][subset] = epSelectors[subset].GetEndpoints(channelWeights);
- }
-
- // Generate UFEP for single
- {
- EndpointSelector<3, 8> epSelector;
-
- for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
- {
- for (int px = 0; px < 16; px++)
- epSelector.ContributePass(preWeightedPixels[px], pass, ParallelMath::MakeFloat(1.0f));
-
- epSelector.FinishPass(pass);
- }
-
- singleUFEP = epSelector.GetEndpoints(channelWeights);
- }
-
- for (int partitionedInt = 0; partitionedInt < 2; partitionedInt++)
- {
- bool partitioned = (partitionedInt == 1);
-
- for (int aPrec = BC7Data::g_maxHDRPrecision; aPrec >= 0; aPrec--)
- {
- if (!BC7Data::g_hdrModesExistForPrecision[partitionedInt][aPrec])
- continue;
-
- int numPartitions = partitioned ? 32 : 1;
- int numSubsets = partitioned ? 2 : 1;
- int indexBits = partitioned ? 3 : 4;
- int indexRange = (1 << indexBits);
-
- for (int p = 0; p < numPartitions; p++)
- {
- int partitionMask = partitioned ? BC7Data::g_partitionMap[p] : 0;
-
- const int MaxMetaRounds = MaxTweakRounds * MaxRefineRounds;
-
- MAInt16 metaEndPointsQuantized[MaxMetaRounds][2][2][3];
- MUInt15 metaIndexes[MaxMetaRounds][16];
- MFloat metaError[MaxMetaRounds][2];
-
- bool roundValid[MaxMetaRounds][2];
-
- for (int r = 0; r < MaxMetaRounds; r++)
- for (int subset = 0; subset < 2; subset++)
- roundValid[r][subset] = true;
-
- for (int subset = 0; subset < numSubsets; subset++)
- {
- for (int tweak = 0; tweak < MaxTweakRounds; tweak++)
- {
- EndpointRefiner<3> refiners[2];
-
- bool abortRemainingRefines = false;
- for (int refinePass = 0; refinePass < MaxRefineRounds; refinePass++)
- {
- int metaRound = tweak * MaxRefineRounds + refinePass;
-
- if (tweak >= numTweakRounds || refinePass >= numRefineRounds)
- abortRemainingRefines = true;
-
- if (abortRemainingRefines)
- {
- roundValid[metaRound][subset] = false;
- continue;
- }
-
- MAInt16(&mrQuantizedEndPoints)[2][2][3] = metaEndPointsQuantized[metaRound];
- MUInt15(&mrIndexes)[16] = metaIndexes[metaRound];
-
- MSInt16 endPointsColorSpace[2][3];
-
- if (refinePass == 0)
- {
- UnfinishedEndpoints<3> ufep = partitioned ? partitionedUFEP[p][subset] : singleUFEP;
-
- if (isSigned)
- ufep.FinishHDRSigned(tweak, indexRange, endPointsColorSpace[0], endPointsColorSpace[1], &rtn);
- else
- ufep.FinishHDRUnsigned(tweak, indexRange, endPointsColorSpace[0], endPointsColorSpace[1], &rtn);
- }
- else
- refiners[subset].GetRefinedEndpointsHDR(endPointsColorSpace, isSigned, &rtn);
-
- refiners[subset].Init(indexRange, channelWeights);
-
- int fixupIndex = (subset == 0) ? 0 : BC7Data::g_fixupIndexes2[p];
-
- IndexSelectorHDR<3> indexSelector;
- if (isSigned)
- QuantizeEndpointsSigned(endPointsColorSpace, floatPixels2CL, floatPixelsLinearWeighted, mrQuantizedEndPoints[subset], mrIndexes, indexSelector, fixupIndex, aPrec, indexRange, channelWeights, fastIndexing, &rtn);
- else
- QuantizeEndpointsUnsigned(endPointsColorSpace, floatPixels2CL, floatPixelsLinearWeighted, mrQuantizedEndPoints[subset], mrIndexes, indexSelector, fixupIndex, aPrec, indexRange, channelWeights, fastIndexing, &rtn);
-
- if (metaRound > 0)
- {
- ParallelMath::Int16CompFlag anySame = ParallelMath::MakeBoolInt16(false);
-
- for (int prevRound = 0; prevRound < metaRound; prevRound++)
- {
- MAInt16(&prevRoundEPs)[2][3] = metaEndPointsQuantized[prevRound][subset];
-
- ParallelMath::Int16CompFlag same = ParallelMath::MakeBoolInt16(true);
-
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 3; ch++)
- same = (same & ParallelMath::Equal(prevRoundEPs[epi][ch], mrQuantizedEndPoints[subset][epi][ch]));
-
- anySame = (anySame | same);
- if (ParallelMath::AllSet(anySame))
- break;
- }
-
- if (ParallelMath::AllSet(anySame))
- {
- roundValid[metaRound][subset] = false;
- continue;
- }
- }
-
- MFloat subsetError = ParallelMath::MakeFloatZero();
-
- {
- for (int px = 0; px < 16; px++)
- {
- if (subset != ((partitionMask >> px) & 1))
- continue;
-
- MUInt15 index;
- if (px == fixupIndex)
- index = mrIndexes[px];
- else
- {
- index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixels2CL[px], &rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[px], &rtn);
- mrIndexes[px] = index;
- }
-
- MSInt16 reconstructed[3];
- if (isSigned)
- indexSelector.ReconstructHDRSigned(mrIndexes[px], reconstructed);
- else
- indexSelector.ReconstructHDRUnsigned(mrIndexes[px], reconstructed);
-
- subsetError = subsetError + (fastIndexing ? BCCommon::ComputeErrorHDRFast<3>(flags, reconstructed, pixels[px], channelWeightsSq) : BCCommon::ComputeErrorHDRSlow<3>(flags, reconstructed, pixels[px], channelWeightsSq));
-
- if (refinePass != numRefineRounds - 1)
- refiners[subset].ContributeUnweightedPW(preWeightedPixels[px], index);
- }
- }
-
- metaError[metaRound][subset] = subsetError;
- }
- }
- }
-
- // Now we have a bunch of attempts, but not all of them will fit in the delta coding scheme
- int numMeta1 = partitioned ? MaxMetaRounds : 1;
- for (int meta0 = 0; meta0 < MaxMetaRounds; meta0++)
- {
- if (!roundValid[meta0][0])
- continue;
-
- for (int meta1 = 0; meta1 < numMeta1; meta1++)
- {
- MFloat combinedError = metaError[meta0][0];
- if (partitioned)
- {
- if (!roundValid[meta1][1])
- continue;
-
- combinedError = combinedError + metaError[meta1][1];
- }
-
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(combinedError, bestError);
- if (!ParallelMath::AnySet(errorBetter))
- continue;
-
- ParallelMath::Int16CompFlag needsCommit = ParallelMath::FloatFlagToInt16(errorBetter);
-
- // Figure out if this is encodable
- for (int mode = 0; mode < BC7Data::g_numHDRModes; mode++)
- {
- const BC7Data::BC6HModeInfo &modeInfo = BC7Data::g_hdrModes[mode];
-
- if (modeInfo.m_partitioned != partitioned || modeInfo.m_aPrec != aPrec)
- continue;
-
- MAInt16 encodedEPs[2][2][3];
- ParallelMath::Int16CompFlag isLegal;
- if (partitioned)
- EvaluatePartitionedLegality(metaEndPointsQuantized[meta0][0], metaEndPointsQuantized[meta1][1], modeInfo.m_aPrec, modeInfo.m_bPrec, modeInfo.m_transformed, encodedEPs, isLegal);
- else
- EvaluateSingleLegality(metaEndPointsQuantized[meta0][0], modeInfo.m_aPrec, modeInfo.m_bPrec, modeInfo.m_transformed, encodedEPs[0], isLegal);
-
- ParallelMath::Int16CompFlag isLegalAndBetter = (ParallelMath::FloatFlagToInt16(errorBetter) & isLegal);
- if (!ParallelMath::AnySet(isLegalAndBetter))
- continue;
-
- ParallelMath::FloatCompFlag isLegalAndBetterFloat = ParallelMath::Int16FlagToFloat(isLegalAndBetter);
-
- ParallelMath::ConditionalSet(bestError, isLegalAndBetterFloat, combinedError);
- ParallelMath::ConditionalSet(bestMode, isLegalAndBetter, ParallelMath::MakeUInt15(static_cast<uint16_t>(mode)));
- ParallelMath::ConditionalSet(bestPartition, isLegalAndBetter, ParallelMath::MakeUInt15(static_cast<uint16_t>(p)));
-
- for (int subset = 0; subset < numSubsets; subset++)
- {
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- ParallelMath::ConditionalSet(bestEndPoints[subset][epi][ch], isLegalAndBetter, encodedEPs[subset][epi][ch]);
- }
- }
-
- for (int px = 0; px < 16; px++)
- {
- int subset = ((partitionMask >> px) & 1);
- if (subset == 0)
- ParallelMath::ConditionalSet(bestIndexes[px], isLegalAndBetter, metaIndexes[meta0][px]);
- else
- ParallelMath::ConditionalSet(bestIndexes[px], isLegalAndBetter, metaIndexes[meta1][px]);
- }
-
- needsCommit = ParallelMath::AndNot(needsCommit, isLegalAndBetter);
- if (!ParallelMath::AnySet(needsCommit))
- break;
- }
- }
- }
- }
- }
- }
-
- // At this point, everything should be set
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- ParallelMath::ScalarUInt16 mode = ParallelMath::Extract(bestMode, block);
- ParallelMath::ScalarUInt16 partition = ParallelMath::Extract(bestPartition, block);
- int32_t eps[2][2][3];
- ParallelMath::ScalarUInt16 indexes[16];
-
- const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode];
-
- const BC6HData::ModeDescriptor* desc = BC6HData::g_modeDescriptors[mode];
-
- const size_t headerBits = modeInfo.m_partitioned ? 82 : 65;
-
- for (int subset = 0; subset < 2; subset++)
- {
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- eps[subset][epi][ch] = ParallelMath::Extract(bestEndPoints[subset][epi][ch], block);
- }
- }
-
- for (int px = 0; px < 16; px++)
- indexes[px] = ParallelMath::Extract(bestIndexes[px], block);
-
- uint16_t modeID = modeInfo.m_modeID;
-
- PackingVector pv;
- pv.Init();
-
- for (size_t i = 0; i < headerBits; i++)
- {
- int32_t codedValue = 0;
- switch (desc[i].m_eField)
- {
- case BC6HData::M: codedValue = modeID; break;
- case BC6HData::D: codedValue = partition; break;
- case BC6HData::RW: codedValue = eps[0][0][0]; break;
- case BC6HData::RX: codedValue = eps[0][1][0]; break;
- case BC6HData::RY: codedValue = eps[1][0][0]; break;
- case BC6HData::RZ: codedValue = eps[1][1][0]; break;
- case BC6HData::GW: codedValue = eps[0][0][1]; break;
- case BC6HData::GX: codedValue = eps[0][1][1]; break;
- case BC6HData::GY: codedValue = eps[1][0][1]; break;
- case BC6HData::GZ: codedValue = eps[1][1][1]; break;
- case BC6HData::BW: codedValue = eps[0][0][2]; break;
- case BC6HData::BX: codedValue = eps[0][1][2]; break;
- case BC6HData::BY: codedValue = eps[1][0][2]; break;
- case BC6HData::BZ: codedValue = eps[1][1][2]; break;
- default: assert(false); break;
- }
-
- pv.Pack(static_cast<uint16_t>((codedValue >> desc[i].m_uBit) & 1), 1);
- }
-
- int fixupIndex1 = 0;
- int indexBits = 4;
- if (modeInfo.m_partitioned)
- {
- fixupIndex1 = BC7Data::g_fixupIndexes2[partition];
- indexBits = 3;
- }
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::ScalarUInt16 index = ParallelMath::Extract(bestIndexes[px], block);
- if (px == 0 || px == fixupIndex1)
- pv.Pack(index, indexBits - 1);
- else
- pv.Pack(index, indexBits);
- }
-
- pv.Flush(packedBlocks + 16 * block);
- }
- }
-
- static void SignExtendSingle(int &v, int bits)
- {
- if (v & (1 << (bits - 1)))
- v |= -(1 << bits);
- }
-
- static void UnpackOne(PixelBlockF16 &output, const uint8_t *pBC, bool isSigned)
- {
- UnpackingVector pv;
- pv.Init(pBC);
-
- int numModeBits = 2;
- int modeBits = pv.Unpack(2);
- if (modeBits != 0 && modeBits != 1)
- {
- modeBits |= pv.Unpack(3) << 2;
- numModeBits += 3;
- }
-
- int mode = -1;
- for (int possibleMode = 0; possibleMode < BC7Data::g_numHDRModes; possibleMode++)
- {
- if (BC7Data::g_hdrModes[possibleMode].m_modeID == modeBits)
- {
- mode = possibleMode;
- break;
- }
- }
-
- if (mode < 0)
- {
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 3; ch++)
- output.m_pixels[px][ch] = 0;
- output.m_pixels[px][3] = 0x3c00; // 1.0
- }
- return;
- }
-
- const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode];
- const size_t headerBits = modeInfo.m_partitioned ? 82 : 65;
- const BC6HData::ModeDescriptor* desc = BC6HData::g_modeDescriptors[mode];
-
- int32_t partition = 0;
- int32_t eps[2][2][3];
-
- for (int subset = 0; subset < 2; subset++)
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 3; ch++)
- eps[subset][epi][ch] = 0;
-
- for (size_t i = numModeBits; i < headerBits; i++)
- {
- int32_t *pCodedValue = NULL;
-
- switch (desc[i].m_eField)
- {
- case BC6HData::D: pCodedValue = &partition; break;
- case BC6HData::RW: pCodedValue = &eps[0][0][0]; break;
- case BC6HData::RX: pCodedValue = &eps[0][1][0]; break;
- case BC6HData::RY: pCodedValue = &eps[1][0][0]; break;
- case BC6HData::RZ: pCodedValue = &eps[1][1][0]; break;
- case BC6HData::GW: pCodedValue = &eps[0][0][1]; break;
- case BC6HData::GX: pCodedValue = &eps[0][1][1]; break;
- case BC6HData::GY: pCodedValue = &eps[1][0][1]; break;
- case BC6HData::GZ: pCodedValue = &eps[1][1][1]; break;
- case BC6HData::BW: pCodedValue = &eps[0][0][2]; break;
- case BC6HData::BX: pCodedValue = &eps[0][1][2]; break;
- case BC6HData::BY: pCodedValue = &eps[1][0][2]; break;
- case BC6HData::BZ: pCodedValue = &eps[1][1][2]; break;
- default: assert(false); break;
- }
-
- (*pCodedValue) |= pv.Unpack(1) << desc[i].m_uBit;
- }
-
-
- uint16_t modeID = modeInfo.m_modeID;
-
- int fixupIndex1 = 0;
- int indexBits = 4;
- int numSubsets = 1;
- if (modeInfo.m_partitioned)
- {
- fixupIndex1 = BC7Data::g_fixupIndexes2[partition];
- indexBits = 3;
- numSubsets = 2;
- }
-
- int indexes[16];
- for (int px = 0; px < 16; px++)
- {
- if (px == 0 || px == fixupIndex1)
- indexes[px] = pv.Unpack(indexBits - 1);
- else
- indexes[px] = pv.Unpack(indexBits);
- }
-
- if (modeInfo.m_partitioned)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- if (isSigned)
- SignExtendSingle(eps[0][0][ch], modeInfo.m_aPrec);
- if (modeInfo.m_transformed || isSigned)
- {
- SignExtendSingle(eps[0][1][ch], modeInfo.m_bPrec[ch]);
- SignExtendSingle(eps[1][0][ch], modeInfo.m_bPrec[ch]);
- SignExtendSingle(eps[1][1][ch], modeInfo.m_bPrec[ch]);
- }
- }
- }
- else
- {
- for (int ch = 0; ch < 3; ch++)
- {
- if (isSigned)
- SignExtendSingle(eps[0][0][ch], modeInfo.m_aPrec);
- if (modeInfo.m_transformed || isSigned)
- SignExtendSingle(eps[0][1][ch], modeInfo.m_bPrec[ch]);
- }
- }
-
- int aPrec = modeInfo.m_aPrec;
-
- if (modeInfo.m_transformed)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- int wrapMask = (1 << aPrec) - 1;
-
- eps[0][1][ch] = ((eps[0][0][ch] + eps[0][1][ch]) & wrapMask);
- if (isSigned)
- SignExtendSingle(eps[0][1][ch], aPrec);
-
- if (modeInfo.m_partitioned)
- {
- eps[1][0][ch] = ((eps[0][0][ch] + eps[1][0][ch]) & wrapMask);
- eps[1][1][ch] = ((eps[0][0][ch] + eps[1][1][ch]) & wrapMask);
-
- if (isSigned)
- {
- SignExtendSingle(eps[1][0][ch], aPrec);
- SignExtendSingle(eps[1][1][ch], aPrec);
- }
- }
- }
- }
-
- // Unquantize endpoints
- for (int subset = 0; subset < numSubsets; subset++)
- {
- for (int epi = 0; epi < 2; epi++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- int &v = eps[subset][epi][ch];
-
- if (isSigned)
- {
- if (aPrec >= 16)
- {
- // Nothing
- }
- else
- {
- bool s = false;
- int comp = v;
- if (v < 0)
- {
- s = true;
- comp = -comp;
- }
-
- int unq = 0;
- if (comp == 0)
- unq = 0;
- else if (comp >= ((1 << (aPrec - 1)) - 1))
- unq = 0x7fff;
- else
- unq = ((comp << 15) + 0x4000) >> (aPrec - 1);
-
- if (s)
- unq = -unq;
-
- v = unq;
- }
- }
- else
- {
- if (aPrec >= 15)
- {
- // Nothing
- }
- else if (v == 0)
- {
- // Nothing
- }
- else if (v == ((1 << aPrec) - 1))
- v = 0xffff;
- else
- v = ((v << 16) + 0x8000) >> aPrec;
- }
- }
- }
- }
-
- const int *weights = BC7Data::g_weightTables[indexBits];
-
- for (int px = 0; px < 16; px++)
- {
- int subset = 0;
- if (modeInfo.m_partitioned)
- subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
-
- int w = weights[indexes[px]];
- for (int ch = 0; ch < 3; ch++)
- {
- int comp = ((64 - w) * eps[subset][0][ch] + w * eps[subset][1][ch] + 32) >> 6;
-
- if (isSigned)
- {
- if (comp < 0)
- comp = -(((-comp) * 31) >> 5);
- else
- comp = (comp * 31) >> 5;
-
- int s = 0;
- if (comp < 0)
- {
- s = 0x8000;
- comp = -comp;
- }
-
- output.m_pixels[px][ch] = static_cast<uint16_t>(s | comp);
- }
- else
- {
- comp = (comp * 31) >> 6;
- output.m_pixels[px][ch] = static_cast<uint16_t>(comp);
- }
- }
- output.m_pixels[px][3] = 0x3c00; // 1.0
- }
- }
- };
-
- namespace S3TCSingleColorTables
- {
- struct SingleColorTableEntry
- {
- uint8_t m_min;
- uint8_t m_max;
- uint8_t m_actualColor;
- uint8_t m_span;
- };
-
- SingleColorTableEntry g_singleColor5_3[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 8, 0, 2, 8 }, { 8, 0, 2, 8 }, { 0, 8, 5, 8 }, { 0, 8, 5, 8 }, { 0, 8, 5, 8 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 16, 8, 10, 8 }, { 33, 0, 11, 33 }, { 8, 16, 13, 8 }, { 8, 16, 13, 8 }, { 8, 16, 13, 8 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 24, 16, 18, 8 }, { 41, 8, 19, 33 }, { 16, 24, 21, 8 }, { 16, 24, 21, 8 }, { 0, 33, 22, 33 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 33, 24, 27, 9 }, { 33, 24, 27, 9 }, { 33, 24, 27, 9 }, { 41, 24, 29, 17 }, { 24, 33, 30, 9 }, { 24, 33, 30, 9 },
- { 16, 41, 32, 25 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 41, 33, 35, 8 }, { 41, 33, 35, 8 }, { 33, 41, 38, 8 }, { 33, 41, 38, 8 }, { 33, 41, 38, 8 },
- { 24, 49, 40, 25 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 49, 41, 43, 8 }, { 66, 33, 44, 33 }, { 41, 49, 46, 8 }, { 41, 49, 46, 8 }, { 41, 49, 46, 8 },
- { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 57, 49, 51, 8 }, { 74, 41, 52, 33 }, { 49, 57, 54, 8 }, { 49, 57, 54, 8 }, { 33, 66, 55, 33 },
- { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 66, 57, 60, 9 }, { 66, 57, 60, 9 }, { 66, 57, 60, 9 }, { 74, 57, 62, 17 }, { 57, 66, 63, 9 },
- { 57, 66, 63, 9 }, { 49, 74, 65, 25 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 74, 66, 68, 8 }, { 74, 66, 68, 8 }, { 66, 74, 71, 8 }, { 66, 74, 71, 8 },
- { 66, 74, 71, 8 }, { 57, 82, 73, 25 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 82, 74, 76, 8 }, { 99, 66, 77, 33 }, { 74, 82, 79, 8 }, { 74, 82, 79, 8 },
- { 74, 82, 79, 8 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 90, 82, 84, 8 }, { 107, 74, 85, 33 }, { 82, 90, 87, 8 }, { 82, 90, 87, 8 },
- { 66, 99, 88, 33 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 99, 90, 93, 9 }, { 99, 90, 93, 9 }, { 99, 90, 93, 9 }, { 107, 90, 95, 17 },
- { 90, 99, 96, 9 }, { 90, 99, 96, 9 }, { 82, 107, 98, 25 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 107, 99, 101, 8 }, { 107, 99, 101, 8 }, { 99, 107, 104, 8 },
- { 99, 107, 104, 8 }, { 99, 107, 104, 8 }, { 90, 115, 106, 25 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 115, 107, 109, 8 }, { 132, 99, 110, 33 }, { 107, 115, 112, 8 },
- { 107, 115, 112, 8 }, { 107, 115, 112, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 123, 115, 117, 8 }, { 140, 107, 118, 33 }, { 115, 123, 120, 8 },
- { 115, 123, 120, 8 }, { 99, 132, 121, 33 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 132, 123, 126, 9 }, { 132, 123, 126, 9 }, { 132, 123, 126, 9 },
- { 140, 123, 128, 17 }, { 123, 132, 129, 9 }, { 123, 132, 129, 9 }, { 115, 140, 131, 25 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 140, 132, 134, 8 }, { 140, 132, 134, 8 },
- { 132, 140, 137, 8 }, { 132, 140, 137, 8 }, { 132, 140, 137, 8 }, { 123, 148, 139, 25 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 148, 140, 142, 8 }, { 165, 132, 143, 33 },
- { 140, 148, 145, 8 }, { 140, 148, 145, 8 }, { 140, 148, 145, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 156, 148, 150, 8 }, { 173, 140, 151, 33 },
- { 148, 156, 153, 8 }, { 148, 156, 153, 8 }, { 132, 165, 154, 33 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 165, 156, 159, 9 }, { 165, 156, 159, 9 },
- { 165, 156, 159, 9 }, { 173, 156, 161, 17 }, { 156, 165, 162, 9 }, { 156, 165, 162, 9 }, { 148, 173, 164, 25 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 173, 165, 167, 8 },
- { 173, 165, 167, 8 }, { 165, 173, 170, 8 }, { 165, 173, 170, 8 }, { 165, 173, 170, 8 }, { 156, 181, 172, 25 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 181, 173, 175, 8 },
- { 198, 165, 176, 33 }, { 173, 181, 178, 8 }, { 173, 181, 178, 8 }, { 173, 181, 178, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 189, 181, 183, 8 },
- { 206, 173, 184, 33 }, { 181, 189, 186, 8 }, { 181, 189, 186, 8 }, { 165, 198, 187, 33 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 198, 189, 192, 9 },
- { 198, 189, 192, 9 }, { 198, 189, 192, 9 }, { 206, 189, 194, 17 }, { 189, 198, 195, 9 }, { 189, 198, 195, 9 }, { 181, 206, 197, 25 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
- { 206, 198, 200, 8 }, { 206, 198, 200, 8 }, { 198, 206, 203, 8 }, { 198, 206, 203, 8 }, { 198, 206, 203, 8 }, { 189, 214, 205, 25 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
- { 214, 206, 208, 8 }, { 231, 198, 209, 33 }, { 206, 214, 211, 8 }, { 206, 214, 211, 8 }, { 206, 214, 211, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
- { 222, 214, 216, 8 }, { 239, 206, 217, 33 }, { 214, 222, 219, 8 }, { 214, 222, 219, 8 }, { 198, 231, 220, 33 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
- { 231, 222, 225, 9 }, { 231, 222, 225, 9 }, { 231, 222, 225, 9 }, { 239, 222, 227, 17 }, { 222, 231, 228, 9 }, { 222, 231, 228, 9 }, { 214, 239, 230, 25 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 239, 231, 233, 8 }, { 239, 231, 233, 8 }, { 231, 239, 236, 8 }, { 231, 239, 236, 8 }, { 231, 239, 236, 8 }, { 222, 247, 238, 25 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 247, 239, 241, 8 }, { 247, 239, 241, 8 }, { 239, 247, 244, 8 }, { 239, 247, 244, 8 }, { 239, 247, 244, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 255, 247, 249, 8 }, { 255, 247, 249, 8 }, { 247, 255, 252, 8 }, { 247, 255, 252, 8 }, { 247, 255, 252, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor6_3[256] =
- {
- { 0, 0, 0, 0 }, { 4, 0, 1, 4 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 8, 4, 5, 4 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 12, 8, 9, 4 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 16, 12, 13, 4 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 20, 16, 17, 4 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 24, 20, 21, 4 }, { 20, 24, 22, 4 }, { 69, 0, 23, 69 },
- { 24, 24, 24, 0 }, { 28, 24, 25, 4 }, { 24, 28, 26, 4 }, { 65, 8, 27, 57 }, { 28, 28, 28, 0 }, { 32, 28, 29, 4 }, { 28, 32, 30, 4 }, { 69, 12, 31, 57 },
- { 32, 32, 32, 0 }, { 36, 32, 33, 4 }, { 32, 36, 34, 4 }, { 65, 20, 35, 45 }, { 36, 36, 36, 0 }, { 40, 36, 37, 4 }, { 36, 40, 38, 4 }, { 69, 24, 39, 45 },
- { 40, 40, 40, 0 }, { 44, 40, 41, 4 }, { 40, 44, 42, 4 }, { 65, 32, 43, 33 }, { 44, 44, 44, 0 }, { 48, 44, 45, 4 }, { 44, 48, 46, 4 }, { 69, 36, 47, 33 },
- { 48, 48, 48, 0 }, { 52, 48, 49, 4 }, { 48, 52, 50, 4 }, { 65, 44, 51, 21 }, { 52, 52, 52, 0 }, { 56, 52, 53, 4 }, { 52, 56, 54, 4 }, { 69, 48, 55, 21 },
- { 56, 56, 56, 0 }, { 60, 56, 57, 4 }, { 56, 60, 58, 4 }, { 65, 56, 59, 9 }, { 60, 60, 60, 0 }, { 65, 60, 61, 5 }, { 56, 65, 62, 9 }, { 60, 65, 63, 5 },
- { 56, 69, 64, 13 }, { 65, 65, 65, 0 }, { 69, 65, 66, 4 }, { 65, 69, 67, 4 }, { 60, 73, 68, 13 }, { 69, 69, 69, 0 }, { 73, 69, 70, 4 }, { 69, 73, 71, 4 },
- { 56, 81, 72, 25 }, { 73, 73, 73, 0 }, { 77, 73, 74, 4 }, { 73, 77, 75, 4 }, { 60, 85, 76, 25 }, { 77, 77, 77, 0 }, { 81, 77, 78, 4 }, { 77, 81, 79, 4 },
- { 56, 93, 80, 37 }, { 81, 81, 81, 0 }, { 85, 81, 82, 4 }, { 81, 85, 83, 4 }, { 60, 97, 84, 37 }, { 85, 85, 85, 0 }, { 89, 85, 86, 4 }, { 85, 89, 87, 4 },
- { 56, 105, 88, 49 }, { 89, 89, 89, 0 }, { 93, 89, 90, 4 }, { 89, 93, 91, 4 }, { 60, 109, 92, 49 }, { 93, 93, 93, 0 }, { 97, 93, 94, 4 }, { 93, 97, 95, 4 },
- { 134, 77, 96, 57 }, { 97, 97, 97, 0 }, { 101, 97, 98, 4 }, { 97, 101, 99, 4 }, { 130, 85, 100, 45 }, { 101, 101, 101, 0 }, { 105, 101, 102, 4 }, { 101, 105, 103, 4 },
- { 134, 89, 104, 45 }, { 105, 105, 105, 0 }, { 109, 105, 106, 4 }, { 105, 109, 107, 4 }, { 130, 97, 108, 33 }, { 109, 109, 109, 0 }, { 113, 109, 110, 4 }, { 109, 113, 111, 4 },
- { 134, 101, 112, 33 }, { 113, 113, 113, 0 }, { 117, 113, 114, 4 }, { 113, 117, 115, 4 }, { 130, 109, 116, 21 }, { 117, 117, 117, 0 }, { 121, 117, 118, 4 }, { 117, 121, 119, 4 },
- { 134, 113, 120, 21 }, { 121, 121, 121, 0 }, { 125, 121, 122, 4 }, { 121, 125, 123, 4 }, { 130, 121, 124, 9 }, { 125, 125, 125, 0 }, { 130, 125, 126, 5 }, { 121, 130, 127, 9 },
- { 125, 130, 128, 5 }, { 121, 134, 129, 13 }, { 130, 130, 130, 0 }, { 134, 130, 131, 4 }, { 130, 134, 132, 4 }, { 125, 138, 133, 13 }, { 134, 134, 134, 0 }, { 138, 134, 135, 4 },
- { 134, 138, 136, 4 }, { 121, 146, 137, 25 }, { 138, 138, 138, 0 }, { 142, 138, 139, 4 }, { 138, 142, 140, 4 }, { 125, 150, 141, 25 }, { 142, 142, 142, 0 }, { 146, 142, 143, 4 },
- { 142, 146, 144, 4 }, { 121, 158, 145, 37 }, { 146, 146, 146, 0 }, { 150, 146, 147, 4 }, { 146, 150, 148, 4 }, { 125, 162, 149, 37 }, { 150, 150, 150, 0 }, { 154, 150, 151, 4 },
- { 150, 154, 152, 4 }, { 121, 170, 153, 49 }, { 154, 154, 154, 0 }, { 158, 154, 155, 4 }, { 154, 158, 156, 4 }, { 125, 174, 157, 49 }, { 158, 158, 158, 0 }, { 162, 158, 159, 4 },
- { 158, 162, 160, 4 }, { 199, 142, 161, 57 }, { 162, 162, 162, 0 }, { 166, 162, 163, 4 }, { 162, 166, 164, 4 }, { 195, 150, 165, 45 }, { 166, 166, 166, 0 }, { 170, 166, 167, 4 },
- { 166, 170, 168, 4 }, { 199, 154, 169, 45 }, { 170, 170, 170, 0 }, { 174, 170, 171, 4 }, { 170, 174, 172, 4 }, { 195, 162, 173, 33 }, { 174, 174, 174, 0 }, { 178, 174, 175, 4 },
- { 174, 178, 176, 4 }, { 199, 166, 177, 33 }, { 178, 178, 178, 0 }, { 182, 178, 179, 4 }, { 178, 182, 180, 4 }, { 195, 174, 181, 21 }, { 182, 182, 182, 0 }, { 186, 182, 183, 4 },
- { 182, 186, 184, 4 }, { 199, 178, 185, 21 }, { 186, 186, 186, 0 }, { 190, 186, 187, 4 }, { 186, 190, 188, 4 }, { 195, 186, 189, 9 }, { 190, 190, 190, 0 }, { 195, 190, 191, 5 },
- { 186, 195, 192, 9 }, { 190, 195, 193, 5 }, { 186, 199, 194, 13 }, { 195, 195, 195, 0 }, { 199, 195, 196, 4 }, { 195, 199, 197, 4 }, { 190, 203, 198, 13 }, { 199, 199, 199, 0 },
- { 203, 199, 200, 4 }, { 199, 203, 201, 4 }, { 186, 211, 202, 25 }, { 203, 203, 203, 0 }, { 207, 203, 204, 4 }, { 203, 207, 205, 4 }, { 190, 215, 206, 25 }, { 207, 207, 207, 0 },
- { 211, 207, 208, 4 }, { 207, 211, 209, 4 }, { 186, 223, 210, 37 }, { 211, 211, 211, 0 }, { 215, 211, 212, 4 }, { 211, 215, 213, 4 }, { 190, 227, 214, 37 }, { 215, 215, 215, 0 },
- { 219, 215, 216, 4 }, { 215, 219, 217, 4 }, { 186, 235, 218, 49 }, { 219, 219, 219, 0 }, { 223, 219, 220, 4 }, { 219, 223, 221, 4 }, { 190, 239, 222, 49 }, { 223, 223, 223, 0 },
- { 227, 223, 224, 4 }, { 223, 227, 225, 4 }, { 186, 247, 226, 61 }, { 227, 227, 227, 0 }, { 231, 227, 228, 4 }, { 227, 231, 229, 4 }, { 190, 251, 230, 61 }, { 231, 231, 231, 0 },
- { 235, 231, 232, 4 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 239, 235, 236, 4 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
- { 243, 239, 240, 4 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 247, 243, 244, 4 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 251, 247, 248, 4 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 255, 251, 252, 4 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor5_2[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 41, 32, 17 },
- { 24, 41, 32, 17 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 24, 49, 36, 25 }, { 24, 49, 36, 25 }, { 33, 41, 37, 8 }, { 33, 41, 37, 8 }, { 24, 57, 40, 33 },
- { 24, 57, 40, 33 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 49, 49, 49, 0 },
- { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 57, 57, 57, 0 },
- { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 },
- { 57, 74, 65, 17 }, { 57, 74, 65, 17 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 57, 82, 69, 25 }, { 57, 82, 69, 25 }, { 66, 74, 70, 8 }, { 66, 74, 70, 8 },
- { 57, 90, 73, 33 }, { 57, 90, 73, 33 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 },
- { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 },
- { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 },
- { 90, 99, 94, 9 }, { 90, 107, 98, 17 }, { 90, 107, 98, 17 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 90, 115, 102, 25 }, { 90, 115, 102, 25 }, { 99, 107, 103, 8 },
- { 99, 107, 103, 8 }, { 90, 123, 106, 33 }, { 90, 123, 106, 33 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 111, 8 }, { 107, 115, 111, 8 },
- { 107, 115, 111, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 119, 8 }, { 115, 123, 119, 8 },
- { 115, 123, 119, 8 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 127, 9 }, { 123, 132, 127, 9 },
- { 123, 132, 127, 9 }, { 123, 132, 127, 9 }, { 123, 140, 131, 17 }, { 123, 140, 131, 17 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 123, 148, 135, 25 }, { 123, 148, 135, 25 },
- { 132, 140, 136, 8 }, { 132, 140, 136, 8 }, { 123, 156, 139, 33 }, { 123, 156, 139, 33 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 144, 8 },
- { 140, 148, 144, 8 }, { 140, 148, 144, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 152, 8 },
- { 148, 156, 152, 8 }, { 148, 156, 152, 8 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 160, 9 },
- { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 173, 164, 17 }, { 156, 173, 164, 17 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 156, 181, 168, 25 },
- { 156, 181, 168, 25 }, { 165, 173, 169, 8 }, { 165, 173, 169, 8 }, { 156, 189, 172, 33 }, { 156, 189, 172, 33 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 },
- { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 },
- { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 },
- { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 206, 197, 17 }, { 189, 206, 197, 17 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
- { 189, 214, 201, 25 }, { 189, 214, 201, 25 }, { 198, 206, 202, 8 }, { 198, 206, 202, 8 }, { 189, 222, 205, 33 }, { 189, 222, 205, 33 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
- { 206, 206, 206, 0 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
- { 214, 214, 214, 0 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
- { 222, 222, 222, 0 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 239, 230, 17 }, { 222, 239, 230, 17 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 222, 247, 234, 25 }, { 222, 247, 234, 25 }, { 231, 239, 235, 8 }, { 231, 239, 235, 8 }, { 222, 255, 238, 33 }, { 222, 255, 238, 33 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 239, 239, 239, 0 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor6_2[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 22, 4 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 28, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 32, 30, 4 }, { 32, 32, 32, 0 },
- { 32, 32, 32, 0 }, { 32, 32, 32, 0 }, { 32, 36, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 40, 38, 4 }, { 40, 40, 40, 0 },
- { 40, 40, 40, 0 }, { 40, 40, 40, 0 }, { 40, 44, 42, 4 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 48, 46, 4 }, { 48, 48, 48, 0 },
- { 48, 48, 48, 0 }, { 48, 48, 48, 0 }, { 48, 52, 50, 4 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 56, 54, 4 }, { 56, 56, 56, 0 },
- { 56, 56, 56, 0 }, { 56, 56, 56, 0 }, { 56, 60, 58, 4 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 65, 62, 5 }, { 60, 65, 62, 5 },
- { 60, 69, 64, 9 }, { 65, 65, 65, 0 }, { 60, 73, 66, 13 }, { 65, 69, 67, 4 }, { 60, 77, 68, 17 }, { 69, 69, 69, 0 }, { 60, 81, 70, 21 }, { 69, 73, 71, 4 },
- { 60, 85, 72, 25 }, { 73, 73, 73, 0 }, { 60, 89, 74, 29 }, { 73, 77, 75, 4 }, { 60, 93, 76, 33 }, { 77, 77, 77, 0 }, { 60, 97, 78, 37 }, { 77, 81, 79, 4 },
- { 60, 101, 80, 41 }, { 81, 81, 81, 0 }, { 60, 105, 82, 45 }, { 81, 85, 83, 4 }, { 60, 109, 84, 49 }, { 85, 85, 85, 0 }, { 60, 113, 86, 53 }, { 85, 89, 87, 4 },
- { 60, 117, 88, 57 }, { 89, 89, 89, 0 }, { 60, 121, 90, 61 }, { 89, 93, 91, 4 }, { 60, 125, 92, 65 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 97, 95, 4 },
- { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 101, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 105, 103, 4 },
- { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 109, 107, 4 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 113, 111, 4 },
- { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 117, 115, 4 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 121, 119, 4 },
- { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 125, 123, 4 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 130, 127, 5 },
- { 125, 130, 127, 5 }, { 125, 134, 129, 9 }, { 130, 130, 130, 0 }, { 125, 138, 131, 13 }, { 130, 134, 132, 4 }, { 125, 142, 133, 17 }, { 134, 134, 134, 0 }, { 125, 146, 135, 21 },
- { 134, 138, 136, 4 }, { 125, 150, 137, 25 }, { 138, 138, 138, 0 }, { 125, 154, 139, 29 }, { 138, 142, 140, 4 }, { 125, 158, 141, 33 }, { 142, 142, 142, 0 }, { 125, 162, 143, 37 },
- { 142, 146, 144, 4 }, { 125, 166, 145, 41 }, { 146, 146, 146, 0 }, { 125, 170, 147, 45 }, { 146, 150, 148, 4 }, { 125, 174, 149, 49 }, { 150, 150, 150, 0 }, { 125, 178, 151, 53 },
- { 150, 154, 152, 4 }, { 125, 182, 153, 57 }, { 154, 154, 154, 0 }, { 125, 186, 155, 61 }, { 154, 158, 156, 4 }, { 125, 190, 157, 65 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 },
- { 158, 162, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 166, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 },
- { 166, 170, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 174, 172, 4 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 },
- { 174, 178, 176, 4 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 182, 180, 4 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 },
- { 182, 186, 184, 4 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 190, 188, 4 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 },
- { 190, 195, 192, 5 }, { 190, 195, 192, 5 }, { 190, 199, 194, 9 }, { 195, 195, 195, 0 }, { 190, 203, 196, 13 }, { 195, 199, 197, 4 }, { 190, 207, 198, 17 }, { 199, 199, 199, 0 },
- { 190, 211, 200, 21 }, { 199, 203, 201, 4 }, { 190, 215, 202, 25 }, { 203, 203, 203, 0 }, { 190, 219, 204, 29 }, { 203, 207, 205, 4 }, { 190, 223, 206, 33 }, { 207, 207, 207, 0 },
- { 190, 227, 208, 37 }, { 207, 211, 209, 4 }, { 190, 231, 210, 41 }, { 211, 211, 211, 0 }, { 190, 235, 212, 45 }, { 211, 215, 213, 4 }, { 190, 239, 214, 49 }, { 215, 215, 215, 0 },
- { 190, 243, 216, 53 }, { 215, 219, 217, 4 }, { 190, 247, 218, 57 }, { 219, 219, 219, 0 }, { 190, 251, 220, 61 }, { 219, 223, 221, 4 }, { 190, 255, 222, 65 }, { 223, 223, 223, 0 },
- { 223, 223, 223, 0 }, { 223, 227, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 231, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor5_3_p[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 8, 0, 2, 8 }, { 8, 0, 2, 8 }, { 0, 8, 5, 8 }, { 0, 8, 5, 8 }, { 0, 8, 5, 8 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 16, 8, 10, 8 }, { 33, 0, 11, 33 }, { 8, 16, 13, 8 }, { 8, 16, 13, 8 }, { 8, 16, 13, 8 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 24, 16, 18, 8 }, { 41, 8, 19, 33 }, { 16, 24, 21, 8 }, { 16, 24, 21, 8 }, { 0, 33, 22, 33 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 33, 24, 27, 9 }, { 33, 24, 27, 9 }, { 33, 24, 27, 9 }, { 41, 24, 29, 17 }, { 24, 33, 30, 9 }, { 24, 33, 30, 9 },
- { 16, 41, 32, 25 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 41, 33, 35, 8 }, { 41, 33, 35, 8 }, { 33, 41, 38, 8 }, { 33, 41, 38, 8 }, { 33, 41, 38, 8 },
- { 24, 49, 40, 25 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 49, 41, 43, 8 }, { 66, 33, 44, 33 }, { 41, 49, 46, 8 }, { 41, 49, 46, 8 }, { 41, 49, 46, 8 },
- { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 57, 49, 51, 8 }, { 74, 41, 52, 33 }, { 49, 57, 54, 8 }, { 49, 57, 54, 8 }, { 33, 66, 55, 33 },
- { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 66, 57, 60, 9 }, { 66, 57, 60, 9 }, { 66, 57, 60, 9 }, { 74, 57, 62, 17 }, { 57, 66, 63, 9 },
- { 57, 66, 63, 9 }, { 49, 74, 65, 25 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 74, 66, 68, 8 }, { 74, 66, 68, 8 }, { 66, 74, 71, 8 }, { 66, 74, 71, 8 },
- { 66, 74, 71, 8 }, { 57, 82, 73, 25 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 82, 74, 76, 8 }, { 99, 66, 77, 33 }, { 74, 82, 79, 8 }, { 74, 82, 79, 8 },
- { 74, 82, 79, 8 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 90, 82, 84, 8 }, { 107, 74, 85, 33 }, { 82, 90, 87, 8 }, { 82, 90, 87, 8 },
- { 66, 99, 88, 33 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 99, 90, 93, 9 }, { 99, 90, 93, 9 }, { 99, 90, 93, 9 }, { 107, 90, 95, 17 },
- { 90, 99, 96, 9 }, { 90, 99, 96, 9 }, { 82, 107, 98, 25 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 107, 99, 101, 8 }, { 107, 99, 101, 8 }, { 99, 107, 104, 8 },
- { 99, 107, 104, 8 }, { 99, 107, 104, 8 }, { 90, 115, 106, 25 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 115, 107, 109, 8 }, { 132, 99, 110, 33 }, { 107, 115, 112, 8 },
- { 107, 115, 112, 8 }, { 107, 115, 112, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 123, 115, 117, 8 }, { 140, 107, 118, 33 }, { 115, 123, 120, 8 },
- { 115, 123, 120, 8 }, { 99, 132, 121, 33 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 132, 123, 126, 9 }, { 132, 123, 126, 9 }, { 132, 123, 126, 9 },
- { 140, 123, 128, 17 }, { 123, 132, 129, 9 }, { 123, 132, 129, 9 }, { 115, 140, 131, 25 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 140, 132, 134, 8 }, { 140, 132, 134, 8 },
- { 132, 140, 137, 8 }, { 132, 140, 137, 8 }, { 132, 140, 137, 8 }, { 123, 148, 139, 25 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 148, 140, 142, 8 }, { 165, 132, 143, 33 },
- { 140, 148, 145, 8 }, { 140, 148, 145, 8 }, { 140, 148, 145, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 156, 148, 150, 8 }, { 173, 140, 151, 33 },
- { 148, 156, 153, 8 }, { 148, 156, 153, 8 }, { 132, 165, 154, 33 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 165, 156, 159, 9 }, { 165, 156, 159, 9 },
- { 165, 156, 159, 9 }, { 173, 156, 161, 17 }, { 156, 165, 162, 9 }, { 156, 165, 162, 9 }, { 148, 173, 164, 25 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 173, 165, 167, 8 },
- { 173, 165, 167, 8 }, { 165, 173, 170, 8 }, { 165, 173, 170, 8 }, { 165, 173, 170, 8 }, { 156, 181, 172, 25 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 181, 173, 175, 8 },
- { 198, 165, 176, 33 }, { 173, 181, 178, 8 }, { 173, 181, 178, 8 }, { 173, 181, 178, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 189, 181, 183, 8 },
- { 206, 173, 184, 33 }, { 181, 189, 186, 8 }, { 181, 189, 186, 8 }, { 165, 198, 187, 33 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 198, 189, 192, 9 },
- { 198, 189, 192, 9 }, { 198, 189, 192, 9 }, { 206, 189, 194, 17 }, { 189, 198, 195, 9 }, { 189, 198, 195, 9 }, { 181, 206, 197, 25 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
- { 206, 198, 200, 8 }, { 206, 198, 200, 8 }, { 198, 206, 203, 8 }, { 198, 206, 203, 8 }, { 198, 206, 203, 8 }, { 189, 214, 205, 25 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
- { 214, 206, 208, 8 }, { 231, 198, 209, 33 }, { 206, 214, 211, 8 }, { 206, 214, 211, 8 }, { 206, 214, 211, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
- { 222, 214, 216, 8 }, { 239, 206, 217, 33 }, { 214, 222, 219, 8 }, { 214, 222, 219, 8 }, { 198, 231, 220, 33 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
- { 231, 222, 225, 9 }, { 231, 222, 225, 9 }, { 231, 222, 225, 9 }, { 239, 222, 227, 17 }, { 222, 231, 228, 9 }, { 222, 231, 228, 9 }, { 214, 239, 230, 25 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 239, 231, 233, 8 }, { 239, 231, 233, 8 }, { 231, 239, 236, 8 }, { 231, 239, 236, 8 }, { 231, 239, 236, 8 }, { 222, 247, 238, 25 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 247, 239, 241, 8 }, { 247, 239, 241, 8 }, { 239, 247, 244, 8 }, { 239, 247, 244, 8 }, { 239, 247, 244, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 255, 247, 249, 8 }, { 255, 247, 249, 8 }, { 247, 255, 252, 8 }, { 247, 255, 252, 8 }, { 247, 255, 252, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor6_3_p[256] =
- {
- { 0, 0, 0, 0 }, { 4, 0, 1, 4 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 8, 4, 5, 4 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 12, 8, 9, 4 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 16, 12, 13, 4 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 20, 16, 17, 4 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 24, 20, 21, 4 }, { 20, 24, 22, 4 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 28, 24, 25, 4 }, { 24, 28, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 32, 28, 29, 4 }, { 28, 32, 30, 4 }, { 32, 32, 32, 0 },
- { 32, 32, 32, 0 }, { 36, 32, 33, 4 }, { 32, 36, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 40, 36, 37, 4 }, { 36, 40, 38, 4 }, { 40, 40, 40, 0 },
- { 40, 40, 40, 0 }, { 44, 40, 41, 4 }, { 40, 44, 42, 4 }, { 65, 32, 43, 33 }, { 44, 44, 44, 0 }, { 48, 44, 45, 4 }, { 44, 48, 46, 4 }, { 69, 36, 47, 33 },
- { 48, 48, 48, 0 }, { 52, 48, 49, 4 }, { 48, 52, 50, 4 }, { 65, 44, 51, 21 }, { 52, 52, 52, 0 }, { 56, 52, 53, 4 }, { 52, 56, 54, 4 }, { 69, 48, 55, 21 },
- { 56, 56, 56, 0 }, { 60, 56, 57, 4 }, { 56, 60, 58, 4 }, { 65, 56, 59, 9 }, { 60, 60, 60, 0 }, { 65, 60, 61, 5 }, { 56, 65, 62, 9 }, { 60, 65, 63, 5 },
- { 56, 69, 64, 13 }, { 65, 65, 65, 0 }, { 69, 65, 66, 4 }, { 65, 69, 67, 4 }, { 60, 73, 68, 13 }, { 69, 69, 69, 0 }, { 73, 69, 70, 4 }, { 69, 73, 71, 4 },
- { 56, 81, 72, 25 }, { 73, 73, 73, 0 }, { 77, 73, 74, 4 }, { 73, 77, 75, 4 }, { 60, 85, 76, 25 }, { 77, 77, 77, 0 }, { 81, 77, 78, 4 }, { 77, 81, 79, 4 },
- { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 85, 81, 82, 4 }, { 81, 85, 83, 4 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 89, 85, 86, 4 }, { 85, 89, 87, 4 },
- { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 93, 89, 90, 4 }, { 89, 93, 91, 4 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 97, 93, 94, 4 }, { 93, 97, 95, 4 },
- { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 101, 97, 98, 4 }, { 97, 101, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 105, 101, 102, 4 }, { 101, 105, 103, 4 },
- { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 109, 105, 106, 4 }, { 105, 109, 107, 4 }, { 130, 97, 108, 33 }, { 109, 109, 109, 0 }, { 113, 109, 110, 4 }, { 109, 113, 111, 4 },
- { 134, 101, 112, 33 }, { 113, 113, 113, 0 }, { 117, 113, 114, 4 }, { 113, 117, 115, 4 }, { 130, 109, 116, 21 }, { 117, 117, 117, 0 }, { 121, 117, 118, 4 }, { 117, 121, 119, 4 },
- { 134, 113, 120, 21 }, { 121, 121, 121, 0 }, { 125, 121, 122, 4 }, { 121, 125, 123, 4 }, { 130, 121, 124, 9 }, { 125, 125, 125, 0 }, { 130, 125, 126, 5 }, { 121, 130, 127, 9 },
- { 125, 130, 128, 5 }, { 121, 134, 129, 13 }, { 130, 130, 130, 0 }, { 134, 130, 131, 4 }, { 130, 134, 132, 4 }, { 125, 138, 133, 13 }, { 134, 134, 134, 0 }, { 138, 134, 135, 4 },
- { 134, 138, 136, 4 }, { 121, 146, 137, 25 }, { 138, 138, 138, 0 }, { 142, 138, 139, 4 }, { 138, 142, 140, 4 }, { 125, 150, 141, 25 }, { 142, 142, 142, 0 }, { 146, 142, 143, 4 },
- { 142, 146, 144, 4 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 150, 146, 147, 4 }, { 146, 150, 148, 4 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 }, { 154, 150, 151, 4 },
- { 150, 154, 152, 4 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 158, 154, 155, 4 }, { 154, 158, 156, 4 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 }, { 162, 158, 159, 4 },
- { 158, 162, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 166, 162, 163, 4 }, { 162, 166, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 170, 166, 167, 4 },
- { 166, 170, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 174, 170, 171, 4 }, { 170, 174, 172, 4 }, { 195, 162, 173, 33 }, { 174, 174, 174, 0 }, { 178, 174, 175, 4 },
- { 174, 178, 176, 4 }, { 199, 166, 177, 33 }, { 178, 178, 178, 0 }, { 182, 178, 179, 4 }, { 178, 182, 180, 4 }, { 195, 174, 181, 21 }, { 182, 182, 182, 0 }, { 186, 182, 183, 4 },
- { 182, 186, 184, 4 }, { 199, 178, 185, 21 }, { 186, 186, 186, 0 }, { 190, 186, 187, 4 }, { 186, 190, 188, 4 }, { 195, 186, 189, 9 }, { 190, 190, 190, 0 }, { 195, 190, 191, 5 },
- { 186, 195, 192, 9 }, { 190, 195, 193, 5 }, { 186, 199, 194, 13 }, { 195, 195, 195, 0 }, { 199, 195, 196, 4 }, { 195, 199, 197, 4 }, { 190, 203, 198, 13 }, { 199, 199, 199, 0 },
- { 203, 199, 200, 4 }, { 199, 203, 201, 4 }, { 186, 211, 202, 25 }, { 203, 203, 203, 0 }, { 207, 203, 204, 4 }, { 203, 207, 205, 4 }, { 190, 215, 206, 25 }, { 207, 207, 207, 0 },
- { 211, 207, 208, 4 }, { 207, 211, 209, 4 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 215, 211, 212, 4 }, { 211, 215, 213, 4 }, { 215, 215, 215, 0 }, { 215, 215, 215, 0 },
- { 219, 215, 216, 4 }, { 215, 219, 217, 4 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 223, 219, 220, 4 }, { 219, 223, 221, 4 }, { 223, 223, 223, 0 }, { 223, 223, 223, 0 },
- { 227, 223, 224, 4 }, { 223, 227, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 231, 227, 228, 4 }, { 227, 231, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
- { 235, 231, 232, 4 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 239, 235, 236, 4 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
- { 243, 239, 240, 4 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 247, 243, 244, 4 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 251, 247, 248, 4 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 255, 251, 252, 4 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor5_2_p[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 41, 32, 17 },
- { 24, 41, 32, 17 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 24, 49, 36, 25 }, { 24, 49, 36, 25 }, { 33, 41, 37, 8 }, { 33, 41, 37, 8 }, { 24, 57, 40, 33 },
- { 24, 57, 40, 33 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 49, 49, 49, 0 },
- { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 57, 57, 57, 0 },
- { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 },
- { 57, 74, 65, 17 }, { 57, 74, 65, 17 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 57, 82, 69, 25 }, { 57, 82, 69, 25 }, { 66, 74, 70, 8 }, { 66, 74, 70, 8 },
- { 57, 90, 73, 33 }, { 57, 90, 73, 33 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 },
- { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 },
- { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 },
- { 90, 99, 94, 9 }, { 90, 107, 98, 17 }, { 90, 107, 98, 17 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 90, 115, 102, 25 }, { 90, 115, 102, 25 }, { 99, 107, 103, 8 },
- { 99, 107, 103, 8 }, { 90, 123, 106, 33 }, { 90, 123, 106, 33 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 111, 8 }, { 107, 115, 111, 8 },
- { 107, 115, 111, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 119, 8 }, { 115, 123, 119, 8 },
- { 115, 123, 119, 8 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 127, 9 }, { 123, 132, 127, 9 },
- { 123, 132, 127, 9 }, { 123, 132, 127, 9 }, { 123, 140, 131, 17 }, { 123, 140, 131, 17 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 123, 148, 135, 25 }, { 123, 148, 135, 25 },
- { 132, 140, 136, 8 }, { 132, 140, 136, 8 }, { 123, 156, 139, 33 }, { 123, 156, 139, 33 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 144, 8 },
- { 140, 148, 144, 8 }, { 140, 148, 144, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 152, 8 },
- { 148, 156, 152, 8 }, { 148, 156, 152, 8 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 160, 9 },
- { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 173, 164, 17 }, { 156, 173, 164, 17 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 156, 181, 168, 25 },
- { 156, 181, 168, 25 }, { 165, 173, 169, 8 }, { 165, 173, 169, 8 }, { 156, 189, 172, 33 }, { 156, 189, 172, 33 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 },
- { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 },
- { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 },
- { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 206, 197, 17 }, { 189, 206, 197, 17 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
- { 189, 214, 201, 25 }, { 189, 214, 201, 25 }, { 198, 206, 202, 8 }, { 198, 206, 202, 8 }, { 189, 222, 205, 33 }, { 189, 222, 205, 33 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
- { 206, 206, 206, 0 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
- { 214, 214, 214, 0 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
- { 222, 222, 222, 0 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 239, 230, 17 }, { 222, 239, 230, 17 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 222, 247, 234, 25 }, { 222, 247, 234, 25 }, { 231, 239, 235, 8 }, { 231, 239, 235, 8 }, { 222, 255, 238, 33 }, { 222, 255, 238, 33 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 239, 239, 239, 0 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
-
- SingleColorTableEntry g_singleColor6_2_p[256] =
- {
- { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
- { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
- { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 22, 4 }, { 24, 24, 24, 0 },
- { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 28, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 32, 30, 4 }, { 32, 32, 32, 0 },
- { 32, 32, 32, 0 }, { 32, 32, 32, 0 }, { 32, 36, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 40, 38, 4 }, { 40, 40, 40, 0 },
- { 40, 40, 40, 0 }, { 40, 40, 40, 0 }, { 40, 44, 42, 4 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 48, 46, 4 }, { 48, 48, 48, 0 },
- { 48, 48, 48, 0 }, { 48, 48, 48, 0 }, { 48, 52, 50, 4 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 56, 54, 4 }, { 56, 56, 56, 0 },
- { 56, 56, 56, 0 }, { 56, 56, 56, 0 }, { 56, 60, 58, 4 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 65, 62, 5 }, { 60, 65, 62, 5 },
- { 60, 69, 64, 9 }, { 65, 65, 65, 0 }, { 60, 73, 66, 13 }, { 65, 69, 67, 4 }, { 60, 77, 68, 17 }, { 69, 69, 69, 0 }, { 60, 81, 70, 21 }, { 69, 73, 71, 4 },
- { 60, 85, 72, 25 }, { 73, 73, 73, 0 }, { 60, 89, 74, 29 }, { 73, 77, 75, 4 }, { 60, 93, 76, 33 }, { 77, 77, 77, 0 }, { 77, 77, 77, 0 }, { 77, 81, 79, 4 },
- { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 81, 85, 83, 4 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 85, 89, 87, 4 },
- { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 89, 93, 91, 4 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 97, 95, 4 },
- { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 101, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 105, 103, 4 },
- { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 109, 107, 4 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 113, 111, 4 },
- { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 117, 115, 4 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 121, 119, 4 },
- { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 125, 123, 4 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 130, 127, 5 },
- { 125, 130, 127, 5 }, { 125, 134, 129, 9 }, { 130, 130, 130, 0 }, { 125, 138, 131, 13 }, { 130, 134, 132, 4 }, { 125, 142, 133, 17 }, { 134, 134, 134, 0 }, { 125, 146, 135, 21 },
- { 134, 138, 136, 4 }, { 125, 150, 137, 25 }, { 138, 138, 138, 0 }, { 125, 154, 139, 29 }, { 138, 142, 140, 4 }, { 125, 158, 141, 33 }, { 142, 142, 142, 0 }, { 142, 142, 142, 0 },
- { 142, 146, 144, 4 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 146, 150, 148, 4 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 },
- { 150, 154, 152, 4 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 154, 158, 156, 4 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 },
- { 158, 162, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 166, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 },
- { 166, 170, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 174, 172, 4 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 },
- { 174, 178, 176, 4 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 182, 180, 4 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 },
- { 182, 186, 184, 4 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 190, 188, 4 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 },
- { 190, 195, 192, 5 }, { 190, 195, 192, 5 }, { 190, 199, 194, 9 }, { 195, 195, 195, 0 }, { 190, 203, 196, 13 }, { 195, 199, 197, 4 }, { 190, 207, 198, 17 }, { 199, 199, 199, 0 },
- { 190, 211, 200, 21 }, { 199, 203, 201, 4 }, { 190, 215, 202, 25 }, { 203, 203, 203, 0 }, { 190, 219, 204, 29 }, { 203, 207, 205, 4 }, { 190, 223, 206, 33 }, { 207, 207, 207, 0 },
- { 207, 207, 207, 0 }, { 207, 211, 209, 4 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 211, 215, 213, 4 }, { 215, 215, 215, 0 }, { 215, 215, 215, 0 },
- { 215, 215, 215, 0 }, { 215, 219, 217, 4 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 219, 223, 221, 4 }, { 223, 223, 223, 0 }, { 223, 223, 223, 0 },
- { 223, 223, 223, 0 }, { 223, 227, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 231, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
- { 231, 231, 231, 0 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
- { 239, 239, 239, 0 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
- { 247, 247, 247, 0 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
- };
- }
-
- class S3TCComputer
- {
- public:
- typedef ParallelMath::Float MFloat;
- typedef ParallelMath::SInt16 MSInt16;
- typedef ParallelMath::UInt15 MUInt15;
- typedef ParallelMath::UInt16 MUInt16;
- typedef ParallelMath::SInt32 MSInt32;
-
- static void Init(MFloat& error)
- {
- error = ParallelMath::MakeFloat(FLT_MAX);
- }
-
- static void QuantizeTo6Bits(MUInt15& v)
- {
- MUInt15 reduced = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(v, ParallelMath::MakeUInt15(253)) + ParallelMath::MakeUInt16(512), 10));
- v = (reduced << 2) | ParallelMath::RightShift(reduced, 4);
- }
-
- static void QuantizeTo5Bits(MUInt15& v)
- {
- MUInt15 reduced = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(v, ParallelMath::MakeUInt15(249)) + ParallelMath::MakeUInt16(1024), 11));
- v = (reduced << 3) | ParallelMath::RightShift(reduced, 2);
- }
-
- static void QuantizeTo565(MUInt15 endPoint[3])
- {
- QuantizeTo5Bits(endPoint[0]);
- QuantizeTo6Bits(endPoint[1]);
- QuantizeTo5Bits(endPoint[2]);
- }
-
- static MFloat ParanoidFactorForSpan(const MSInt16& span)
- {
- return ParallelMath::Abs(ParallelMath::ToFloat(span)) * 0.03f;
- }
-
- static MFloat ParanoidDiff(const MUInt15& a, const MUInt15& b, const MFloat& d)
- {
- MFloat absDiff = ParallelMath::Abs(ParallelMath::ToFloat(ParallelMath::LosslessCast<MSInt16>::Cast(a) - ParallelMath::LosslessCast<MSInt16>::Cast(b)));
- absDiff = absDiff + d;
- return absDiff * absDiff;
- }
-
- static void TestSingleColor(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], int range, const float* channelWeights,
- MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- float channelWeightsSq[3];
-
- for (int ch = 0; ch < 3; ch++)
- channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
-
- MUInt15 totals[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 3; ch++)
- totals[ch] = totals[ch] + pixels[px][ch];
- }
-
- MUInt15 average[3];
- for (int ch = 0; ch < 3; ch++)
- average[ch] = ParallelMath::RightShift(totals[ch] + ParallelMath::MakeUInt15(8), 4);
-
- const S3TCSingleColorTables::SingleColorTableEntry* rbTable = NULL;
- const S3TCSingleColorTables::SingleColorTableEntry* gTable = NULL;
- if (flags & cvtt::Flags::S3TC_Paranoid)
- {
- if (range == 4)
- {
- rbTable = S3TCSingleColorTables::g_singleColor5_3_p;
- gTable = S3TCSingleColorTables::g_singleColor6_3_p;
- }
- else
- {
- assert(range == 3);
- rbTable = S3TCSingleColorTables::g_singleColor5_2_p;
- gTable = S3TCSingleColorTables::g_singleColor6_2_p;
- }
- }
- else
- {
- if (range == 4)
- {
- rbTable = S3TCSingleColorTables::g_singleColor5_3;
- gTable = S3TCSingleColorTables::g_singleColor6_3;
- }
- else
- {
- assert(range == 3);
- rbTable = S3TCSingleColorTables::g_singleColor5_2;
- gTable = S3TCSingleColorTables::g_singleColor6_2;
- }
- }
-
- MUInt15 interpolated[3];
- MUInt15 eps[2][3];
- MSInt16 spans[3];
- for (int i = 0; i < ParallelMath::ParallelSize; i++)
- {
- for (int ch = 0; ch < 3; ch++)
- {
- uint16_t avg = ParallelMath::Extract(average[ch], i);
- const S3TCSingleColorTables::SingleColorTableEntry& tableEntry = ((ch == 1) ? gTable[avg] : rbTable[avg]);
- ParallelMath::PutUInt15(eps[0][ch], i, tableEntry.m_min);
- ParallelMath::PutUInt15(eps[1][ch], i, tableEntry.m_max);
- ParallelMath::PutUInt15(interpolated[ch], i, tableEntry.m_actualColor);
- ParallelMath::PutSInt16(spans[ch], i, tableEntry.m_span);
- }
- }
-
- MFloat error = ParallelMath::MakeFloatZero();
- if (flags & cvtt::Flags::S3TC_Paranoid)
- {
- MFloat spanParanoidFactors[3];
- for (int ch = 0; ch < 3; ch++)
- spanParanoidFactors[ch] = ParanoidFactorForSpan(spans[ch]);
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 3; ch++)
- error = error + ParanoidDiff(interpolated[ch], pixels[px][ch], spanParanoidFactors[ch]) * channelWeightsSq[ch];
- }
- }
- else
- {
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 3; ch++)
- error = error + ParallelMath::ToFloat(ParallelMath::SqDiffUInt8(interpolated[ch], pixels[px][ch])) * channelWeightsSq[ch];
- }
- }
-
- ParallelMath::FloatCompFlag better = ParallelMath::Less(error, bestError);
- ParallelMath::Int16CompFlag better16 = ParallelMath::FloatFlagToInt16(better);
-
- if (ParallelMath::AnySet(better16))
- {
- bestError = ParallelMath::Min(bestError, error);
- for (int epi = 0; epi < 2; epi++)
- for (int ch = 0; ch < 3; ch++)
- ParallelMath::ConditionalSet(bestEndpoints[epi][ch], better16, eps[epi][ch]);
-
- MUInt15 vindexes = ParallelMath::MakeUInt15(1);
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestIndexes[px], better16, vindexes);
-
- ParallelMath::ConditionalSet(bestRange, better16, ParallelMath::MakeUInt15(range));
- }
- }
-
- static void TestEndpoints(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], const MUInt15 unquantizedEndPoints[2][3], int range, const float* channelWeights,
- MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, EndpointRefiner<3> *refiner, const ParallelMath::RoundTowardNearestForScope *rtn)
- {
- float channelWeightsSq[3];
-
- for (int ch = 0; ch < 3; ch++)
- channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
-
- MUInt15 endPoints[2][3];
-
- for (int ep = 0; ep < 2; ep++)
- for (int ch = 0; ch < 3; ch++)
- endPoints[ep][ch] = unquantizedEndPoints[ep][ch];
-
- QuantizeTo565(endPoints[0]);
- QuantizeTo565(endPoints[1]);
-
- IndexSelector<3> selector;
- selector.Init<false>(channelWeights, endPoints, range);
-
- MUInt15 indexes[16];
-
- MFloat paranoidFactors[3];
- for (int ch = 0; ch < 3; ch++)
- paranoidFactors[ch] = ParanoidFactorForSpan(ParallelMath::LosslessCast<MSInt16>::Cast(endPoints[0][ch]) - ParallelMath::LosslessCast<MSInt16>::Cast(endPoints[1][ch]));
-
- MFloat error = ParallelMath::MakeFloatZero();
- AggregatedError<3> aggError;
- for (int px = 0; px < 16; px++)
- {
- MUInt15 index = selector.SelectIndexLDR(floatPixels[px], rtn);
- indexes[px] = index;
-
- if (refiner)
- refiner->ContributeUnweightedPW(preWeightedPixels[px], index);
-
- MUInt15 reconstructed[3];
- selector.ReconstructLDRPrecise(index, reconstructed);
-
- if (flags & Flags::S3TC_Paranoid)
- {
- for (int ch = 0; ch < 3; ch++)
- error = error + ParanoidDiff(reconstructed[ch], pixels[px][ch], paranoidFactors[ch]) * channelWeightsSq[ch];
- }
- else
- BCCommon::ComputeErrorLDR<3>(flags, reconstructed, pixels[px], aggError);
- }
-
- if (!(flags & Flags::S3TC_Paranoid))
- error = aggError.Finalize(flags, channelWeightsSq);
-
- ParallelMath::FloatCompFlag better = ParallelMath::Less(error, bestError);
-
- if (ParallelMath::AnySet(better))
- {
- ParallelMath::Int16CompFlag betterInt16 = ParallelMath::FloatFlagToInt16(better);
-
- ParallelMath::ConditionalSet(bestError, better, error);
-
- for (int ep = 0; ep < 2; ep++)
- for (int ch = 0; ch < 3; ch++)
- ParallelMath::ConditionalSet(bestEndpoints[ep][ch], betterInt16, endPoints[ep][ch]);
-
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestIndexes[px], betterInt16, indexes[px]);
-
- ParallelMath::ConditionalSet(bestRange, betterInt16, ParallelMath::MakeUInt15(static_cast<uint16_t>(range)));
- }
- }
-
- static void TestCounts(uint32_t flags, const int *counts, int nCounts, const MUInt15 &numElements, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], bool alphaTest,
- const MFloat floatSortedInputs[16][4], const MFloat preWeightedFloatSortedInputs[16][4], const float *channelWeights, MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange,
- const ParallelMath::RoundTowardNearestForScope* rtn)
- {
- UNREFERENCED_PARAMETER(alphaTest);
- UNREFERENCED_PARAMETER(flags);
-
- EndpointRefiner<3> refiner;
-
- refiner.Init(nCounts, channelWeights);
-
- bool escape = false;
- int e = 0;
- for (int i = 0; i < nCounts; i++)
- {
- for (int n = 0; n < counts[i]; n++)
- {
- ParallelMath::Int16CompFlag valid = ParallelMath::Less(ParallelMath::MakeUInt15(static_cast<uint16_t>(n)), numElements);
- if (!ParallelMath::AnySet(valid))
- {
- escape = true;
- break;
- }
-
- if (ParallelMath::AllSet(valid))
- refiner.ContributeUnweightedPW(preWeightedFloatSortedInputs[e++], ParallelMath::MakeUInt15(static_cast<uint16_t>(i)));
- else
- {
- MFloat weight = ParallelMath::Select(ParallelMath::Int16FlagToFloat(valid), ParallelMath::MakeFloat(1.0f), ParallelMath::MakeFloat(0.0f));
- refiner.ContributePW(preWeightedFloatSortedInputs[e++], ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), weight);
- }
- }
-
- if (escape)
- break;
- }
-
- MUInt15 endPoints[2][3];
- refiner.GetRefinedEndpointsLDR(endPoints, rtn);
-
- TestEndpoints(flags, pixels, floatPixels, preWeightedPixels, endPoints, nCounts, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, NULL, rtn);
- }
-
- static void PackExplicitAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride)
- {
- UNREFERENCED_PARAMETER(flags);
- ParallelMath::RoundTowardNearestForScope rtn;
-
- float weights[1] = { 1.0f };
-
- MUInt15 pixels[16];
- MFloat floatPixels[16];
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::ConvertLDRInputs(inputs, px, inputChannel, pixels[px]);
- floatPixels[px] = ParallelMath::ToFloat(pixels[px]);
- }
-
- MUInt15 ep[2][1] = { { ParallelMath::MakeUInt15(0) },{ ParallelMath::MakeUInt15(255) } };
-
- IndexSelector<1> selector;
- selector.Init<false>(weights, ep, 16);
-
- MUInt15 indexes[16];
-
- for (int px = 0; px < 16; px++)
- indexes[px] = selector.SelectIndexLDR(&floatPixels[px], &rtn);
-
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- for (int px = 0; px < 16; px += 8)
- {
- int index0 = ParallelMath::Extract(indexes[px], block);
- int index1 = ParallelMath::Extract(indexes[px], block);
-
- packedBlocks[px / 2] = static_cast<uint8_t>(index0 | (index1 << 4));
- }
-
- packedBlocks += packedBlockStride;
- }
- }
-
- static void PackInterpolatedAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride, bool isSigned, int maxTweakRounds, int numRefineRounds)
- {
- if (maxTweakRounds < 1)
- maxTweakRounds = 1;
-
- if (numRefineRounds < 1)
- numRefineRounds = 1;
-
- ParallelMath::RoundTowardNearestForScope rtn;
-
- float oneWeight[1] = { 1.0f };
-
- MUInt15 pixels[16];
- MFloat floatPixels[16];
-
- MUInt15 highTerminal = isSigned ? ParallelMath::MakeUInt15(254) : ParallelMath::MakeUInt15(255);
- MUInt15 highTerminalMinusOne = highTerminal - ParallelMath::MakeUInt15(1);
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::ConvertLDRInputs(inputs, px, inputChannel, pixels[px]);
-
- if (isSigned)
- pixels[px] = ParallelMath::Min(pixels[px], highTerminal);
-
- floatPixels[px] = ParallelMath::ToFloat(pixels[px]);
- }
-
- MUInt15 sortedPixels[16];
- for (int px = 0; px < 16; px++)
- sortedPixels[px] = pixels[px];
-
- for (int sortEnd = 15; sortEnd > 0; sortEnd--)
- {
- for (int sortOffset = 0; sortOffset < sortEnd; sortOffset++)
- {
- MUInt15 a = sortedPixels[sortOffset];
- MUInt15 b = sortedPixels[sortOffset + 1];
-
- sortedPixels[sortOffset] = ParallelMath::Min(a, b);
- sortedPixels[sortOffset + 1] = ParallelMath::Max(a, b);
- }
- }
-
- MUInt15 zero = ParallelMath::MakeUInt15(0);
- MUInt15 one = ParallelMath::MakeUInt15(1);
-
- MUInt15 bestIsFullRange = zero;
- MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
- MUInt15 bestEP[2] = { zero, zero };
- MUInt15 bestIndexes[16] = {
- zero, zero, zero, zero,
- zero, zero, zero, zero,
- zero, zero, zero, zero,
- zero, zero, zero, zero
- };
-
- // Full-precision
- {
- MUInt15 minEP = sortedPixels[0];
- MUInt15 maxEP = sortedPixels[15];
-
- MFloat base[1] = { ParallelMath::ToFloat(minEP) };
- MFloat offset[1] = { ParallelMath::ToFloat(maxEP - minEP) };
-
- UnfinishedEndpoints<1> ufep = UnfinishedEndpoints<1>(base, offset);
-
- int numTweakRounds = BCCommon::TweakRoundsForRange(8);
- if (numTweakRounds > maxTweakRounds)
- numTweakRounds = maxTweakRounds;
-
- for (int tweak = 0; tweak < numTweakRounds; tweak++)
- {
- MUInt15 ep[2][1];
-
- ufep.FinishLDR(tweak, 8, ep[0], ep[1]);
-
- for (int refinePass = 0; refinePass < numRefineRounds; refinePass++)
- {
- EndpointRefiner<1> refiner;
- refiner.Init(8, oneWeight);
-
- if (isSigned)
- for (int epi = 0; epi < 2; epi++)
- ep[epi][0] = ParallelMath::Min(ep[epi][0], highTerminal);
-
- IndexSelector<1> indexSelector;
- indexSelector.Init<false>(oneWeight, ep, 8);
-
- MUInt15 indexes[16];
-
- AggregatedError<1> aggError;
- for (int px = 0; px < 16; px++)
- {
- MUInt15 index = indexSelector.SelectIndexLDR(&floatPixels[px], &rtn);
-
- MUInt15 reconstructedPixel;
-
- indexSelector.ReconstructLDRPrecise(index, &reconstructedPixel);
- BCCommon::ComputeErrorLDR<1>(flags, &reconstructedPixel, &pixels[px], aggError);
-
- if (refinePass != numRefineRounds - 1)
- refiner.ContributeUnweightedPW(&floatPixels[px], index);
-
- indexes[px] = index;
- }
- MFloat error = aggError.Finalize(flags | Flags::Uniform, oneWeight);
-
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
- ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
-
- if (ParallelMath::AnySet(errorBetter16))
- {
- bestError = ParallelMath::Min(error, bestError);
- ParallelMath::ConditionalSet(bestIsFullRange, errorBetter16, one);
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestIndexes[px], errorBetter16, indexes[px]);
-
- for (int epi = 0; epi < 2; epi++)
- ParallelMath::ConditionalSet(bestEP[epi], errorBetter16, ep[epi][0]);
- }
-
- if (refinePass != numRefineRounds - 1)
- refiner.GetRefinedEndpointsLDR(ep, &rtn);
- }
- }
- }
-
- // Reduced precision with special endpoints
- {
- MUInt15 bestHeuristicMin = sortedPixels[0];
- MUInt15 bestHeuristicMax = sortedPixels[15];
-
- ParallelMath::Int16CompFlag canTryClipping;
-
- // In reduced precision, we want try putting endpoints at the reserved indexes at the ends.
- // The heuristic we use is to assign indexes to the end as long as they aren't off by more than half of the index range.
- // This will usually not find anything, but it's cheap to check.
-
- {
- MUInt15 largestPossibleRange = bestHeuristicMax - bestHeuristicMin; // Max: 255
- MUInt15 lowestPossibleClearance = ParallelMath::Min(bestHeuristicMin, static_cast<MUInt15>(highTerminal - bestHeuristicMax));
-
- MUInt15 lowestPossibleClearanceTimes10 = (lowestPossibleClearance << 2) + (lowestPossibleClearance << 4);
- canTryClipping = ParallelMath::LessOrEqual(lowestPossibleClearanceTimes10, largestPossibleRange);
- }
-
- if (ParallelMath::AnySet(canTryClipping))
- {
- MUInt15 lowClearances[16];
- MUInt15 highClearances[16];
- MUInt15 bestSkipCount = ParallelMath::MakeUInt15(0);
-
- lowClearances[0] = highClearances[0] = ParallelMath::MakeUInt15(0);
-
- for (int px = 1; px < 16; px++)
- {
- lowClearances[px] = sortedPixels[px - 1];
- highClearances[px] = highTerminal - sortedPixels[16 - px];
- }
-
- for (uint16_t firstIndex = 0; firstIndex < 16; firstIndex++)
- {
- uint16_t numSkippedLow = firstIndex;
-
- MUInt15 lowClearance = lowClearances[firstIndex];
-
- for (uint16_t lastIndex = firstIndex; lastIndex < 16; lastIndex++)
- {
- uint16_t numSkippedHigh = 15 - lastIndex;
- uint16_t numSkipped = numSkippedLow + numSkippedHigh;
-
- MUInt15 numSkippedV = ParallelMath::MakeUInt15(numSkipped);
-
- ParallelMath::Int16CompFlag areMoreSkipped = ParallelMath::Less(bestSkipCount, numSkippedV);
-
- if (!ParallelMath::AnySet(areMoreSkipped))
- continue;
-
- MUInt15 clearance = ParallelMath::Max(highClearances[numSkippedHigh], lowClearance);
- MUInt15 clearanceTimes10 = (clearance << 2) + (clearance << 4);
-
- MUInt15 range = sortedPixels[lastIndex] - sortedPixels[firstIndex];
-
- ParallelMath::Int16CompFlag isBetter = (areMoreSkipped & ParallelMath::LessOrEqual(clearanceTimes10, range));
- ParallelMath::ConditionalSet(bestHeuristicMin, isBetter, sortedPixels[firstIndex]);
- ParallelMath::ConditionalSet(bestHeuristicMax, isBetter, sortedPixels[lastIndex]);
- }
- }
- }
-
- MUInt15 bestSimpleMin = one;
- MUInt15 bestSimpleMax = highTerminalMinusOne;
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::ConditionalSet(bestSimpleMin, ParallelMath::Less(zero, sortedPixels[15 - px]), sortedPixels[15 - px]);
- ParallelMath::ConditionalSet(bestSimpleMax, ParallelMath::Less(sortedPixels[px], highTerminal), sortedPixels[px]);
- }
-
- MUInt15 minEPs[2] = { bestSimpleMin, bestHeuristicMin };
- MUInt15 maxEPs[2] = { bestSimpleMax, bestHeuristicMax };
-
- int minEPRange = 2;
- if (ParallelMath::AllSet(ParallelMath::Equal(minEPs[0], minEPs[1])))
- minEPRange = 1;
-
- int maxEPRange = 2;
- if (ParallelMath::AllSet(ParallelMath::Equal(maxEPs[0], maxEPs[1])))
- maxEPRange = 1;
-
- for (int minEPIndex = 0; minEPIndex < minEPRange; minEPIndex++)
- {
- for (int maxEPIndex = 0; maxEPIndex < maxEPRange; maxEPIndex++)
- {
- MFloat base[1] = { ParallelMath::ToFloat(minEPs[minEPIndex]) };
- MFloat offset[1] = { ParallelMath::ToFloat(maxEPs[maxEPIndex] - minEPs[minEPIndex]) };
-
- UnfinishedEndpoints<1> ufep = UnfinishedEndpoints<1>(base, offset);
-
- int numTweakRounds = BCCommon::TweakRoundsForRange(6);
- if (numTweakRounds > maxTweakRounds)
- numTweakRounds = maxTweakRounds;
-
- for (int tweak = 0; tweak < numTweakRounds; tweak++)
- {
- MUInt15 ep[2][1];
-
- ufep.FinishLDR(tweak, 8, ep[0], ep[1]);
-
- for (int refinePass = 0; refinePass < numRefineRounds; refinePass++)
- {
- EndpointRefiner<1> refiner;
- refiner.Init(6, oneWeight);
-
- if (isSigned)
- for (int epi = 0; epi < 2; epi++)
- ep[epi][0] = ParallelMath::Min(ep[epi][0], highTerminal);
-
- IndexSelector<1> indexSelector;
- indexSelector.Init<false>(oneWeight, ep, 6);
-
- MUInt15 indexes[16];
- MFloat error = ParallelMath::MakeFloatZero();
-
- for (int px = 0; px < 16; px++)
- {
- MUInt15 selectedIndex = indexSelector.SelectIndexLDR(&floatPixels[px], &rtn);
-
- MUInt15 reconstructedPixel;
-
- indexSelector.ReconstructLDRPrecise(selectedIndex, &reconstructedPixel);
-
- MFloat zeroError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &zero, &pixels[px], 1, oneWeight);
- MFloat highTerminalError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &highTerminal, &pixels[px], 1, oneWeight);
- MFloat selectedIndexError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &reconstructedPixel, &pixels[px], 1, oneWeight);
-
- MFloat bestPixelError = zeroError;
- MUInt15 index = ParallelMath::MakeUInt15(6);
-
- ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(ParallelMath::Less(highTerminalError, bestPixelError)), ParallelMath::MakeUInt15(7));
- bestPixelError = ParallelMath::Min(bestPixelError, highTerminalError);
-
- ParallelMath::FloatCompFlag selectedIndexBetter = ParallelMath::Less(selectedIndexError, bestPixelError);
-
- if (ParallelMath::AllSet(selectedIndexBetter))
- {
- if (refinePass != numRefineRounds - 1)
- refiner.ContributeUnweightedPW(&floatPixels[px], selectedIndex);
- }
- else
- {
- MFloat refineWeight = ParallelMath::Select(selectedIndexBetter, ParallelMath::MakeFloat(1.0f), ParallelMath::MakeFloatZero());
-
- if (refinePass != numRefineRounds - 1)
- refiner.ContributePW(&floatPixels[px], selectedIndex, refineWeight);
- }
-
- ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(selectedIndexBetter), selectedIndex);
- bestPixelError = ParallelMath::Min(bestPixelError, selectedIndexError);
-
- error = error + bestPixelError;
-
- indexes[px] = index;
- }
-
- ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
- ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
-
- if (ParallelMath::AnySet(errorBetter16))
- {
- bestError = ParallelMath::Min(error, bestError);
- ParallelMath::ConditionalSet(bestIsFullRange, errorBetter16, zero);
- for (int px = 0; px < 16; px++)
- ParallelMath::ConditionalSet(bestIndexes[px], errorBetter16, indexes[px]);
-
- for (int epi = 0; epi < 2; epi++)
- ParallelMath::ConditionalSet(bestEP[epi], errorBetter16, ep[epi][0]);
- }
-
- if (refinePass != numRefineRounds - 1)
- refiner.GetRefinedEndpointsLDR(ep, &rtn);
- }
- }
- }
- }
- }
-
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- int ep0 = ParallelMath::Extract(bestEP[0], block);
- int ep1 = ParallelMath::Extract(bestEP[1], block);
- int isFullRange = ParallelMath::Extract(bestIsFullRange, block);
-
- if (isSigned)
- {
- ep0 -= 127;
- ep1 -= 127;
-
- assert(ep0 >= -127 && ep0 <= 127);
- assert(ep1 >= -127 && ep1 <= 127);
- }
-
-
- bool swapEndpoints = (isFullRange != 0) != (ep0 > ep1);
-
- if (swapEndpoints)
- std::swap(ep0, ep1);
-
- uint16_t dumpBits = 0;
- int dumpBitsOffset = 0;
- int dumpByteOffset = 2;
- packedBlocks[0] = static_cast<uint8_t>(ep0 & 0xff);
- packedBlocks[1] = static_cast<uint8_t>(ep1 & 0xff);
-
- int maxValue = (isFullRange != 0) ? 7 : 5;
-
- for (int px = 0; px < 16; px++)
- {
- int index = ParallelMath::Extract(bestIndexes[px], block);
-
- if (swapEndpoints && index <= maxValue)
- index = maxValue - index;
-
- if (index != 0)
- {
- if (index == maxValue)
- index = 1;
- else if (index < maxValue)
- index++;
- }
-
- assert(index >= 0 && index < 8);
-
- dumpBits |= static_cast<uint16_t>(index << dumpBitsOffset);
- dumpBitsOffset += 3;
-
- if (dumpBitsOffset >= 8)
- {
- assert(dumpByteOffset < 8);
- packedBlocks[dumpByteOffset] = static_cast<uint8_t>(dumpBits & 0xff);
- dumpBits >>= 8;
- dumpBitsOffset -= 8;
- dumpByteOffset++;
- }
- }
-
- assert(dumpBitsOffset == 0);
- assert(dumpByteOffset == 8);
-
- packedBlocks += packedBlockStride;
- }
- }
-
- static void PackRGB(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, size_t packedBlockStride, const float channelWeights[4], bool alphaTest, float alphaThreshold, bool exhaustive, int maxTweakRounds, int numRefineRounds)
- {
- ParallelMath::RoundTowardNearestForScope rtn;
-
- if (numRefineRounds < 1)
- numRefineRounds = 1;
-
- if (maxTweakRounds < 1)
- maxTweakRounds = 1;
-
- EndpointSelector<3, 8> endpointSelector;
-
- MUInt15 pixels[16][4];
- MFloat floatPixels[16][4];
-
- MFloat preWeightedPixels[16][4];
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 4; ch++)
- ParallelMath::ConvertLDRInputs(inputs, px, ch, pixels[px][ch]);
- }
-
- for (int px = 0; px < 16; px++)
- {
- for (int ch = 0; ch < 4; ch++)
- floatPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]);
- }
-
- if (alphaTest)
- {
- MUInt15 threshold = ParallelMath::MakeUInt15(static_cast<uint16_t>(floor(alphaThreshold * 255.0f + 0.5f)));
-
- for (int px = 0; px < 16; px++)
- {
- ParallelMath::Int16CompFlag belowThreshold = ParallelMath::Less(pixels[px][3], threshold);
- pixels[px][3] = ParallelMath::Select(belowThreshold, ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(255));
- }
- }
-
- BCCommon::PreWeightPixelsLDR<4>(preWeightedPixels, pixels, channelWeights);
-
- MUInt15 minAlpha = ParallelMath::MakeUInt15(255);
-
- for (int px = 0; px < 16; px++)
- minAlpha = ParallelMath::Min(minAlpha, pixels[px][3]);
-
- MFloat pixelWeights[16];
- for (int px = 0; px < 16; px++)
- {
- pixelWeights[px] = ParallelMath::MakeFloat(1.0f);
- if (alphaTest)
- {
- ParallelMath::Int16CompFlag isTransparent = ParallelMath::Less(pixels[px][3], ParallelMath::MakeUInt15(255));
-
- ParallelMath::ConditionalSet(pixelWeights[px], ParallelMath::Int16FlagToFloat(isTransparent), ParallelMath::MakeFloatZero());
- }
- }
-
- for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
- {
- for (int px = 0; px < 16; px++)
- endpointSelector.ContributePass(preWeightedPixels[px], pass, pixelWeights[px]);
-
- endpointSelector.FinishPass(pass);
- }
-
- UnfinishedEndpoints<3> ufep = endpointSelector.GetEndpoints(channelWeights);
-
- MUInt15 bestEndpoints[2][3];
- MUInt15 bestIndexes[16];
- MUInt15 bestRange = ParallelMath::MakeUInt15(0);
- MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
-
- for (int px = 0; px < 16; px++)
- bestIndexes[px] = ParallelMath::MakeUInt15(0);
-
- for (int ep = 0; ep < 2; ep++)
- for (int ch = 0; ch < 3; ch++)
- bestEndpoints[ep][ch] = ParallelMath::MakeUInt15(0);
-
- if (exhaustive)
- {
- MSInt16 sortBins[16];
-
- {
- // Compute an 11-bit index, change it to signed, stuff it in the high bits of the sort bins,
- // and pack the original indexes into the low bits.
-
- MUInt15 sortEP[2][3];
- ufep.FinishLDR(0, 11, sortEP[0], sortEP[1]);
-
- IndexSelector<3> sortSelector;
- sortSelector.Init<false>(channelWeights, sortEP, 1 << 11);
-
- for (int16_t px = 0; px < 16; px++)
- {
- MSInt16 sortBin = ParallelMath::LosslessCast<MSInt16>::Cast(sortSelector.SelectIndexLDR(floatPixels[px], &rtn) << 4);
-
- if (alphaTest)
- {
- ParallelMath::Int16CompFlag isTransparent = ParallelMath::Less(pixels[px][3], ParallelMath::MakeUInt15(255));
-
- ParallelMath::ConditionalSet(sortBin, isTransparent, ParallelMath::MakeSInt16(-16)); // 0xfff0
- }
-
- sortBin = sortBin + ParallelMath::MakeSInt16(px);
-
- sortBins[px] = sortBin;
- }
- }
-
- // Sort bins
- for (int sortEnd = 1; sortEnd < 16; sortEnd++)
- {
- for (int sortLoc = sortEnd; sortLoc > 0; sortLoc--)
- {
- MSInt16 a = sortBins[sortLoc];
- MSInt16 b = sortBins[sortLoc - 1];
-
- sortBins[sortLoc] = ParallelMath::Max(a, b);
- sortBins[sortLoc - 1] = ParallelMath::Min(a, b);
- }
- }
-
- MUInt15 firstElement = ParallelMath::MakeUInt15(0);
- for (uint16_t e = 0; e < 16; e++)
- {
- ParallelMath::Int16CompFlag isInvalid = ParallelMath::Less(sortBins[e], ParallelMath::MakeSInt16(0));
- ParallelMath::ConditionalSet(firstElement, isInvalid, ParallelMath::MakeUInt15(e + 1));
- if (!ParallelMath::AnySet(isInvalid))
- break;
- }
-
- MUInt15 numElements = ParallelMath::MakeUInt15(16) - firstElement;
-
- MUInt15 sortedInputs[16][4];
- MFloat floatSortedInputs[16][4];
- MFloat pwFloatSortedInputs[16][4];
-
- for (int e = 0; e < 16; e++)
- {
- for (int ch = 0; ch < 4; ch++)
- sortedInputs[e][ch] = ParallelMath::MakeUInt15(0);
- }
-
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- for (int e = ParallelMath::Extract(firstElement, block); e < 16; e++)
- {
- ParallelMath::ScalarUInt16 sortBin = ParallelMath::Extract(sortBins[e], block);
- int originalIndex = (sortBin & 15);
-
- for (int ch = 0; ch < 4; ch++)
- ParallelMath::PutUInt15(sortedInputs[15 - e][ch], block, ParallelMath::Extract(pixels[originalIndex][ch], block));
- }
- }
-
- for (int e = 0; e < 16; e++)
- {
- for (int ch = 0; ch < 4; ch++)
- {
- MFloat f = ParallelMath::ToFloat(sortedInputs[e][ch]);
- floatSortedInputs[e][ch] = f;
- pwFloatSortedInputs[e][ch] = f * channelWeights[ch];
- }
- }
-
- for (int n0 = 0; n0 <= 15; n0++)
- {
- int remainingFor1 = 16 - n0;
- if (remainingFor1 == 16)
- remainingFor1 = 15;
-
- for (int n1 = 0; n1 <= remainingFor1; n1++)
- {
- int remainingFor2 = 16 - n1 - n0;
- if (remainingFor2 == 16)
- remainingFor2 = 15;
-
- for (int n2 = 0; n2 <= remainingFor2; n2++)
- {
- int n3 = 16 - n2 - n1 - n0;
-
- if (n3 == 16)
- continue;
-
- int counts[4] = { n0, n1, n2, n3 };
-
- TestCounts(flags, counts, 4, numElements, pixels, floatPixels, preWeightedPixels, alphaTest, floatSortedInputs, pwFloatSortedInputs, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
- }
- }
- }
-
- TestSingleColor(flags, pixels, floatPixels, 4, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
-
- if (alphaTest)
- {
- for (int n0 = 0; n0 <= 15; n0++)
- {
- int remainingFor1 = 16 - n0;
- if (remainingFor1 == 16)
- remainingFor1 = 15;
-
- for (int n1 = 0; n1 <= remainingFor1; n1++)
- {
- int n2 = 16 - n1 - n0;
-
- if (n2 == 16)
- continue;
-
- int counts[3] = { n0, n1, n2 };
-
- TestCounts(flags, counts, 3, numElements, pixels, floatPixels, preWeightedPixels, alphaTest, floatSortedInputs, pwFloatSortedInputs, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
- }
- }
-
- TestSingleColor(flags, pixels, floatPixels, 3, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
- }
- }
- else
- {
- int minRange = alphaTest ? 3 : 4;
-
- for (int range = minRange; range <= 4; range++)
- {
- int tweakRounds = BCCommon::TweakRoundsForRange(range);
- if (tweakRounds > maxTweakRounds)
- tweakRounds = maxTweakRounds;
-
- for (int tweak = 0; tweak < tweakRounds; tweak++)
- {
- MUInt15 endPoints[2][3];
-
- ufep.FinishLDR(tweak, range, endPoints[0], endPoints[1]);
-
- for (int refine = 0; refine < numRefineRounds; refine++)
- {
- EndpointRefiner<3> refiner;
- refiner.Init(range, channelWeights);
-
- TestEndpoints(flags, pixels, floatPixels, preWeightedPixels, endPoints, range, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &refiner, &rtn);
-
- if (refine != numRefineRounds - 1)
- refiner.GetRefinedEndpointsLDR(endPoints, &rtn);
- }
- }
- }
- }
-
- for (int block = 0; block < ParallelMath::ParallelSize; block++)
- {
- ParallelMath::ScalarUInt16 range = ParallelMath::Extract(bestRange, block);
- assert(range == 3 || range == 4);
-
- ParallelMath::ScalarUInt16 compressedEP[2];
- for (int ep = 0; ep < 2; ep++)
- {
- ParallelMath::ScalarUInt16 endPoint[3];
- for (int ch = 0; ch < 3; ch++)
- endPoint[ch] = ParallelMath::Extract(bestEndpoints[ep][ch], block);
-
- int compressed = (endPoint[0] & 0xf8) << 8;
- compressed |= (endPoint[1] & 0xfc) << 3;
- compressed |= (endPoint[2] & 0xf8) >> 3;
-
- compressedEP[ep] = static_cast<ParallelMath::ScalarUInt16>(compressed);
- }
-
- int indexOrder[4];
-
- if (range == 4)
- {
- if (compressedEP[0] == compressedEP[1])
- {
- indexOrder[0] = 0;
- indexOrder[1] = 0;
- indexOrder[2] = 0;
- indexOrder[3] = 0;
- }
- else if (compressedEP[0] < compressedEP[1])
- {
- std::swap(compressedEP[0], compressedEP[1]);
- indexOrder[0] = 1;
- indexOrder[1] = 3;
- indexOrder[2] = 2;
- indexOrder[3] = 0;
- }
- else
- {
- indexOrder[0] = 0;
- indexOrder[1] = 2;
- indexOrder[2] = 3;
- indexOrder[3] = 1;
- }
- }
- else
- {
- assert(range == 3);
-
- if (compressedEP[0] > compressedEP[1])
- {
- std::swap(compressedEP[0], compressedEP[1]);
- indexOrder[0] = 1;
- indexOrder[1] = 2;
- indexOrder[2] = 0;
- }
- else
- {
- indexOrder[0] = 0;
- indexOrder[1] = 2;
- indexOrder[2] = 1;
- }
- indexOrder[3] = 3;
- }
-
- packedBlocks[0] = static_cast<uint8_t>(compressedEP[0] & 0xff);
- packedBlocks[1] = static_cast<uint8_t>((compressedEP[0] >> 8) & 0xff);
- packedBlocks[2] = static_cast<uint8_t>(compressedEP[1] & 0xff);
- packedBlocks[3] = static_cast<uint8_t>((compressedEP[1] >> 8) & 0xff);
-
- for (int i = 0; i < 16; i += 4)
- {
- int packedIndexes = 0;
- for (int subi = 0; subi < 4; subi++)
- {
- ParallelMath::ScalarUInt16 index = ParallelMath::Extract(bestIndexes[i + subi], block);
- packedIndexes |= (indexOrder[index] << (subi * 2));
- }
-
- packedBlocks[4 + i / 4] = static_cast<uint8_t>(packedIndexes);
- }
-
- packedBlocks += packedBlockStride;
- }
- }
- };
-
- // Signed input blocks are converted into unsigned space, with the maximum value being 254
- void BiasSignedInput(PixelBlockU8 inputNormalized[ParallelMath::ParallelSize], const PixelBlockS8 inputSigned[ParallelMath::ParallelSize])
- {
- for (size_t block = 0; block < ParallelMath::ParallelSize; block++)
- {
- const PixelBlockS8& inputSignedBlock = inputSigned[block];
- PixelBlockU8& inputNormalizedBlock = inputNormalized[block];
-
- for (size_t px = 0; px < 16; px++)
- {
- for (size_t ch = 0; ch < 4; ch++)
- inputNormalizedBlock.m_pixels[px][ch] = static_cast<uint8_t>(std::max<int>(inputSignedBlock.m_pixels[px][ch], -127) + 127);
- }
- }
- }
-
- void FillWeights(const Options &options, float channelWeights[4])
- {
- if (options.flags & Flags::Uniform)
- channelWeights[0] = channelWeights[1] = channelWeights[2] = channelWeights[3] = 1.0f;
- else
- {
- channelWeights[0] = options.redWeight;
- channelWeights[1] = options.greenWeight;
- channelWeights[2] = options.blueWeight;
- channelWeights[3] = options.alphaWeight;
- }
- }
- }
-
- namespace Kernels
- {
- void EncodeBC7(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::BC7Computer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, options.seedPoints, options.refineRoundsBC7);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC6HU(uint8_t *pBC, const PixelBlockF16 *pBlocks, const cvtt::Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::BC6HComputer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, false, options.seedPoints, options.refineRoundsBC6H);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC6HS(uint8_t *pBC, const PixelBlockF16 *pBlocks, const cvtt::Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::BC6HComputer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, true, options.seedPoints, options.refineRoundsBC6H);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC1(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC, 8, channelWeights, true, options.threshold, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
- pBC += ParallelMath::ParallelSize * 8;
- }
- }
-
- void EncodeBC2(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC + 8, 16, channelWeights, false, 1.0f, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
- Internal::S3TCComputer::PackExplicitAlpha(options.flags, pBlocks + blockBase, 3, pBC, 16);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC3(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC + 8, 16, channelWeights, false, 1.0f, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 3, pBC, 16, false, options.seedPoints, options.refineRoundsIIC);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC4U(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 0, pBC, 8, false, options.seedPoints, options.refineRoundsIIC);
- pBC += ParallelMath::ParallelSize * 8;
- }
- }
-
- void EncodeBC4S(uint8_t *pBC, const PixelBlockS8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- PixelBlockU8 inputBlocks[ParallelMath::ParallelSize];
- Internal::BiasSignedInput(inputBlocks, pBlocks + blockBase);
-
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 0, pBC, 8, true, options.seedPoints, options.refineRoundsIIC);
- pBC += ParallelMath::ParallelSize * 8;
- }
- }
-
- void EncodeBC5U(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 0, pBC, 16, false, options.seedPoints, options.refineRoundsIIC);
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 1, pBC + 8, 16, false, options.seedPoints, options.refineRoundsIIC);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void EncodeBC5S(uint8_t *pBC, const PixelBlockS8 *pBlocks, const Options &options)
- {
- assert(pBlocks);
- assert(pBC);
-
- float channelWeights[4];
- Internal::FillWeights(options, channelWeights);
-
- for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
- {
- PixelBlockU8 inputBlocks[ParallelMath::ParallelSize];
- Internal::BiasSignedInput(inputBlocks, pBlocks + blockBase);
-
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 0, pBC, 16, true, options.seedPoints, options.refineRoundsIIC);
- Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 1, pBC + 8, 16, true, options.seedPoints, options.refineRoundsIIC);
- pBC += ParallelMath::ParallelSize * 16;
- }
- }
-
- void DecodeBC7(PixelBlockU8 *pBlocks, const uint8_t *pBC)
- {
- assert(pBlocks);
- assert(pBC);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
- {
- Internal::BC7Computer::UnpackOne(pBlocks[blockBase], pBC);
- pBC += 16;
- }
- }
-
- void DecodeBC6HU(PixelBlockF16 *pBlocks, const uint8_t *pBC)
- {
- assert(pBlocks);
- assert(pBC);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
- {
- Internal::BC6HComputer::UnpackOne(pBlocks[blockBase], pBC, false);
- pBC += 16;
- }
- }
-
- void DecodeBC6HS(PixelBlockF16 *pBlocks, const uint8_t *pBC)
- {
- assert(pBlocks);
- assert(pBC);
-
- for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
- {
- Internal::BC6HComputer::UnpackOne(pBlocks[blockBase], pBC, true);
- pBC += 16;
- }
- }
- }
-}
diff --git a/thirdparty/cvtt/ConvectionKernels.h b/thirdparty/cvtt/ConvectionKernels.h
index fb5ca130f9..3da48405ff 100644
--- a/thirdparty/cvtt/ConvectionKernels.h
+++ b/thirdparty/cvtt/ConvectionKernels.h
@@ -25,21 +25,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef __CVTT_CONVECTION_KERNELS__
#define __CVTT_CONVECTION_KERNELS__
+#include <stddef.h>
#include <stdint.h>
namespace cvtt
{
namespace Flags
{
- // Enable partitioned modes in BC7 encoding (slower, better quality)
- const uint32_t BC7_EnablePartitioning = 0x001;
-
- // Enable 3-partition modes in BC7 encoding (slower, better quality, requires BC7_EnablePartitioning)
- const uint32_t BC7_Enable3Subsets = 0x002;
-
- // Enable dual-plane modes in BC7 encoding (slower, better quality)
- const uint32_t BC7_EnableDualPlane = 0x004;
-
// Use fast indexing in BC7 encoding (about 2x faster, slightly worse quality)
const uint32_t BC7_FastIndexing = 0x008;
@@ -61,13 +53,19 @@ namespace cvtt
// Uniform color channel importance
const uint32_t Uniform = 0x200;
+ // Use fake BT.709 color space for etc2comp compatibility (slower)
+ const uint32_t ETC_UseFakeBT709 = 0x400;
+
+ // Use accurate quantization functions when quantizing fake BT.709 (much slower, marginal improvement on specific blocks)
+ const uint32_t ETC_FakeBT709Accurate = 0x800;
+
// Misc useful default flag combinations
- const uint32_t Fastest = (BC6H_FastIndexing | S3TC_Paranoid);
- const uint32_t Faster = (BC7_EnableDualPlane | BC6H_FastIndexing | S3TC_Paranoid);
- const uint32_t Fast = (BC7_EnablePartitioning | BC7_EnableDualPlane | BC7_FastIndexing | S3TC_Paranoid);
- const uint32_t Default = (BC7_EnablePartitioning | BC7_EnableDualPlane | BC7_Enable3Subsets | BC7_FastIndexing | S3TC_Paranoid);
- const uint32_t Better = (BC7_EnablePartitioning | BC7_EnableDualPlane | BC7_Enable3Subsets | S3TC_Paranoid | S3TC_Exhaustive);
- const uint32_t Ultra = (BC7_EnablePartitioning | BC7_EnableDualPlane | BC7_Enable3Subsets | BC7_TrySingleColor | S3TC_Paranoid | S3TC_Exhaustive);
+ const uint32_t Fastest = (BC6H_FastIndexing | BC7_FastIndexing | S3TC_Paranoid);
+ const uint32_t Faster = (BC6H_FastIndexing | BC7_FastIndexing | S3TC_Paranoid);
+ const uint32_t Fast = (BC7_FastIndexing | S3TC_Paranoid);
+ const uint32_t Default = (BC7_FastIndexing | S3TC_Paranoid);
+ const uint32_t Better = (S3TC_Paranoid | S3TC_Exhaustive);
+ const uint32_t Ultra = (BC7_TrySingleColor | S3TC_Paranoid | S3TC_Exhaustive | ETC_FakeBT709Accurate);
}
const unsigned int NumParallelBlocks = 8;
@@ -81,7 +79,7 @@ namespace cvtt
float blueWeight; // Blue channel importance
float alphaWeight; // Alpha channel importance
- int refineRoundsBC7; // Number of refine rounds for BC7
+ int refineRoundsBC7; // Number of refine rounds for BC7
int refineRoundsBC6H; // Number of refine rounds for BC6H (max 3)
int refineRoundsIIC; // Number of refine rounds for independent interpolated channels (BC3 alpha, BC4, BC5)
int refineRoundsS3TC; // Number of refine rounds for S3TC RGB
@@ -104,6 +102,102 @@ namespace cvtt
}
};
+ struct BC7FineTuningParams
+ {
+ // Seed point counts for each mode+configuration combination
+ uint8_t mode0SP[16];
+ uint8_t mode1SP[64];
+ uint8_t mode2SP[64];
+ uint8_t mode3SP[64];
+ uint8_t mode4SP[4][2];
+ uint8_t mode5SP[4];
+ uint8_t mode6SP;
+ uint8_t mode7SP[64];
+
+ BC7FineTuningParams()
+ {
+ for (int i = 0; i < 16; i++)
+ this->mode0SP[i] = 4;
+
+ for (int i = 0; i < 64; i++)
+ {
+ this->mode1SP[i] = 4;
+ this->mode2SP[i] = 4;
+ this->mode3SP[i] = 4;
+ this->mode7SP[i] = 4;
+ }
+
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 2; j++)
+ this->mode4SP[i][j] = 4;
+
+ this->mode5SP[i] = 4;
+ }
+
+ this->mode6SP = 4;
+ }
+ };
+
+ struct BC7EncodingPlan
+ {
+ static const int kNumRGBAShapes = 129;
+ static const int kNumRGBShapes = 243;
+
+ uint64_t mode1PartitionEnabled;
+ uint64_t mode2PartitionEnabled;
+ uint64_t mode3PartitionEnabled;
+ uint16_t mode0PartitionEnabled;
+ uint64_t mode7RGBAPartitionEnabled;
+ uint64_t mode7RGBPartitionEnabled;
+ uint8_t mode4SP[4][2];
+ uint8_t mode5SP[4];
+ bool mode6Enabled;
+
+ uint8_t seedPointsForShapeRGB[kNumRGBShapes];
+ uint8_t seedPointsForShapeRGBA[kNumRGBAShapes];
+
+ uint8_t rgbaShapeList[kNumRGBAShapes];
+ uint8_t rgbaNumShapesToEvaluate;
+
+ uint8_t rgbShapeList[kNumRGBShapes];
+ uint8_t rgbNumShapesToEvaluate;
+
+ BC7EncodingPlan()
+ {
+ for (int i = 0; i < kNumRGBShapes; i++)
+ {
+ this->rgbShapeList[i] = i;
+ this->seedPointsForShapeRGB[i] = 4;
+ }
+ this->rgbNumShapesToEvaluate = kNumRGBShapes;
+
+ for (int i = 0; i < kNumRGBAShapes; i++)
+ {
+ this->rgbaShapeList[i] = i;
+ this->seedPointsForShapeRGBA[i] = 4;
+ }
+ this->rgbaNumShapesToEvaluate = kNumRGBAShapes;
+
+
+ this->mode0PartitionEnabled = 0xffff;
+ this->mode1PartitionEnabled = 0xffffffffffffffffULL;
+ this->mode2PartitionEnabled = 0xffffffffffffffffULL;
+ this->mode3PartitionEnabled = 0xffffffffffffffffULL;
+ this->mode6Enabled = true;
+ this->mode7RGBPartitionEnabled = 0xffffffffffffffffULL;
+ this->mode7RGBAPartitionEnabled = 0xffffffffffffffffULL;
+
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 2; j++)
+ this->mode4SP[i][j] = 4;
+
+ this->mode5SP[i] = 4;
+ }
+ }
+ };
+
// RGBA input block for unsigned 8-bit formats
struct PixelBlockU8
{
@@ -116,14 +210,34 @@ namespace cvtt
int8_t m_pixels[16][4];
};
+ struct PixelBlockScalarS16
+ {
+ int16_t m_pixels[16];
+ };
+
// RGBA input block for half-precision float formats (bit-cast to int16_t)
struct PixelBlockF16
{
int16_t m_pixels[16][4];
};
+ class ETC2CompressionData
+ {
+ protected:
+ ETC2CompressionData() {}
+ };
+
+ class ETC1CompressionData
+ {
+ protected:
+ ETC1CompressionData() {}
+ };
+
namespace Kernels
{
+ typedef void* allocFunc_t(void *context, size_t size);
+ typedef void freeFunc_t(void *context, void* ptr, size_t size);
+
// NOTE: All functions accept and output NumParallelBlocks blocks at once
void EncodeBC1(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options);
void EncodeBC2(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options);
@@ -134,7 +248,28 @@ namespace cvtt
void EncodeBC5S(uint8_t *pBC, const PixelBlockS8 *pBlocks, const Options &options);
void EncodeBC6HU(uint8_t *pBC, const PixelBlockF16 *pBlocks, const Options &options);
void EncodeBC6HS(uint8_t *pBC, const PixelBlockF16 *pBlocks, const Options &options);
- void EncodeBC7(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options);
+ void EncodeBC7(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options, const BC7EncodingPlan &encodingPlan);
+ void EncodeETC1(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options, ETC1CompressionData *compressionData);
+ void EncodeETC2(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options, ETC2CompressionData *compressionData);
+ void EncodeETC2RGBA(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC2CompressionData *compressionData);
+ void EncodeETC2PunchthroughAlpha(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC2CompressionData *compressionData);
+
+ void EncodeETC2Alpha(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options);
+ void EncodeETC2Alpha11(uint8_t *pBC, const PixelBlockScalarS16 *pBlocks, bool isSigned, const cvtt::Options &options);
+
+ // Generates a BC7 encoding plan from a quality parameter that ranges from 1 (fastest) to 100 (best)
+ void ConfigureBC7EncodingPlanFromQuality(BC7EncodingPlan &encodingPlan, int quality);
+
+ // Generates a BC7 encoding plan from fine-tuning parameters.
+ bool ConfigureBC7EncodingPlanFromFineTuningParams(BC7EncodingPlan &encodingPlan, const BC7FineTuningParams &params);
+
+ // ETC compression requires temporary storage that normally consumes a large amount of stack space.
+ // To allocate and release it, use one of these functions.
+ ETC2CompressionData *AllocETC2Data(allocFunc_t allocFunc, void *context, const cvtt::Options &options);
+ void ReleaseETC2Data(ETC2CompressionData *compressionData, freeFunc_t freeFunc);
+
+ ETC1CompressionData *AllocETC1Data(allocFunc_t allocFunc, void *context);
+ void ReleaseETC1Data(ETC1CompressionData *compressionData, freeFunc_t freeFunc);
void DecodeBC6HU(PixelBlockF16 *pBlocks, const uint8_t *pBC);
void DecodeBC6HS(PixelBlockF16 *pBlocks, const uint8_t *pBC);
diff --git a/thirdparty/cvtt/ConvectionKernels_API.cpp b/thirdparty/cvtt/ConvectionKernels_API.cpp
new file mode 100644
index 0000000000..707e71d474
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_API.cpp
@@ -0,0 +1,346 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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 "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include <stdint.h>
+#include "ConvectionKernels.h"
+#include "ConvectionKernels_Util.h"
+#include "ConvectionKernels_BC67.h"
+#include "ConvectionKernels_ETC.h"
+#include "ConvectionKernels_S3TC.h"
+
+#include <assert.h>
+
+namespace cvtt
+{
+ namespace Kernels
+ {
+ void EncodeBC7(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, const BC7EncodingPlan &encodingPlan)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::BC7Computer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, encodingPlan, options.refineRoundsBC7);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC6HU(uint8_t *pBC, const PixelBlockF16 *pBlocks, const cvtt::Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::BC6HComputer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, false, options.seedPoints, options.refineRoundsBC6H);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC6HS(uint8_t *pBC, const PixelBlockF16 *pBlocks, const cvtt::Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::BC6HComputer::Pack(options.flags, pBlocks + blockBase, pBC, channelWeights, true, options.seedPoints, options.refineRoundsBC6H);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC1(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC, 8, channelWeights, true, options.threshold, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeBC2(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC + 8, 16, channelWeights, false, 1.0f, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
+ Internal::S3TCComputer::PackExplicitAlpha(options.flags, pBlocks + blockBase, 3, pBC, 16);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC3(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::S3TCComputer::PackRGB(options.flags, pBlocks + blockBase, pBC + 8, 16, channelWeights, false, 1.0f, (options.flags & Flags::S3TC_Exhaustive) != 0, options.seedPoints, options.refineRoundsS3TC);
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 3, pBC, 16, false, options.seedPoints, options.refineRoundsIIC);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC4U(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 0, pBC, 8, false, options.seedPoints, options.refineRoundsIIC);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeBC4S(uint8_t *pBC, const PixelBlockS8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ PixelBlockU8 inputBlocks[ParallelMath::ParallelSize];
+ Util::BiasSignedInput(inputBlocks, pBlocks + blockBase);
+
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 0, pBC, 8, true, options.seedPoints, options.refineRoundsIIC);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeBC5U(uint8_t *pBC, const PixelBlockU8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 0, pBC, 16, false, options.seedPoints, options.refineRoundsIIC);
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, pBlocks + blockBase, 1, pBC + 8, 16, false, options.seedPoints, options.refineRoundsIIC);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeBC5S(uint8_t *pBC, const PixelBlockS8 *pBlocks, const Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ PixelBlockU8 inputBlocks[ParallelMath::ParallelSize];
+ Util::BiasSignedInput(inputBlocks, pBlocks + blockBase);
+
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 0, pBC, 16, true, options.seedPoints, options.refineRoundsIIC);
+ Internal::S3TCComputer::PackInterpolatedAlpha(options.flags, inputBlocks, 1, pBC + 8, 16, true, options.seedPoints, options.refineRoundsIIC);
+ pBC += ParallelMath::ParallelSize * 16;
+ }
+ }
+
+ void EncodeETC1(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC1CompressionData *compressionData)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::ETCComputer::CompressETC1Block(pBC, pBlocks + blockBase, compressionData, options);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeETC2(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC2CompressionData *compressionData)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::ETCComputer::CompressETC2Block(pBC, pBlocks + blockBase, compressionData, options, false);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeETC2PunchthroughAlpha(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC2CompressionData *compressionData)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ float channelWeights[4];
+ Util::FillWeights(options, channelWeights);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::ETCComputer::CompressETC2Block(pBC, pBlocks + blockBase, compressionData, options, true);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeETC2Alpha(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::ETCComputer::CompressETC2AlphaBlock(pBC, pBlocks + blockBase, options);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeETC2Alpha11(uint8_t *pBC, const PixelBlockScalarS16 *pBlocks, bool isSigned, const cvtt::Options &options)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase += ParallelMath::ParallelSize)
+ {
+ Internal::ETCComputer::CompressEACBlock(pBC, pBlocks + blockBase, isSigned, options);
+ pBC += ParallelMath::ParallelSize * 8;
+ }
+ }
+
+ void EncodeETC2RGBA(uint8_t *pBC, const PixelBlockU8 *pBlocks, const cvtt::Options &options, cvtt::ETC2CompressionData *compressionData)
+ {
+ uint8_t alphaBlockData[cvtt::NumParallelBlocks * 8];
+ uint8_t colorBlockData[cvtt::NumParallelBlocks * 8];
+
+ EncodeETC2(colorBlockData, pBlocks, options, compressionData);
+ EncodeETC2Alpha(alphaBlockData, pBlocks, options);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
+ {
+ for (size_t blockData = 0; blockData < 8; blockData++)
+ pBC[blockBase * 16 + blockData] = alphaBlockData[blockBase * 8 + blockData];
+
+ for (size_t blockData = 0; blockData < 8; blockData++)
+ pBC[blockBase * 16 + 8 + blockData] = colorBlockData[blockBase * 8 + blockData];
+ }
+ }
+
+ void DecodeBC7(PixelBlockU8 *pBlocks, const uint8_t *pBC)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
+ {
+ Internal::BC7Computer::UnpackOne(pBlocks[blockBase], pBC);
+ pBC += 16;
+ }
+ }
+
+ void DecodeBC6HU(PixelBlockF16 *pBlocks, const uint8_t *pBC)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
+ {
+ Internal::BC6HComputer::UnpackOne(pBlocks[blockBase], pBC, false);
+ pBC += 16;
+ }
+ }
+
+ void DecodeBC6HS(PixelBlockF16 *pBlocks, const uint8_t *pBC)
+ {
+ assert(pBlocks);
+ assert(pBC);
+
+ for (size_t blockBase = 0; blockBase < cvtt::NumParallelBlocks; blockBase++)
+ {
+ Internal::BC6HComputer::UnpackOne(pBlocks[blockBase], pBC, true);
+ pBC += 16;
+ }
+ }
+
+ ETC1CompressionData *AllocETC1Data(allocFunc_t allocFunc, void *context)
+ {
+ return cvtt::Internal::ETCComputer::AllocETC1Data(allocFunc, context);
+ }
+
+ void ReleaseETC1Data(ETC1CompressionData *compressionData, freeFunc_t freeFunc)
+ {
+ cvtt::Internal::ETCComputer::ReleaseETC1Data(compressionData, freeFunc);
+ }
+
+ ETC2CompressionData *AllocETC2Data(allocFunc_t allocFunc, void *context, const cvtt::Options &options)
+ {
+ return cvtt::Internal::ETCComputer::AllocETC2Data(allocFunc, context, options);
+ }
+
+ void ReleaseETC2Data(ETC2CompressionData *compressionData, freeFunc_t freeFunc)
+ {
+ cvtt::Internal::ETCComputer::ReleaseETC2Data(compressionData, freeFunc);
+ }
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_AggregatedError.h b/thirdparty/cvtt/ConvectionKernels_AggregatedError.h
new file mode 100644
index 0000000000..9f9356a345
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_AggregatedError.h
@@ -0,0 +1,55 @@
+#pragma once
+#ifndef __CVTT_AGGREGATEDERROR_H__
+#define __CVTT_AGGREGATEDERROR_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ template<int TVectorSize>
+ class AggregatedError
+ {
+ public:
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt31 MUInt31;
+ typedef ParallelMath::Float MFloat;
+
+ AggregatedError()
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_errorUnweighted[ch] = ParallelMath::MakeUInt31(0);
+ }
+
+ void Add(const MUInt16 &channelErrorUnweighted, int ch)
+ {
+ m_errorUnweighted[ch] = m_errorUnweighted[ch] + ParallelMath::ToUInt31(channelErrorUnweighted);
+ }
+
+ MFloat Finalize(uint32_t flags, const float channelWeightsSq[TVectorSize]) const
+ {
+ if (flags & cvtt::Flags::Uniform)
+ {
+ MUInt31 total = m_errorUnweighted[0];
+ for (int ch = 1; ch < TVectorSize; ch++)
+ total = total + m_errorUnweighted[ch];
+ return ParallelMath::ToFloat(total);
+ }
+ else
+ {
+ MFloat total = ParallelMath::ToFloat(m_errorUnweighted[0]) * channelWeightsSq[0];
+ for (int ch = 1; ch < TVectorSize; ch++)
+ total = total + ParallelMath::ToFloat(m_errorUnweighted[ch]) * channelWeightsSq[ch];
+ return total;
+ }
+ }
+
+ private:
+ MUInt31 m_errorUnweighted[TVectorSize];
+ };
+ }
+}
+
+#endif
+
diff --git a/thirdparty/cvtt/ConvectionKernels_BC67.cpp b/thirdparty/cvtt/ConvectionKernels_BC67.cpp
new file mode 100644
index 0000000000..791859b232
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC67.cpp
@@ -0,0 +1,3485 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_BC67.h"
+
+#include "ConvectionKernels_AggregatedError.h"
+#include "ConvectionKernels_BCCommon.h"
+#include "ConvectionKernels_BC7_Prio.h"
+#include "ConvectionKernels_BC7_SingleColor.h"
+#include "ConvectionKernels_BC6H_IO.h"
+#include "ConvectionKernels_EndpointRefiner.h"
+#include "ConvectionKernels_EndpointSelector.h"
+#include "ConvectionKernels_IndexSelectorHDR.h"
+#include "ConvectionKernels_ParallelMath.h"
+#include "ConvectionKernels_UnfinishedEndpoints.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ namespace BC67
+ {
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::UInt15 MUInt15;
+
+ struct WorkInfo
+ {
+ MUInt15 m_mode;
+ MFloat m_error;
+ MUInt15 m_ep[3][2][4];
+ MUInt15 m_indexes[16];
+ MUInt15 m_indexes2[16];
+
+ union
+ {
+ MUInt15 m_partition;
+ struct IndexSelectorAndRotation
+ {
+ MUInt15 m_indexSelector;
+ MUInt15 m_rotation;
+ } m_isr;
+ } m_u;
+ };
+ }
+
+ namespace BC7Data
+ {
+ enum AlphaMode
+ {
+ AlphaMode_Combined,
+ AlphaMode_Separate,
+ AlphaMode_None,
+ };
+
+ enum PBitMode
+ {
+ PBitMode_PerEndpoint,
+ PBitMode_PerSubset,
+ PBitMode_None
+ };
+
+ struct BC7ModeInfo
+ {
+ PBitMode m_pBitMode;
+ AlphaMode m_alphaMode;
+ int m_rgbBits;
+ int m_alphaBits;
+ int m_partitionBits;
+ int m_numSubsets;
+ int m_indexBits;
+ int m_alphaIndexBits;
+ bool m_hasIndexSelector;
+ };
+
+ BC7ModeInfo g_modes[] =
+ {
+ { PBitMode_PerEndpoint, AlphaMode_None, 4, 0, 4, 3, 3, 0, false }, // 0
+ { PBitMode_PerSubset, AlphaMode_None, 6, 0, 6, 2, 3, 0, false }, // 1
+ { PBitMode_None, AlphaMode_None, 5, 0, 6, 3, 2, 0, false }, // 2
+ { PBitMode_PerEndpoint, AlphaMode_None, 7, 0, 6, 2, 2, 0, false }, // 3 (Mode reference has an error, P-bit is really per-endpoint)
+
+ { PBitMode_None, AlphaMode_Separate, 5, 6, 0, 1, 2, 3, true }, // 4
+ { PBitMode_None, AlphaMode_Separate, 7, 8, 0, 1, 2, 2, false }, // 5
+ { PBitMode_PerEndpoint, AlphaMode_Combined, 7, 7, 0, 1, 4, 0, false }, // 6
+ { PBitMode_PerEndpoint, AlphaMode_Combined, 5, 5, 6, 2, 2, 0, false } // 7
+ };
+
+ const int g_weight2[] = { 0, 21, 43, 64 };
+ const int g_weight3[] = { 0, 9, 18, 27, 37, 46, 55, 64 };
+ const int g_weight4[] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
+
+ const int *g_weightTables[] =
+ {
+ NULL,
+ NULL,
+ g_weight2,
+ g_weight3,
+ g_weight4
+ };
+
+ struct BC6HModeInfo
+ {
+ uint16_t m_modeID;
+ bool m_partitioned;
+ bool m_transformed;
+ int m_aPrec;
+ int m_bPrec[3];
+ };
+
+ // [partitioned][precision]
+ bool g_hdrModesExistForPrecision[2][17] =
+ {
+ //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ { false, false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, true },
+ { false, false, false, false, false, false, true, true, true, true, true, true, false, false, false, false, false },
+ };
+
+ BC6HModeInfo g_hdrModes[] =
+ {
+ { 0x00, true, true, 10,{ 5, 5, 5 } },
+ { 0x01, true, true, 7,{ 6, 6, 6 } },
+ { 0x02, true, true, 11,{ 5, 4, 4 } },
+ { 0x06, true, true, 11,{ 4, 5, 4 } },
+ { 0x0a, true, true, 11,{ 4, 4, 5 } },
+ { 0x0e, true, true, 9,{ 5, 5, 5 } },
+ { 0x12, true, true, 8,{ 6, 5, 5 } },
+ { 0x16, true, true, 8,{ 5, 6, 5 } },
+ { 0x1a, true, true, 8,{ 5, 5, 6 } },
+ { 0x1e, true, false, 6,{ 6, 6, 6 } },
+ { 0x03, false, false, 10,{ 10, 10, 10 } },
+ { 0x07, false, true, 11,{ 9, 9, 9 } },
+ { 0x0b, false, true, 12,{ 8, 8, 8 } },
+ { 0x0f, false, true, 16,{ 4, 4, 4 } },
+ };
+
+ const int g_maxHDRPrecision = 16;
+
+ static const size_t g_numHDRModes = sizeof(g_hdrModes) / sizeof(g_hdrModes[0]);
+
+ static uint16_t g_partitionMap[64] =
+ {
+ 0xCCCC, 0x8888, 0xEEEE, 0xECC8,
+ 0xC880, 0xFEEC, 0xFEC8, 0xEC80,
+ 0xC800, 0xFFEC, 0xFE80, 0xE800,
+ 0xFFE8, 0xFF00, 0xFFF0, 0xF000,
+ 0xF710, 0x008E, 0x7100, 0x08CE,
+ 0x008C, 0x7310, 0x3100, 0x8CCE,
+ 0x088C, 0x3110, 0x6666, 0x366C,
+ 0x17E8, 0x0FF0, 0x718E, 0x399C,
+ 0xaaaa, 0xf0f0, 0x5a5a, 0x33cc,
+ 0x3c3c, 0x55aa, 0x9696, 0xa55a,
+ 0x73ce, 0x13c8, 0x324c, 0x3bdc,
+ 0x6996, 0xc33c, 0x9966, 0x660,
+ 0x272, 0x4e4, 0x4e40, 0x2720,
+ 0xc936, 0x936c, 0x39c6, 0x639c,
+ 0x9336, 0x9cc6, 0x817e, 0xe718,
+ 0xccf0, 0xfcc, 0x7744, 0xee22,
+ };
+
+ static uint32_t g_partitionMap2[64] =
+ {
+ 0xaa685050, 0x6a5a5040, 0x5a5a4200, 0x5450a0a8,
+ 0xa5a50000, 0xa0a05050, 0x5555a0a0, 0x5a5a5050,
+ 0xaa550000, 0xaa555500, 0xaaaa5500, 0x90909090,
+ 0x94949494, 0xa4a4a4a4, 0xa9a59450, 0x2a0a4250,
+ 0xa5945040, 0x0a425054, 0xa5a5a500, 0x55a0a0a0,
+ 0xa8a85454, 0x6a6a4040, 0xa4a45000, 0x1a1a0500,
+ 0x0050a4a4, 0xaaa59090, 0x14696914, 0x69691400,
+ 0xa08585a0, 0xaa821414, 0x50a4a450, 0x6a5a0200,
+ 0xa9a58000, 0x5090a0a8, 0xa8a09050, 0x24242424,
+ 0x00aa5500, 0x24924924, 0x24499224, 0x50a50a50,
+ 0x500aa550, 0xaaaa4444, 0x66660000, 0xa5a0a5a0,
+ 0x50a050a0, 0x69286928, 0x44aaaa44, 0x66666600,
+ 0xaa444444, 0x54a854a8, 0x95809580, 0x96969600,
+ 0xa85454a8, 0x80959580, 0xaa141414, 0x96960000,
+ 0xaaaa1414, 0xa05050a0, 0xa0a5a5a0, 0x96000000,
+ 0x40804080, 0xa9a8a9a8, 0xaaaaaa44, 0x2a4a5254,
+ };
+
+ static int g_fixupIndexes2[64] =
+ {
+ 15,15,15,15,
+ 15,15,15,15,
+ 15,15,15,15,
+ 15,15,15,15,
+ 15, 2, 8, 2,
+ 2, 8, 8,15,
+ 2, 8, 2, 2,
+ 8, 8, 2, 2,
+
+ 15,15, 6, 8,
+ 2, 8,15,15,
+ 2, 8, 2, 2,
+ 2,15,15, 6,
+ 6, 2, 6, 8,
+ 15,15, 2, 2,
+ 15,15,15,15,
+ 15, 2, 2,15,
+ };
+
+ static int g_fixupIndexes3[64][2] =
+ {
+ { 3,15 },{ 3, 8 },{ 15, 8 },{ 15, 3 },
+ { 8,15 },{ 3,15 },{ 15, 3 },{ 15, 8 },
+ { 8,15 },{ 8,15 },{ 6,15 },{ 6,15 },
+ { 6,15 },{ 5,15 },{ 3,15 },{ 3, 8 },
+ { 3,15 },{ 3, 8 },{ 8,15 },{ 15, 3 },
+ { 3,15 },{ 3, 8 },{ 6,15 },{ 10, 8 },
+ { 5, 3 },{ 8,15 },{ 8, 6 },{ 6,10 },
+ { 8,15 },{ 5,15 },{ 15,10 },{ 15, 8 },
+
+ { 8,15 },{ 15, 3 },{ 3,15 },{ 5,10 },
+ { 6,10 },{ 10, 8 },{ 8, 9 },{ 15,10 },
+ { 15, 6 },{ 3,15 },{ 15, 8 },{ 5,15 },
+ { 15, 3 },{ 15, 6 },{ 15, 6 },{ 15, 8 },
+ { 3,15 },{ 15, 3 },{ 5,15 },{ 5,15 },
+ { 5,15 },{ 8,15 },{ 5,15 },{ 10,15 },
+ { 5,15 },{ 10,15 },{ 8,15 },{ 13,15 },
+ { 15, 3 },{ 12,15 },{ 3,15 },{ 3, 8 },
+ };
+
+ static const unsigned char g_fragments[] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0, 16
+ 0, 1, 2, 3, // 16, 4
+ 0, 1, 4, // 20, 3
+ 0, 1, 2, 4, // 23, 4
+ 2, 3, 7, // 27, 3
+ 1, 2, 3, 7, // 30, 4
+ 0, 1, 2, 3, 4, 5, 6, 7, // 34, 8
+ 0, 1, 4, 8, // 42, 4
+ 0, 1, 2, 4, 5, 8, // 46, 6
+ 0, 1, 2, 3, 4, 5, 6, 8, // 52, 8
+ 1, 4, 5, 6, 9, // 60, 5
+ 2, 5, 6, 7, 10, // 65, 5
+ 5, 6, 9, 10, // 70, 4
+ 2, 3, 7, 11, // 74, 4
+ 1, 2, 3, 6, 7, 11, // 78, 6
+ 0, 1, 2, 3, 5, 6, 7, 11, // 84, 8
+ 0, 1, 2, 3, 8, 9, 10, 11, // 92, 8
+ 2, 3, 6, 7, 8, 9, 10, 11, // 100, 8
+ 4, 5, 6, 7, 8, 9, 10, 11, // 108, 8
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, // 116, 12
+ 0, 4, 8, 12, // 128, 4
+ 0, 2, 3, 4, 6, 7, 8, 12, // 132, 8
+ 0, 1, 2, 4, 5, 8, 9, 12, // 140, 8
+ 0, 1, 2, 3, 4, 5, 6, 8, 9, 12, // 148, 10
+ 3, 6, 7, 8, 9, 12, // 158, 6
+ 3, 5, 6, 7, 8, 9, 10, 12, // 164, 8
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, // 172, 12
+ 0, 1, 2, 5, 6, 7, 11, 12, // 184, 8
+ 5, 8, 9, 10, 13, // 192, 5
+ 8, 12, 13, // 197, 3
+ 4, 8, 12, 13, // 200, 4
+ 2, 3, 6, 9, 12, 13, // 204, 6
+ 0, 1, 2, 3, 8, 9, 12, 13, // 210, 8
+ 0, 1, 4, 5, 8, 9, 12, 13, // 218, 8
+ 2, 3, 6, 7, 8, 9, 12, 13, // 226, 8
+ 2, 3, 5, 6, 9, 10, 12, 13, // 234, 8
+ 0, 3, 6, 7, 9, 10, 12, 13, // 242, 8
+ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, // 250, 12
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, // 262, 13
+ 2, 3, 4, 7, 8, 11, 12, 13, // 275, 8
+ 1, 2, 6, 7, 8, 11, 12, 13, // 283, 8
+ 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, // 291, 10
+ 2, 3, 4, 5, 10, 11, 12, 13, // 301, 8
+ 0, 1, 6, 7, 10, 11, 12, 13, // 309, 8
+ 6, 9, 10, 11, 14, // 317, 5
+ 0, 2, 4, 6, 8, 10, 12, 14, // 322, 8
+ 1, 3, 5, 7, 8, 10, 12, 14, // 330, 8
+ 1, 3, 4, 6, 9, 11, 12, 14, // 338, 8
+ 0, 2, 5, 7, 9, 11, 12, 14, // 346, 8
+ 0, 3, 4, 5, 8, 9, 13, 14, // 354, 8
+ 2, 3, 4, 7, 8, 9, 13, 14, // 362, 8
+ 1, 2, 5, 6, 9, 10, 13, 14, // 370, 8
+ 0, 3, 4, 7, 9, 10, 13, 14, // 378, 8
+ 0, 3, 5, 6, 8, 11, 13, 14, // 386, 8
+ 1, 2, 4, 7, 8, 11, 13, 14, // 394, 8
+ 0, 1, 4, 7, 10, 11, 13, 14, // 402, 8
+ 0, 3, 6, 7, 10, 11, 13, 14, // 410, 8
+ 8, 12, 13, 14, // 418, 4
+ 1, 2, 3, 7, 8, 12, 13, 14, // 422, 8
+ 4, 8, 9, 12, 13, 14, // 430, 6
+ 0, 4, 5, 8, 9, 12, 13, 14, // 436, 8
+ 1, 2, 3, 6, 7, 8, 9, 12, 13, 14, // 444, 10
+ 2, 6, 8, 9, 10, 12, 13, 14, // 454, 8
+ 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, // 462, 12
+ 0, 7, 9, 10, 11, 12, 13, 14, // 474, 8
+ 1, 2, 3, 4, 5, 6, 8, 15, // 482, 8
+ 3, 7, 11, 15, // 490, 4
+ 0, 1, 3, 4, 5, 7, 11, 15, // 494, 8
+ 0, 4, 5, 10, 11, 15, // 502, 6
+ 1, 2, 3, 6, 7, 10, 11, 15, // 508, 8
+ 0, 1, 2, 3, 5, 6, 7, 10, 11, 15, // 516, 10
+ 0, 4, 5, 6, 9, 10, 11, 15, // 526, 8
+ 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 15, // 534, 12
+ 1, 2, 4, 5, 8, 9, 12, 15, // 546, 8
+ 2, 3, 5, 6, 8, 9, 12, 15, // 554, 8
+ 0, 3, 5, 6, 9, 10, 12, 15, // 562, 8
+ 1, 2, 4, 7, 9, 10, 12, 15, // 570, 8
+ 1, 2, 5, 6, 8, 11, 12, 15, // 578, 8
+ 0, 3, 4, 7, 8, 11, 12, 15, // 586, 8
+ 0, 1, 5, 6, 10, 11, 12, 15, // 594, 8
+ 1, 2, 6, 7, 10, 11, 12, 15, // 602, 8
+ 1, 3, 4, 6, 8, 10, 13, 15, // 610, 8
+ 0, 2, 5, 7, 8, 10, 13, 15, // 618, 8
+ 0, 2, 4, 6, 9, 11, 13, 15, // 626, 8
+ 1, 3, 5, 7, 9, 11, 13, 15, // 634, 8
+ 0, 1, 2, 3, 4, 5, 7, 8, 12, 13, 15, // 642, 11
+ 2, 3, 4, 5, 8, 9, 14, 15, // 653, 8
+ 0, 1, 6, 7, 8, 9, 14, 15, // 661, 8
+ 0, 1, 5, 10, 14, 15, // 669, 6
+ 0, 3, 4, 5, 9, 10, 14, 15, // 675, 8
+ 0, 1, 5, 6, 9, 10, 14, 15, // 683, 8
+ 11, 14, 15, // 691, 3
+ 7, 11, 14, 15, // 694, 4
+ 1, 2, 4, 5, 8, 11, 14, 15, // 698, 8
+ 0, 1, 4, 7, 8, 11, 14, 15, // 706, 8
+ 0, 1, 4, 5, 10, 11, 14, 15, // 714, 8
+ 2, 3, 6, 7, 10, 11, 14, 15, // 722, 8
+ 4, 5, 6, 7, 10, 11, 14, 15, // 730, 8
+ 0, 1, 4, 5, 7, 8, 10, 11, 14, 15, // 738, 10
+ 0, 1, 2, 3, 5, 6, 7, 9, 10, 11, 14, 15, // 748, 12
+ 0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 14, 15, // 760, 13
+ 0, 1, 2, 3, 4, 6, 7, 11, 12, 14, 15, // 773, 11
+ 3, 4, 8, 9, 10, 13, 14, 15, // 784, 8
+ 11, 13, 14, 15, // 792, 4
+ 0, 1, 2, 4, 11, 13, 14, 15, // 796, 8
+ 0, 1, 2, 4, 5, 10, 11, 13, 14, 15, // 804, 10
+ 7, 10, 11, 13, 14, 15, // 814, 6
+ 3, 6, 7, 10, 11, 13, 14, 15, // 820, 8
+ 1, 5, 9, 10, 11, 13, 14, 15, // 828, 8
+ 1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15, // 836, 12
+ 12, 13, 14, 15, // 848, 4
+ 0, 1, 2, 3, 12, 13, 14, 15, // 852, 8
+ 0, 1, 4, 5, 12, 13, 14, 15, // 860, 8
+ 4, 5, 6, 7, 12, 13, 14, 15, // 868, 8
+ 4, 8, 9, 10, 12, 13, 14, 15, // 876, 8
+ 0, 4, 5, 8, 9, 10, 12, 13, 14, 15, // 884, 10
+ 0, 1, 4, 5, 6, 8, 9, 10, 12, 13, 14, 15, // 894, 12
+ 0, 1, 2, 3, 4, 7, 8, 11, 12, 13, 14, 15, // 906, 12
+ 0, 1, 3, 4, 8, 9, 11, 12, 13, 14, 15, // 918, 11
+ 0, 2, 3, 7, 8, 10, 11, 12, 13, 14, 15, // 929, 11
+ 7, 9, 10, 11, 12, 13, 14, 15, // 940, 8
+ 3, 6, 7, 9, 10, 11, 12, 13, 14, 15, // 948, 10
+ 2, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, // 958, 12
+ 8, 9, 10, 11, 12, 13, 14, 15, // 970, 8
+ 0, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, // 978, 12
+ 0, 1, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, // 990, 13
+ 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1003, 12
+ 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1015, 13
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 1028, 12
+ 0, 2, // 1040, 2
+ 1, 3, // 1042, 2
+ 0, 1, 4, 5, // 1044, 4
+ 0, 1, 2, 4, 5, // 1048, 5
+ 2, 3, 6, // 1053, 3
+ 0, 2, 4, 6, // 1056, 4
+ 1, 2, 5, 6, // 1060, 4
+ 0, 1, 2, 3, 5, 6, // 1064, 6
+ 0, 1, 2, 4, 5, 6, // 1070, 6
+ 0, 1, 2, 3, 4, 5, 6, // 1076, 7
+ 0, 3, 4, 7, // 1083, 4
+ 0, 1, 2, 3, 4, 7, // 1087, 6
+ 1, 3, 5, 7, // 1093, 4
+ 2, 3, 6, 7, // 1097, 4
+ 1, 2, 3, 6, 7, // 1101, 5
+ 1, 2, 3, 5, 6, 7, // 1106, 6
+ 0, 1, 2, 3, 5, 6, 7, // 1112, 7
+ 4, 5, 6, 7, // 1119, 4
+ 0, 8, // 1123, 2
+ 0, 1, 4, 5, 8, // 1125, 5
+ 0, 1, 8, 9, // 1130, 4
+ 4, 5, 8, 9, // 1134, 4
+ 0, 1, 4, 5, 8, 9, // 1138, 6
+ 2, 6, 8, 9, // 1144, 4
+ 6, 7, 8, 9, // 1148, 4
+ 0, 2, 4, 6, 8, 10, // 1152, 6
+ 1, 2, 5, 6, 9, 10, // 1158, 6
+ 0, 3, 4, 7, 9, 10, // 1164, 6
+ 0, 1, 2, 8, 9, 10, // 1170, 6
+ 4, 5, 6, 8, 9, 10, // 1176, 6
+ 3, 11, // 1182, 2
+ 2, 3, 6, 7, 11, // 1184, 5
+ 0, 3, 8, 11, // 1189, 4
+ 0, 3, 4, 7, 8, 11, // 1193, 6
+ 1, 3, 5, 7, 9, 11, // 1199, 6
+ 2, 3, 10, 11, // 1205, 4
+ 1, 5, 10, 11, // 1209, 4
+ 4, 5, 10, 11, // 1213, 4
+ 6, 7, 10, 11, // 1217, 4
+ 2, 3, 6, 7, 10, 11, // 1221, 6
+ 1, 2, 3, 9, 10, 11, // 1227, 6
+ 5, 6, 7, 9, 10, 11, // 1233, 6
+ 8, 9, 10, 11, // 1239, 4
+ 4, 12, // 1243, 2
+ 0, 1, 2, 3, 4, 5, 8, 12, // 1245, 8
+ 8, 9, 12, // 1253, 3
+ 0, 4, 5, 8, 9, 12, // 1256, 6
+ 0, 1, 4, 5, 8, 9, 12, // 1262, 7
+ 2, 3, 5, 6, 8, 9, 12, // 1269, 7
+ 1, 5, 9, 13, // 1276, 4
+ 6, 7, 9, 13, // 1280, 4
+ 1, 4, 7, 10, 13, // 1284, 5
+ 1, 6, 8, 11, 13, // 1289, 5
+ 0, 1, 12, 13, // 1294, 4
+ 4, 5, 12, 13, // 1298, 4
+ 0, 1, 6, 7, 12, 13, // 1302, 6
+ 0, 1, 4, 8, 12, 13, // 1308, 6
+ 8, 9, 12, 13, // 1314, 4
+ 4, 8, 9, 12, 13, // 1318, 5
+ 4, 5, 8, 9, 12, 13, // 1323, 6
+ 0, 4, 5, 8, 9, 12, 13, // 1329, 7
+ 0, 1, 6, 10, 12, 13, // 1336, 6
+ 3, 6, 7, 9, 10, 12, 13, // 1342, 7
+ 0, 1, 10, 11, 12, 13, // 1349, 6
+ 2, 4, 7, 9, 14, // 1355, 5
+ 4, 5, 10, 14, // 1360, 4
+ 2, 6, 10, 14, // 1364, 4
+ 2, 5, 8, 11, 14, // 1368, 5
+ 0, 2, 12, 14, // 1373, 4
+ 8, 10, 12, 14, // 1377, 4
+ 4, 6, 8, 10, 12, 14, // 1381, 6
+ 13, 14, // 1387, 2
+ 9, 10, 13, 14, // 1389, 4
+ 5, 6, 9, 10, 13, 14, // 1393, 6
+ 0, 1, 2, 12, 13, 14, // 1399, 6
+ 4, 5, 6, 12, 13, 14, // 1405, 6
+ 8, 9, 12, 13, 14, // 1411, 5
+ 8, 9, 10, 12, 13, 14, // 1416, 6
+ 7, 15, // 1422, 2
+ 0, 5, 10, 15, // 1424, 4
+ 0, 1, 2, 3, 6, 7, 11, 15, // 1428, 8
+ 10, 11, 15, // 1436, 3
+ 0, 1, 5, 6, 10, 11, 15, // 1439, 7
+ 3, 6, 7, 10, 11, 15, // 1446, 6
+ 12, 15, // 1452, 2
+ 0, 3, 12, 15, // 1454, 4
+ 4, 7, 12, 15, // 1458, 4
+ 0, 3, 6, 9, 12, 15, // 1462, 6
+ 0, 3, 5, 10, 12, 15, // 1468, 6
+ 8, 11, 12, 15, // 1474, 4
+ 5, 6, 8, 11, 12, 15, // 1478, 6
+ 4, 7, 8, 11, 12, 15, // 1484, 6
+ 1, 3, 13, 15, // 1490, 4
+ 9, 11, 13, 15, // 1494, 4
+ 5, 7, 9, 11, 13, 15, // 1498, 6
+ 2, 3, 14, 15, // 1504, 4
+ 2, 3, 4, 5, 14, 15, // 1508, 6
+ 6, 7, 14, 15, // 1514, 4
+ 2, 3, 5, 9, 14, 15, // 1518, 6
+ 2, 3, 8, 9, 14, 15, // 1524, 6
+ 10, 14, 15, // 1530, 3
+ 0, 4, 5, 9, 10, 14, 15, // 1533, 7
+ 2, 3, 7, 11, 14, 15, // 1540, 6
+ 10, 11, 14, 15, // 1546, 4
+ 7, 10, 11, 14, 15, // 1550, 5
+ 6, 7, 10, 11, 14, 15, // 1555, 6
+ 1, 2, 3, 13, 14, 15, // 1561, 6
+ 5, 6, 7, 13, 14, 15, // 1567, 6
+ 10, 11, 13, 14, 15, // 1573, 5
+ 9, 10, 11, 13, 14, 15, // 1578, 6
+ 0, 4, 8, 9, 12, 13, 14, 15, // 1584, 8
+ 9, 10, 12, 13, 14, 15, // 1592, 6
+ 8, 11, 12, 13, 14, 15, // 1598, 6
+ 3, 7, 10, 11, 12, 13, 14, 15, // 1604, 8
+ };
+ static const int g_shapeRanges[][2] =
+ {
+ { 0, 16 },{ 16, 4 },{ 20, 3 },{ 23, 4 },{ 27, 3 },{ 30, 4 },{ 34, 8 },{ 42, 4 },{ 46, 6 },{ 52, 8 },{ 60, 5 },
+ { 65, 5 },{ 70, 4 },{ 74, 4 },{ 78, 6 },{ 84, 8 },{ 92, 8 },{ 100, 8 },{ 108, 8 },{ 116, 12 },{ 128, 4 },{ 132, 8 },
+ { 140, 8 },{ 148, 10 },{ 158, 6 },{ 164, 8 },{ 172, 12 },{ 184, 8 },{ 192, 5 },{ 197, 3 },{ 200, 4 },{ 204, 6 },{ 210, 8 },
+ { 218, 8 },{ 226, 8 },{ 234, 8 },{ 242, 8 },{ 250, 12 },{ 262, 13 },{ 275, 8 },{ 283, 8 },{ 291, 10 },{ 301, 8 },{ 309, 8 },
+ { 317, 5 },{ 322, 8 },{ 330, 8 },{ 338, 8 },{ 346, 8 },{ 354, 8 },{ 362, 8 },{ 370, 8 },{ 378, 8 },{ 386, 8 },{ 394, 8 },
+ { 402, 8 },{ 410, 8 },{ 418, 4 },{ 422, 8 },{ 430, 6 },{ 436, 8 },{ 444, 10 },{ 454, 8 },{ 462, 12 },{ 474, 8 },{ 482, 8 },
+ { 490, 4 },{ 494, 8 },{ 502, 6 },{ 508, 8 },{ 516, 10 },{ 526, 8 },{ 534, 12 },{ 546, 8 },{ 554, 8 },{ 562, 8 },{ 570, 8 },
+ { 578, 8 },{ 586, 8 },{ 594, 8 },{ 602, 8 },{ 610, 8 },{ 618, 8 },{ 626, 8 },{ 634, 8 },{ 642, 11 },{ 653, 8 },{ 661, 8 },
+ { 669, 6 },{ 675, 8 },{ 683, 8 },{ 691, 3 },{ 694, 4 },{ 698, 8 },{ 706, 8 },{ 714, 8 },{ 722, 8 },{ 730, 8 },{ 738, 10 },
+ { 748, 12 },{ 760, 13 },{ 773, 11 },{ 784, 8 },{ 792, 4 },{ 796, 8 },{ 804, 10 },{ 814, 6 },{ 820, 8 },{ 828, 8 },{ 836, 12 },
+ { 848, 4 },{ 852, 8 },{ 860, 8 },{ 868, 8 },{ 876, 8 },{ 884, 10 },{ 894, 12 },{ 906, 12 },{ 918, 11 },{ 929, 11 },{ 940, 8 },
+ { 948, 10 },{ 958, 12 },{ 970, 8 },{ 978, 12 },{ 990, 13 },{ 1003, 12 },{ 1015, 13 },{ 1028, 12 },{ 1040, 2 },{ 1042, 2 },{ 1044, 4 },
+ { 1048, 5 },{ 1053, 3 },{ 1056, 4 },{ 1060, 4 },{ 1064, 6 },{ 1070, 6 },{ 1076, 7 },{ 1083, 4 },{ 1087, 6 },{ 1093, 4 },{ 1097, 4 },
+ { 1101, 5 },{ 1106, 6 },{ 1112, 7 },{ 1119, 4 },{ 1123, 2 },{ 1125, 5 },{ 1130, 4 },{ 1134, 4 },{ 1138, 6 },{ 1144, 4 },{ 1148, 4 },
+ { 1152, 6 },{ 1158, 6 },{ 1164, 6 },{ 1170, 6 },{ 1176, 6 },{ 1182, 2 },{ 1184, 5 },{ 1189, 4 },{ 1193, 6 },{ 1199, 6 },{ 1205, 4 },
+ { 1209, 4 },{ 1213, 4 },{ 1217, 4 },{ 1221, 6 },{ 1227, 6 },{ 1233, 6 },{ 1239, 4 },{ 1243, 2 },{ 1245, 8 },{ 1253, 3 },{ 1256, 6 },
+ { 1262, 7 },{ 1269, 7 },{ 1276, 4 },{ 1280, 4 },{ 1284, 5 },{ 1289, 5 },{ 1294, 4 },{ 1298, 4 },{ 1302, 6 },{ 1308, 6 },{ 1314, 4 },
+ { 1318, 5 },{ 1323, 6 },{ 1329, 7 },{ 1336, 6 },{ 1342, 7 },{ 1349, 6 },{ 1355, 5 },{ 1360, 4 },{ 1364, 4 },{ 1368, 5 },{ 1373, 4 },
+ { 1377, 4 },{ 1381, 6 },{ 1387, 2 },{ 1389, 4 },{ 1393, 6 },{ 1399, 6 },{ 1405, 6 },{ 1411, 5 },{ 1416, 6 },{ 1422, 2 },{ 1424, 4 },
+ { 1428, 8 },{ 1436, 3 },{ 1439, 7 },{ 1446, 6 },{ 1452, 2 },{ 1454, 4 },{ 1458, 4 },{ 1462, 6 },{ 1468, 6 },{ 1474, 4 },{ 1478, 6 },
+ { 1484, 6 },{ 1490, 4 },{ 1494, 4 },{ 1498, 6 },{ 1504, 4 },{ 1508, 6 },{ 1514, 4 },{ 1518, 6 },{ 1524, 6 },{ 1530, 3 },{ 1533, 7 },
+ { 1540, 6 },{ 1546, 4 },{ 1550, 5 },{ 1555, 6 },{ 1561, 6 },{ 1567, 6 },{ 1573, 5 },{ 1578, 6 },{ 1584, 8 },{ 1592, 6 },{ 1598, 6 },
+ { 1604, 8 },
+ };
+ static const int g_shapes1[][2] =
+ {
+ { 0, 16 }
+ };
+ static const int g_shapes2[64][2] =
+ {
+ { 33, 96 },{ 63, 66 },{ 20, 109 },{ 22, 107 },{ 37, 92 },{ 7, 122 },{ 8, 121 },{ 23, 106 },
+ { 38, 91 },{ 2, 127 },{ 9, 120 },{ 26, 103 },{ 3, 126 },{ 6, 123 },{ 1, 128 },{ 19, 110 },
+ { 15, 114 },{ 124, 5 },{ 72, 57 },{ 115, 14 },{ 125, 4 },{ 70, 59 },{ 100, 29 },{ 60, 69 },
+ { 116, 13 },{ 99, 30 },{ 78, 51 },{ 94, 35 },{ 104, 25 },{ 111, 18 },{ 71, 58 },{ 90, 39 },
+ { 45, 84 },{ 16, 113 },{ 82, 47 },{ 95, 34 },{ 87, 42 },{ 83, 46 },{ 53, 76 },{ 48, 81 },
+ { 68, 61 },{ 105, 24 },{ 98, 31 },{ 88, 41 },{ 75, 54 },{ 43, 86 },{ 52, 77 },{ 117, 12 },
+ { 119, 10 },{ 118, 11 },{ 85, 44 },{ 101, 28 },{ 36, 93 },{ 55, 74 },{ 89, 40 },{ 79, 50 },
+ { 56, 73 },{ 49, 80 },{ 64, 65 },{ 27, 102 },{ 32, 97 },{ 112, 17 },{ 67, 62 },{ 21, 108 },
+ };
+ static const int g_shapes3[64][3] =
+ {
+ { 148, 160, 240 },{ 132, 212, 205 },{ 136, 233, 187 },{ 175, 237, 143 },{ 6, 186, 232 },{ 33, 142, 232 },{ 131, 123, 142 },{ 131, 96, 186 },
+ { 6, 171, 110 },{ 1, 18, 110 },{ 1, 146, 123 },{ 33, 195, 66 },{ 20, 51, 66 },{ 20, 178, 96 },{ 2, 177, 106 },{ 211, 4, 59 },
+ { 8, 191, 91 },{ 230, 14, 29 },{ 1, 188, 234 },{ 151, 110, 168 },{ 20, 144, 238 },{ 137, 66, 206 },{ 173, 179, 232 },{ 209, 194, 186 },
+ { 239, 165, 142 },{ 131, 152, 242 },{ 214, 54, 12 },{ 140, 219, 201 },{ 190, 150, 231 },{ 156, 135, 241 },{ 185, 227, 167 },{ 145, 210, 59 },
+ { 138, 174, 106 },{ 189, 229, 14 },{ 176, 133, 106 },{ 78, 178, 195 },{ 111, 146, 171 },{ 216, 180, 196 },{ 217, 181, 193 },{ 184, 228, 166 },
+ { 192, 225, 153 },{ 134, 141, 123 },{ 6, 222, 198 },{ 149, 183, 96 },{ 33, 226, 164 },{ 161, 215, 51 },{ 197, 221, 18 },{ 1, 223, 199 },
+ { 154, 163, 110 },{ 20, 236, 169 },{ 157, 204, 66 },{ 1, 202, 220 },{ 20, 170, 235 },{ 203, 158, 66 },{ 162, 155, 110 },{ 6, 201, 218 },
+ { 139, 135, 123 },{ 33, 167, 224 },{ 182, 150, 96 },{ 19, 200, 213 },{ 63, 207, 159 },{ 147, 172, 109 },{ 129, 130, 128 },{ 208, 14, 59 },
+ };
+
+ static const int g_shapeList1[] =
+ {
+ 0,
+ };
+
+ static const int g_shapeList2[] =
+ {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128,
+ };
+
+ static const int g_shapeList12[] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128,
+ };
+
+ static const int g_shapeList3[] =
+ {
+ 1, 2, 4, 6, 8, 12, 14, 18, 19, 20, 29,
+ 33, 51, 54, 59, 63, 66, 78, 91, 96, 106, 109,
+ 110, 111, 123, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
+ 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
+ 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
+ 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
+ 235, 236, 237, 238, 239, 240, 241, 242,
+ };
+
+ static const int g_shapeList3Short[] =
+ {
+ 1, 2, 4, 6, 18, 20, 33, 51, 59, 66, 96,
+ 106, 110, 123, 131, 132, 136, 142, 143, 146, 148, 160,
+ 171, 175, 177, 178, 186, 187, 195, 205, 211, 212, 232,
+ 233, 237, 240,
+ };
+
+ static const int g_shapeListAll[] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230,
+ 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
+ 242,
+ };
+
+ static const int g_numShapes1 = sizeof(g_shapeList1) / sizeof(g_shapeList1[0]);
+ static const int g_numShapes2 = sizeof(g_shapeList2) / sizeof(g_shapeList2[0]);
+ static const int g_numShapes12 = sizeof(g_shapeList12) / sizeof(g_shapeList12[0]);
+ static const int g_numShapes3 = sizeof(g_shapeList3) / sizeof(g_shapeList3[0]);
+ static const int g_numShapes3Short = sizeof(g_shapeList3Short) / sizeof(g_shapeList3Short[0]);
+ static const int g_numShapesAll = sizeof(g_shapeListAll) / sizeof(g_shapeListAll[0]);
+ static const int g_numFragments = sizeof(g_fragments) / sizeof(g_fragments[0]);
+ }
+
+ struct PackingVector
+ {
+ uint32_t m_vector[4];
+ int m_offset;
+
+ void Init()
+ {
+ for (int i = 0; i < 4; i++)
+ m_vector[i] = 0;
+
+ m_offset = 0;
+ }
+
+ void InitPacked(const uint32_t *v, int bits)
+ {
+ for (int b = 0; b < bits; b += 32)
+ m_vector[b / 32] = v[b / 32];
+
+ m_offset = bits;
+ }
+
+ inline void Pack(ParallelMath::ScalarUInt16 value, int bits)
+ {
+ int vOffset = m_offset >> 5;
+ int bitOffset = m_offset & 0x1f;
+
+ m_vector[vOffset] |= (static_cast<uint32_t>(value) << bitOffset) & static_cast<uint32_t>(0xffffffff);
+
+ int overflowBits = bitOffset + bits - 32;
+ if (overflowBits > 0)
+ m_vector[vOffset + 1] |= (static_cast<uint32_t>(value) >> (bits - overflowBits));
+
+ m_offset += bits;
+ }
+
+ inline void Flush(uint8_t* output)
+ {
+ assert(m_offset == 128);
+
+ for (int v = 0; v < 4; v++)
+ {
+ uint32_t chunk = m_vector[v];
+ for (int b = 0; b < 4; b++)
+ output[v * 4 + b] = static_cast<uint8_t>((chunk >> (b * 8)) & 0xff);
+ }
+ }
+ };
+
+
+ struct UnpackingVector
+ {
+ uint32_t m_vector[4];
+
+ void Init(const uint8_t *bytes)
+ {
+ for (int i = 0; i < 4; i++)
+ m_vector[i] = 0;
+
+ for (int b = 0; b < 16; b++)
+ m_vector[b / 4] |= (bytes[b] << ((b % 4) * 8));
+ }
+
+ inline void UnpackStart(uint32_t *v, int bits)
+ {
+ for (int b = 0; b < bits; b += 32)
+ v[b / 32] = m_vector[b / 32];
+
+ int entriesShifted = bits / 32;
+ int carry = bits % 32;
+
+ for (int i = entriesShifted; i < 4; i++)
+ m_vector[i - entriesShifted] = m_vector[i];
+
+ int entriesRemaining = 4 - entriesShifted;
+ if (carry)
+ {
+ uint32_t bitMask = (1 << carry) - 1;
+ for (int i = 0; i < 4; i++)
+ {
+ m_vector[i] >>= carry;
+ if (i != 3)
+ m_vector[i] |= (m_vector[i + 1] & bitMask) << (32 - carry);
+ }
+ }
+ }
+
+ inline ParallelMath::ScalarUInt16 Unpack(int bits)
+ {
+ uint32_t bitMask = (1 << bits) - 1;
+
+ ParallelMath::ScalarUInt16 result = static_cast<ParallelMath::ScalarUInt16>(m_vector[0] & bitMask);
+
+ for (int i = 0; i < 4; i++)
+ {
+ m_vector[i] >>= bits;
+ if (i != 3)
+ m_vector[i] |= (m_vector[i + 1] & bitMask) << (32 - bits);
+ }
+
+ return result;
+ }
+ };
+
+ ParallelMath::Float ScaleHDRValue(const ParallelMath::Float &v, bool isSigned)
+ {
+ if (isSigned)
+ {
+ ParallelMath::Float offset = ParallelMath::Select(ParallelMath::Less(v, ParallelMath::MakeFloatZero()), ParallelMath::MakeFloat(-30.0f), ParallelMath::MakeFloat(30.0f));
+ return (v * 32.0f + offset) / 31.0f;
+ }
+ else
+ return (v * 64.0f + 30.0f) / 31.0f;
+ }
+
+ ParallelMath::SInt16 UnscaleHDRValueSigned(const ParallelMath::SInt16 &v)
+ {
+#ifdef CVTT_ENABLE_ASSERTS
+ for (int i = 0; i < ParallelMath::ParallelSize; i++)
+ assert(ParallelMath::Extract(v, i) != -32768)
+#endif
+
+ ParallelMath::Int16CompFlag negative = ParallelMath::Less(v, ParallelMath::MakeSInt16(0));
+ ParallelMath::UInt15 absComp = ParallelMath::LosslessCast<ParallelMath::UInt15>::Cast(ParallelMath::Select(negative, ParallelMath::SInt16(ParallelMath::MakeSInt16(0) - v), v));
+
+ ParallelMath::UInt31 multiplied = ParallelMath::XMultiply(absComp, ParallelMath::MakeUInt15(31));
+ ParallelMath::UInt31 shifted = ParallelMath::RightShift(multiplied, 5);
+ ParallelMath::UInt15 absCompScaled = ParallelMath::ToUInt15(shifted);
+ ParallelMath::SInt16 signBits = ParallelMath::SelectOrZero(negative, ParallelMath::MakeSInt16(-32768));
+
+ return ParallelMath::LosslessCast<ParallelMath::SInt16>::Cast(absCompScaled) | signBits;
+ }
+
+ ParallelMath::UInt15 UnscaleHDRValueUnsigned(const ParallelMath::UInt16 &v)
+ {
+ return ParallelMath::ToUInt15(ParallelMath::RightShift(ParallelMath::XMultiply(v, ParallelMath::MakeUInt15(31)), 6));
+ }
+
+ void UnscaleHDREndpoints(const ParallelMath::AInt16 inEP[2][3], ParallelMath::AInt16 outEP[2][3], bool isSigned)
+ {
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ if (isSigned)
+ outEP[epi][ch] = ParallelMath::LosslessCast<ParallelMath::AInt16>::Cast(UnscaleHDRValueSigned(ParallelMath::LosslessCast<ParallelMath::SInt16>::Cast(inEP[epi][ch])));
+ else
+ outEP[epi][ch] = ParallelMath::LosslessCast<ParallelMath::AInt16>::Cast(UnscaleHDRValueUnsigned(ParallelMath::LosslessCast<ParallelMath::UInt16>::Cast(inEP[epi][ch])));
+ }
+ }
+ }
+
+ struct SinglePlaneTemporaries
+ {
+ UnfinishedEndpoints<3> unfinishedRGB[BC7Data::g_numShapesAll];
+ UnfinishedEndpoints<4> unfinishedRGBA[BC7Data::g_numShapes12];
+
+ ParallelMath::UInt15 fragmentBestIndexes[BC7Data::g_numFragments];
+ ParallelMath::UInt15 shapeBestEP[BC7Data::g_numShapesAll][2][4];
+ ParallelMath::Float shapeBestError[BC7Data::g_numShapesAll];
+ };
+ }
+}
+
+void cvtt::Internal::BC7Computer::TweakAlpha(const MUInt15 original[2], int tweak, int range, MUInt15 result[2])
+{
+ ParallelMath::RoundTowardNearestForScope roundingMode;
+
+ float tf[2];
+ Util::ComputeTweakFactors(tweak, range, tf);
+
+ MFloat base = ParallelMath::ToFloat(original[0]);
+ MFloat offs = ParallelMath::ToFloat(original[1]) - base;
+
+ result[0] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(base + offs * tf[0], 0.0f, 255.0f), &roundingMode);
+ result[1] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(base + offs * tf[1], 0.0f, 255.0f), &roundingMode);
+}
+
+void cvtt::Internal::BC7Computer::Quantize(MUInt15* color, int bits, int channels)
+{
+ for (int ch = 0; ch < channels; ch++)
+ color[ch] = ParallelMath::RightShift(((color[ch] << bits) - color[ch]) + ParallelMath::MakeUInt15(127 + (1 << (7 - bits))), 8);
+}
+
+void cvtt::Internal::BC7Computer::QuantizeP(MUInt15* color, int bits, uint16_t p, int channels)
+{
+ int16_t addend;
+ if (p)
+ addend = ((1 << (8 - bits)) - 1);
+ else
+ addend = 255;
+
+ for (int ch = 0; ch < channels; ch++)
+ {
+ MUInt16 ch16 = ParallelMath::LosslessCast<MUInt16>::Cast(color[ch]);
+ ch16 = ParallelMath::RightShift((ch16 << (bits + 1)) - ch16 + addend, 9);
+ ch16 = (ch16 << 1) | ParallelMath::MakeUInt16(p);
+ color[ch] = ParallelMath::LosslessCast<MUInt15>::Cast(ch16);
+ }
+}
+
+void cvtt::Internal::BC7Computer::Unquantize(MUInt15* color, int bits, int channels)
+{
+ for (int ch = 0; ch < channels; ch++)
+ {
+ MUInt15 clr = color[ch];
+ clr = clr << (8 - bits);
+ color[ch] = clr | ParallelMath::RightShift(clr, bits);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints0(MUInt15 ep[2][4], uint16_t p[2])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ QuantizeP(ep[j], 4, p[j], 3);
+ Unquantize(ep[j], 5, 3);
+ ep[j][3] = ParallelMath::MakeUInt15(255);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints1(MUInt15 ep[2][4], uint16_t p)
+{
+ for (int j = 0; j < 2; j++)
+ {
+ QuantizeP(ep[j], 6, p, 3);
+ Unquantize(ep[j], 7, 3);
+ ep[j][3] = ParallelMath::MakeUInt15(255);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints2(MUInt15 ep[2][4])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ Quantize(ep[j], 5, 3);
+ Unquantize(ep[j], 5, 3);
+ ep[j][3] = ParallelMath::MakeUInt15(255);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints3(MUInt15 ep[2][4], uint16_t p[2])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ QuantizeP(ep[j], 7, p[j], 3);
+ ep[j][3] = ParallelMath::MakeUInt15(255);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints4(MUInt15 epRGB[2][3], MUInt15 epA[2])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ Quantize(epRGB[j], 5, 3);
+ Unquantize(epRGB[j], 5, 3);
+
+ Quantize(epA + j, 6, 1);
+ Unquantize(epA + j, 6, 1);
+ }
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints5(MUInt15 epRGB[2][3], MUInt15 epA[2])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ Quantize(epRGB[j], 7, 3);
+ Unquantize(epRGB[j], 7, 3);
+ }
+
+ // Alpha is full precision
+ (void)epA;
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints6(MUInt15 ep[2][4], uint16_t p[2])
+{
+ for (int j = 0; j < 2; j++)
+ QuantizeP(ep[j], 7, p[j], 4);
+}
+
+void cvtt::Internal::BC7Computer::CompressEndpoints7(MUInt15 ep[2][4], uint16_t p[2])
+{
+ for (int j = 0; j < 2; j++)
+ {
+ QuantizeP(ep[j], 5, p[j], 4);
+ Unquantize(ep[j], 6, 4);
+ }
+}
+
+void cvtt::Internal::BC7Computer::TrySingleColorRGBAMultiTable(uint32_t flags, const MUInt15 pixels[16][4], const MFloat average[4], int numRealChannels, const uint8_t *fragmentStart, int shapeLength, const MFloat &staticAlphaError, const ParallelMath::Int16CompFlag punchThroughInvalid[4], MFloat& shapeBestError, MUInt15 shapeBestEP[2][4], MUInt15 *fragmentBestIndexes, const float *channelWeightsSq, const cvtt::Tables::BC7SC::Table*const* tables, int numTables, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ MFloat bestAverageError = ParallelMath::MakeFloat(FLT_MAX);
+
+ MUInt15 intAverage[4];
+ for (int ch = 0; ch < 4; ch++)
+ intAverage[ch] = ParallelMath::RoundAndConvertToU15(average[ch], rtn);
+
+ MUInt15 eps[2][4];
+ MUInt15 reconstructed[4];
+ MUInt15 index = ParallelMath::MakeUInt15(0);
+
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ eps[epi][ch] = ParallelMath::MakeUInt15(0);
+ eps[epi][3] = ParallelMath::MakeUInt15(255);
+ }
+
+ for (int ch = 0; ch < 3; ch++)
+ reconstructed[ch] = ParallelMath::MakeUInt15(0);
+ reconstructed[3] = ParallelMath::MakeUInt15(255);
+
+ // Depending on the target index and parity bits, there are multiple valid solid colors.
+ // We want to find the one closest to the actual average.
+ MFloat epsAverageDiff = ParallelMath::MakeFloat(FLT_MAX);
+ for (int t = 0; t < numTables; t++)
+ {
+ const cvtt::Tables::BC7SC::Table& table = *(tables[t]);
+
+ ParallelMath::Int16CompFlag pti = punchThroughInvalid[table.m_pBits];
+
+ MUInt15 candidateReconstructed[4];
+ MUInt15 candidateEPs[2][4];
+
+ for (int i = 0; i < ParallelMath::ParallelSize; i++)
+ {
+ for (int ch = 0; ch < numRealChannels; ch++)
+ {
+ ParallelMath::ScalarUInt16 avgValue = ParallelMath::Extract(intAverage[ch], i);
+ assert(avgValue >= 0 && avgValue <= 255);
+
+ const cvtt::Tables::BC7SC::TableEntry &entry = table.m_entries[avgValue];
+
+ ParallelMath::PutUInt15(candidateEPs[0][ch], i, entry.m_min);
+ ParallelMath::PutUInt15(candidateEPs[1][ch], i, entry.m_max);
+ ParallelMath::PutUInt15(candidateReconstructed[ch], i, entry.m_actualColor);
+ }
+ }
+
+ MFloat avgError = ParallelMath::MakeFloatZero();
+ for (int ch = 0; ch < numRealChannels; ch++)
+ {
+ MFloat delta = ParallelMath::ToFloat(candidateReconstructed[ch]) - average[ch];
+ avgError = avgError + delta * delta * channelWeightsSq[ch];
+ }
+
+ ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(avgError, bestAverageError));
+ better = ParallelMath::AndNot(pti, better); // Mask out punch-through invalidations
+
+ if (ParallelMath::AnySet(better))
+ {
+ ParallelMath::ConditionalSet(bestAverageError, ParallelMath::Int16FlagToFloat(better), avgError);
+
+ MUInt15 candidateIndex = ParallelMath::MakeUInt15(table.m_index);
+
+ ParallelMath::ConditionalSet(index, better, candidateIndex);
+
+ for (int ch = 0; ch < numRealChannels; ch++)
+ ParallelMath::ConditionalSet(reconstructed[ch], better, candidateReconstructed[ch]);
+
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < numRealChannels; ch++)
+ ParallelMath::ConditionalSet(eps[epi][ch], better, candidateEPs[epi][ch]);
+ }
+ }
+
+ AggregatedError<4> aggError;
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ {
+ int px = fragmentStart[pxi];
+
+ BCCommon::ComputeErrorLDR<4>(flags, reconstructed, pixels[px], numRealChannels, aggError);
+ }
+
+ MFloat error = aggError.Finalize(flags, channelWeightsSq) + staticAlphaError;
+
+ ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, shapeBestError));
+ if (ParallelMath::AnySet(better))
+ {
+ shapeBestError = ParallelMath::Min(shapeBestError, error);
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < numRealChannels; ch++)
+ ParallelMath::ConditionalSet(shapeBestEP[epi][ch], better, eps[epi][ch]);
+ }
+
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ ParallelMath::ConditionalSet(fragmentBestIndexes[pxi], better, index);
+ }
+}
+
+void cvtt::Internal::BC7Computer::TrySinglePlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds, BC67::WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ if (numRefineRounds < 1)
+ numRefineRounds = 1;
+
+ float channelWeightsSq[4];
+
+ for (int ch = 0; ch < 4; ch++)
+ channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
+
+ SinglePlaneTemporaries temps;
+
+ MUInt15 maxAlpha = ParallelMath::MakeUInt15(0);
+ MUInt15 minAlpha = ParallelMath::MakeUInt15(255);
+ ParallelMath::Int16CompFlag isPunchThrough = ParallelMath::MakeBoolInt16(true);
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 a = pixels[px][3];
+ maxAlpha = ParallelMath::Max(maxAlpha, a);
+ minAlpha = ParallelMath::Min(minAlpha, a);
+
+ isPunchThrough = (isPunchThrough & (ParallelMath::Equal(a, ParallelMath::MakeUInt15(0)) | ParallelMath::Equal(a, ParallelMath::MakeUInt15(255))));
+ }
+
+ ParallelMath::Int16CompFlag blockHasNonMaxAlpha = ParallelMath::Less(minAlpha, ParallelMath::MakeUInt15(255));
+ ParallelMath::Int16CompFlag blockHasNonZeroAlpha = ParallelMath::Less(ParallelMath::MakeUInt15(0), maxAlpha);
+
+ bool anyBlockHasAlpha = ParallelMath::AnySet(blockHasNonMaxAlpha);
+
+ // Try RGB modes if any block has a min alpha 251 or higher
+ bool allowRGBModes = ParallelMath::AnySet(ParallelMath::Less(ParallelMath::MakeUInt15(250), minAlpha));
+
+ // Try mode 7 if any block has alpha.
+ // Mode 7 is almost never selected for RGB blocks because mode 4 has very accurate 7.7.7.1 endpoints
+ // and its parity bit doesn't affect alpha, meaning mode 7 can only be better in extremely specific
+ // situations, and only by at most 1 unit of error per pixel.
+ bool allowMode7 = anyBlockHasAlpha || (encodingPlan.mode7RGBPartitionEnabled != 0);
+
+ MFloat preWeightedPixels[16][4];
+
+ BCCommon::PreWeightPixelsLDR<4>(preWeightedPixels, pixels, channelWeights);
+
+ // Get initial RGB endpoints
+ if (allowRGBModes)
+ {
+ const uint8_t *shapeList = encodingPlan.rgbShapeList;
+ int numShapesToEvaluate = encodingPlan.rgbNumShapesToEvaluate;
+
+ for (int shapeIter = 0; shapeIter < numShapesToEvaluate; shapeIter++)
+ {
+ int shape = shapeList[shapeIter];
+
+ int shapeStart = BC7Data::g_shapeRanges[shape][0];
+ int shapeSize = BC7Data::g_shapeRanges[shape][1];
+
+ EndpointSelector<3, 8> epSelector;
+
+ for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
+ {
+ for (int spx = 0; spx < shapeSize; spx++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + spx];
+ epSelector.ContributePass(preWeightedPixels[px], epPass, ParallelMath::MakeFloat(1.0f));
+ }
+ epSelector.FinishPass(epPass);
+ }
+ temps.unfinishedRGB[shape] = epSelector.GetEndpoints(channelWeights);
+ }
+ }
+
+ // Get initial RGBA endpoints
+ {
+ const uint8_t *shapeList = encodingPlan.rgbaShapeList;
+ int numShapesToEvaluate = encodingPlan.rgbaNumShapesToEvaluate;
+
+ for (int shapeIter = 0; shapeIter < numShapesToEvaluate; shapeIter++)
+ {
+ int shape = shapeList[shapeIter];
+
+ if (anyBlockHasAlpha || !allowRGBModes)
+ {
+ int shapeStart = BC7Data::g_shapeRanges[shape][0];
+ int shapeSize = BC7Data::g_shapeRanges[shape][1];
+
+ EndpointSelector<4, 8> epSelector;
+
+ for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
+ {
+ for (int spx = 0; spx < shapeSize; spx++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + spx];
+ epSelector.ContributePass(preWeightedPixels[px], epPass, ParallelMath::MakeFloat(1.0f));
+ }
+ epSelector.FinishPass(epPass);
+ }
+ temps.unfinishedRGBA[shape] = epSelector.GetEndpoints(channelWeights);
+ }
+ else
+ {
+ temps.unfinishedRGBA[shape] = temps.unfinishedRGB[shape].ExpandTo<4>(255);
+ }
+ }
+ }
+
+ for (uint16_t mode = 0; mode <= 7; mode++)
+ {
+ if (mode == 4 || mode == 5)
+ continue;
+
+ if (mode < 4 && !allowRGBModes)
+ continue;
+
+ if (mode == 7 && !allowMode7)
+ continue;
+
+ uint64_t partitionEnabledBits = 0;
+ switch (mode)
+ {
+ case 0:
+ partitionEnabledBits = encodingPlan.mode0PartitionEnabled;
+ break;
+ case 1:
+ partitionEnabledBits = encodingPlan.mode1PartitionEnabled;
+ break;
+ case 2:
+ partitionEnabledBits = encodingPlan.mode2PartitionEnabled;
+ break;
+ case 3:
+ partitionEnabledBits = encodingPlan.mode3PartitionEnabled;
+ break;
+ case 6:
+ partitionEnabledBits = encodingPlan.mode6Enabled ? 1 : 0;
+ break;
+ case 7:
+ if (anyBlockHasAlpha)
+ partitionEnabledBits = encodingPlan.mode7RGBAPartitionEnabled;
+ else
+ partitionEnabledBits = encodingPlan.mode7RGBPartitionEnabled;
+ break;
+ default:
+ break;
+ }
+
+ bool isRGB = (mode < 4);
+
+ unsigned int numPartitions = 1 << BC7Data::g_modes[mode].m_partitionBits;
+ int numSubsets = BC7Data::g_modes[mode].m_numSubsets;
+ int indexPrec = BC7Data::g_modes[mode].m_indexBits;
+
+ int parityBitMax = 1;
+ if (BC7Data::g_modes[mode].m_pBitMode == BC7Data::PBitMode_PerEndpoint)
+ parityBitMax = 4;
+ else if (BC7Data::g_modes[mode].m_pBitMode == BC7Data::PBitMode_PerSubset)
+ parityBitMax = 2;
+
+ int numRealChannels = isRGB ? 3 : 4;
+
+ int numShapes;
+ const int *shapeList;
+
+ if (numSubsets == 1)
+ {
+ numShapes = BC7Data::g_numShapes1;
+ shapeList = BC7Data::g_shapeList1;
+ }
+ else if (numSubsets == 2)
+ {
+ numShapes = BC7Data::g_numShapes2;
+ shapeList = BC7Data::g_shapeList2;
+ }
+ else
+ {
+ assert(numSubsets == 3);
+ if (numPartitions == 16)
+ {
+ numShapes = BC7Data::g_numShapes3Short;
+ shapeList = BC7Data::g_shapeList3Short;
+ }
+ else
+ {
+ assert(numPartitions == 64);
+ numShapes = BC7Data::g_numShapes3;
+ shapeList = BC7Data::g_shapeList3;
+ }
+ }
+
+ for (int slot = 0; slot < BC7Data::g_numShapesAll; slot++)
+ temps.shapeBestError[slot] = ParallelMath::MakeFloat(FLT_MAX);
+
+ for (int shapeIter = 0; shapeIter < numShapes; shapeIter++)
+ {
+ int shape = shapeList[shapeIter];
+
+ int numTweakRounds = 0;
+ if (isRGB)
+ numTweakRounds = encodingPlan.seedPointsForShapeRGB[shape];
+ else
+ numTweakRounds = encodingPlan.seedPointsForShapeRGBA[shape];
+
+ if (numTweakRounds == 0)
+ continue;
+
+ if (numTweakRounds > MaxTweakRounds)
+ numTweakRounds = MaxTweakRounds;
+
+ int shapeStart = BC7Data::g_shapeRanges[shape][0];
+ int shapeLength = BC7Data::g_shapeRanges[shape][1];
+
+ AggregatedError<1> alphaAggError;
+ if (isRGB && anyBlockHasAlpha)
+ {
+ MUInt15 filledAlpha[1] = { ParallelMath::MakeUInt15(255) };
+
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + pxi];
+ MUInt15 original[1] = { pixels[px][3] };
+ BCCommon::ComputeErrorLDR<1>(flags, filledAlpha, original, alphaAggError);
+ }
+ }
+
+ float alphaWeightsSq[1] = { channelWeightsSq[3] };
+ MFloat staticAlphaError = alphaAggError.Finalize(flags, alphaWeightsSq);
+
+ MUInt15 tweakBaseEP[MaxTweakRounds][2][4];
+
+ for (int tweak = 0; tweak < numTweakRounds; tweak++)
+ {
+ if (isRGB)
+ {
+ temps.unfinishedRGB[shape].FinishLDR(tweak, 1 << indexPrec, tweakBaseEP[tweak][0], tweakBaseEP[tweak][1]);
+ tweakBaseEP[tweak][0][3] = tweakBaseEP[tweak][1][3] = ParallelMath::MakeUInt15(255);
+ }
+ else
+ {
+ temps.unfinishedRGBA[shape].FinishLDR(tweak, 1 << indexPrec, tweakBaseEP[tweak][0], tweakBaseEP[tweak][1]);
+ }
+ }
+
+ ParallelMath::Int16CompFlag punchThroughInvalid[4];
+ for (int pIter = 0; pIter < parityBitMax; pIter++)
+ {
+ punchThroughInvalid[pIter] = ParallelMath::MakeBoolInt16(false);
+
+ if ((flags & Flags::BC7_RespectPunchThrough) && (mode == 6 || mode == 7))
+ {
+ // Modes 6 and 7 have parity bits that affect alpha
+ if (pIter == 0)
+ punchThroughInvalid[pIter] = (isPunchThrough & blockHasNonZeroAlpha);
+ else if (pIter == parityBitMax - 1)
+ punchThroughInvalid[pIter] = (isPunchThrough & blockHasNonMaxAlpha);
+ else
+ punchThroughInvalid[pIter] = isPunchThrough;
+ }
+ }
+
+ for (int pIter = 0; pIter < parityBitMax; pIter++)
+ {
+ if (ParallelMath::AllSet(punchThroughInvalid[pIter]))
+ continue;
+
+ bool needPunchThroughCheck = ParallelMath::AnySet(punchThroughInvalid[pIter]);
+
+ for (int tweak = 0; tweak < numTweakRounds; tweak++)
+ {
+ uint16_t p[2];
+ p[0] = (pIter & 1);
+ p[1] = ((pIter >> 1) & 1);
+
+ MUInt15 ep[2][4];
+
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 4; ch++)
+ ep[epi][ch] = tweakBaseEP[tweak][epi][ch];
+
+ for (int refine = 0; refine < numRefineRounds; refine++)
+ {
+ switch (mode)
+ {
+ case 0:
+ CompressEndpoints0(ep, p);
+ break;
+ case 1:
+ CompressEndpoints1(ep, p[0]);
+ break;
+ case 2:
+ CompressEndpoints2(ep);
+ break;
+ case 3:
+ CompressEndpoints3(ep, p);
+ break;
+ case 6:
+ CompressEndpoints6(ep, p);
+ break;
+ case 7:
+ CompressEndpoints7(ep, p);
+ break;
+ default:
+ assert(false);
+ break;
+ };
+
+ MFloat shapeError = ParallelMath::MakeFloatZero();
+
+ IndexSelector<4> indexSelector;
+ indexSelector.Init<false>(channelWeights, ep, 1 << indexPrec);
+
+ EndpointRefiner<4> epRefiner;
+ epRefiner.Init(1 << indexPrec, channelWeights);
+
+ MUInt15 indexes[16];
+
+ AggregatedError<4> aggError;
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + pxi];
+
+ MUInt15 index;
+ MUInt15 reconstructed[4];
+
+ index = indexSelector.SelectIndexLDR(floatPixels[px], rtn);
+ indexSelector.ReconstructLDR_BC7(index, reconstructed, numRealChannels);
+
+ if (flags & cvtt::Flags::BC7_FastIndexing)
+ BCCommon::ComputeErrorLDR<4>(flags, reconstructed, pixels[px], numRealChannels, aggError);
+ else
+ {
+ MFloat error = BCCommon::ComputeErrorLDRSimple<4>(flags, reconstructed, pixels[px], numRealChannels, channelWeightsSq);
+
+ MUInt15 altIndexes[2];
+ altIndexes[0] = ParallelMath::Max(index, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
+ altIndexes[1] = ParallelMath::Min(index + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << indexPrec) - 1)));
+
+ for (int ii = 0; ii < 2; ii++)
+ {
+ indexSelector.ReconstructLDR_BC7(altIndexes[ii], reconstructed, numRealChannels);
+
+ MFloat altError = BCCommon::ComputeErrorLDRSimple<4>(flags, reconstructed, pixels[px], numRealChannels, channelWeightsSq);
+ ParallelMath::Int16CompFlag better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altError, error));
+ error = ParallelMath::Min(error, altError);
+ ParallelMath::ConditionalSet(index, better, altIndexes[ii]);
+ }
+
+ shapeError = shapeError + error;
+ }
+
+ if (refine != numRefineRounds - 1)
+ epRefiner.ContributeUnweightedPW(preWeightedPixels[px], index, numRealChannels);
+
+ indexes[pxi] = index;
+ }
+
+ if (flags & cvtt::Flags::BC7_FastIndexing)
+ shapeError = aggError.Finalize(flags, channelWeightsSq);
+
+ if (isRGB)
+ shapeError = shapeError + staticAlphaError;
+
+ ParallelMath::FloatCompFlag shapeErrorBetter;
+ ParallelMath::Int16CompFlag shapeErrorBetter16;
+
+ shapeErrorBetter = ParallelMath::Less(shapeError, temps.shapeBestError[shape]);
+ shapeErrorBetter16 = ParallelMath::FloatFlagToInt16(shapeErrorBetter);
+
+ if (ParallelMath::AnySet(shapeErrorBetter16))
+ {
+ bool punchThroughOK = true;
+ if (needPunchThroughCheck)
+ {
+ shapeErrorBetter16 = ParallelMath::AndNot(punchThroughInvalid[pIter], shapeErrorBetter16);
+ shapeErrorBetter = ParallelMath::Int16FlagToFloat(shapeErrorBetter16);
+
+ if (!ParallelMath::AnySet(shapeErrorBetter16))
+ punchThroughOK = false;
+ }
+
+ if (punchThroughOK)
+ {
+ ParallelMath::ConditionalSet(temps.shapeBestError[shape], shapeErrorBetter, shapeError);
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < numRealChannels; ch++)
+ ParallelMath::ConditionalSet(temps.shapeBestEP[shape][epi][ch], shapeErrorBetter16, ep[epi][ch]);
+
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ ParallelMath::ConditionalSet(temps.fragmentBestIndexes[shapeStart + pxi], shapeErrorBetter16, indexes[pxi]);
+ }
+ }
+
+ if (refine != numRefineRounds - 1)
+ epRefiner.GetRefinedEndpointsLDR(ep, numRealChannels, rtn);
+ } // refine
+ } // tweak
+ } // p
+
+ if (flags & cvtt::Flags::BC7_TrySingleColor)
+ {
+ MUInt15 total[4];
+ for (int ch = 0; ch < 4; ch++)
+ total[ch] = ParallelMath::MakeUInt15(0);
+
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + pxi];
+ for (int ch = 0; ch < 4; ch++)
+ total[ch] = total[ch] + pixels[pxi][ch];
+ }
+
+ MFloat rcpShapeLength = ParallelMath::MakeFloat(1.0f / static_cast<float>(shapeLength));
+ MFloat average[4];
+ for (int ch = 0; ch < 4; ch++)
+ average[ch] = ParallelMath::ToFloat(total[ch]) * rcpShapeLength;
+
+ const uint8_t *fragment = BC7Data::g_fragments + shapeStart;
+ MFloat &shapeBestError = temps.shapeBestError[shape];
+ MUInt15 (&shapeBestEP)[2][4] = temps.shapeBestEP[shape];
+ MUInt15 *fragmentBestIndexes = temps.fragmentBestIndexes + shapeStart;
+
+ const cvtt::Tables::BC7SC::Table **scTables = NULL;
+ int numSCTables = 0;
+
+ const cvtt::Tables::BC7SC::Table *tables0[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode0_p00_i1,
+ &cvtt::Tables::BC7SC::g_mode0_p00_i2,
+ &cvtt::Tables::BC7SC::g_mode0_p00_i3,
+ &cvtt::Tables::BC7SC::g_mode0_p01_i1,
+ &cvtt::Tables::BC7SC::g_mode0_p01_i2,
+ &cvtt::Tables::BC7SC::g_mode0_p01_i3,
+ &cvtt::Tables::BC7SC::g_mode0_p10_i1,
+ &cvtt::Tables::BC7SC::g_mode0_p10_i2,
+ &cvtt::Tables::BC7SC::g_mode0_p10_i3,
+ &cvtt::Tables::BC7SC::g_mode0_p11_i1,
+ &cvtt::Tables::BC7SC::g_mode0_p11_i2,
+ &cvtt::Tables::BC7SC::g_mode0_p11_i3,
+ };
+
+ const cvtt::Tables::BC7SC::Table *tables1[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode1_p0_i1,
+ &cvtt::Tables::BC7SC::g_mode1_p0_i2,
+ &cvtt::Tables::BC7SC::g_mode1_p0_i3,
+ &cvtt::Tables::BC7SC::g_mode1_p1_i1,
+ &cvtt::Tables::BC7SC::g_mode1_p1_i2,
+ &cvtt::Tables::BC7SC::g_mode1_p1_i3,
+ };
+
+ const cvtt::Tables::BC7SC::Table *tables2[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode2,
+ };
+
+ const cvtt::Tables::BC7SC::Table *tables3[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode3_p0,
+ &cvtt::Tables::BC7SC::g_mode3_p1,
+ };
+
+ const cvtt::Tables::BC7SC::Table *tables6[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode6_p0_i1,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i2,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i3,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i4,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i5,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i6,
+ &cvtt::Tables::BC7SC::g_mode6_p0_i7,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i1,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i2,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i3,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i4,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i5,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i6,
+ &cvtt::Tables::BC7SC::g_mode6_p1_i7,
+ };
+
+ const cvtt::Tables::BC7SC::Table *tables7[] =
+ {
+ &cvtt::Tables::BC7SC::g_mode7_p00,
+ &cvtt::Tables::BC7SC::g_mode7_p01,
+ &cvtt::Tables::BC7SC::g_mode7_p10,
+ &cvtt::Tables::BC7SC::g_mode7_p11,
+ };
+
+ switch (mode)
+ {
+ case 0:
+ {
+ scTables = tables0;
+ numSCTables = sizeof(tables0) / sizeof(tables0[0]);
+ }
+ break;
+ case 1:
+ {
+ scTables = tables1;
+ numSCTables = sizeof(tables1) / sizeof(tables1[0]);
+ }
+ break;
+ case 2:
+ {
+
+ scTables = tables2;
+ numSCTables = sizeof(tables2) / sizeof(tables2[0]);
+ }
+ break;
+ case 3:
+ {
+ scTables = tables3;
+ numSCTables = sizeof(tables3) / sizeof(tables3[0]);
+ }
+ break;
+ case 6:
+ {
+ scTables = tables6;
+ numSCTables = sizeof(tables6) / sizeof(tables6[0]);
+ }
+ break;
+ case 7:
+ {
+ scTables = tables7;
+ numSCTables = sizeof(tables7) / sizeof(tables7[0]);
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ TrySingleColorRGBAMultiTable(flags, pixels, average, numRealChannels, fragment, shapeLength, staticAlphaError, punchThroughInvalid, shapeBestError, shapeBestEP, fragmentBestIndexes, channelWeightsSq, scTables, numSCTables, rtn);
+ }
+ } // shapeIter
+
+ uint64_t partitionsEnabledBits = 0xffffffffffffffffULL;
+
+ switch (mode)
+ {
+ case 0:
+ partitionsEnabledBits = encodingPlan.mode0PartitionEnabled;
+ break;
+ case 1:
+ partitionsEnabledBits = encodingPlan.mode1PartitionEnabled;
+ break;
+ case 2:
+ partitionsEnabledBits = encodingPlan.mode2PartitionEnabled;
+ break;
+ case 3:
+ partitionsEnabledBits = encodingPlan.mode3PartitionEnabled;
+ break;
+ case 6:
+ partitionsEnabledBits = encodingPlan.mode6Enabled ? 1 : 0;
+ break;
+ case 7:
+ if (anyBlockHasAlpha)
+ partitionEnabledBits = encodingPlan.mode7RGBAPartitionEnabled;
+ else
+ partitionEnabledBits = encodingPlan.mode7RGBPartitionEnabled;
+ break;
+ default:
+ break;
+ };
+
+ for (uint16_t partition = 0; partition < numPartitions; partition++)
+ {
+ if (((partitionsEnabledBits >> partition) & 1) == 0)
+ continue;
+
+ const int *partitionShapes;
+ if (numSubsets == 1)
+ partitionShapes = BC7Data::g_shapes1[partition];
+ else if (numSubsets == 2)
+ partitionShapes = BC7Data::g_shapes2[partition];
+ else
+ {
+ assert(numSubsets == 3);
+ partitionShapes = BC7Data::g_shapes3[partition];
+ }
+
+ MFloat totalError = ParallelMath::MakeFloatZero();
+ for (int subset = 0; subset < numSubsets; subset++)
+ totalError = totalError + temps.shapeBestError[partitionShapes[subset]];
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(totalError, work.m_error);
+ ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
+
+ if (mode == 7 && anyBlockHasAlpha)
+ {
+ // Some lanes could be better, but we filter them out to ensure consistency with scalar
+ bool isRGBAllowedForThisPartition = (((encodingPlan.mode7RGBPartitionEnabled >> partition) & 1) != 0);
+
+ if (!isRGBAllowedForThisPartition)
+ {
+ errorBetter16 = (errorBetter16 & blockHasNonMaxAlpha);
+ errorBetter = ParallelMath::Int16FlagToFloat(errorBetter16);
+ }
+ }
+
+ if (ParallelMath::AnySet(errorBetter16))
+ {
+ for (int subset = 0; subset < numSubsets; subset++)
+ {
+ int shape = partitionShapes[subset];
+ int shapeStart = BC7Data::g_shapeRanges[shape][0];
+ int shapeLength = BC7Data::g_shapeRanges[shape][1];
+
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 4; ch++)
+ ParallelMath::ConditionalSet(work.m_ep[subset][epi][ch], errorBetter16, temps.shapeBestEP[shape][epi][ch]);
+
+ for (int pxi = 0; pxi < shapeLength; pxi++)
+ {
+ int px = BC7Data::g_fragments[shapeStart + pxi];
+ ParallelMath::ConditionalSet(work.m_indexes[px], errorBetter16, temps.fragmentBestIndexes[shapeStart + pxi]);
+ }
+ }
+
+ ParallelMath::ConditionalSet(work.m_error, errorBetter, totalError);
+ ParallelMath::ConditionalSet(work.m_mode, errorBetter16, ParallelMath::MakeUInt15(mode));
+ ParallelMath::ConditionalSet(work.m_u.m_partition, errorBetter16, ParallelMath::MakeUInt15(partition));
+ }
+ }
+ }
+}
+
+void cvtt::Internal::BC7Computer::TryDualPlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds, BC67::WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ // TODO: These error calculations are not optimal for weight-by-alpha, but this routine needs to be mostly rewritten for that.
+ // The alpha/color solutions are co-dependent in that case, but a good way to solve it would probably be to
+ // solve the alpha channel first, then solve the RGB channels, which in turn breaks down into two cases:
+ // - Separate alpha channel, then weighted RGB
+ // - Alpha+2 other channels, then the independent channel
+ if (numRefineRounds < 1)
+ numRefineRounds = 1;
+
+ float channelWeightsSq[4];
+ for (int ch = 0; ch < 4; ch++)
+ channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
+
+ for (uint16_t mode = 4; mode <= 5; mode++)
+ {
+ int numSP[2] = { 0, 0 };
+
+ for (uint16_t rotation = 0; rotation < 4; rotation++)
+ {
+ if (mode == 4)
+ {
+ numSP[0] = encodingPlan.mode4SP[rotation][0];
+ numSP[1] = encodingPlan.mode4SP[rotation][1];
+ }
+ else
+ numSP[0] = numSP[1] = encodingPlan.mode5SP[rotation];
+
+ if (numSP[0] == 0 && numSP[1] == 0)
+ continue;
+
+ int alphaChannel = (rotation + 3) & 3;
+ int redChannel = (rotation == 1) ? 3 : 0;
+ int greenChannel = (rotation == 2) ? 3 : 1;
+ int blueChannel = (rotation == 3) ? 3 : 2;
+
+ MUInt15 rotatedRGB[16][3];
+ MFloat floatRotatedRGB[16][3];
+
+ for (int px = 0; px < 16; px++)
+ {
+ rotatedRGB[px][0] = pixels[px][redChannel];
+ rotatedRGB[px][1] = pixels[px][greenChannel];
+ rotatedRGB[px][2] = pixels[px][blueChannel];
+
+ for (int ch = 0; ch < 3; ch++)
+ floatRotatedRGB[px][ch] = ParallelMath::ToFloat(rotatedRGB[px][ch]);
+ }
+
+ uint16_t maxIndexSelector = (mode == 4) ? 2 : 1;
+
+ float rotatedRGBWeights[3] = { channelWeights[redChannel], channelWeights[greenChannel], channelWeights[blueChannel] };
+ float rotatedRGBWeightsSq[3] = { channelWeightsSq[redChannel], channelWeightsSq[greenChannel], channelWeightsSq[blueChannel] };
+ float rotatedAlphaWeight[1] = { channelWeights[alphaChannel] };
+ float rotatedAlphaWeightSq[1] = { channelWeightsSq[alphaChannel] };
+
+ float uniformWeight[1] = { 1.0f }; // Since the alpha channel is independent, there's no need to bother with weights when doing refinement or selection, only error
+
+ MFloat preWeightedRotatedRGB[16][3];
+ BCCommon::PreWeightPixelsLDR<3>(preWeightedRotatedRGB, rotatedRGB, rotatedRGBWeights);
+
+ for (uint16_t indexSelector = 0; indexSelector < maxIndexSelector; indexSelector++)
+ {
+ int numTweakRounds = numSP[indexSelector];
+
+ if (numTweakRounds <= 0)
+ continue;
+
+ if (numTweakRounds > MaxTweakRounds)
+ numTweakRounds = MaxTweakRounds;
+
+ EndpointSelector<3, 8> rgbSelector;
+
+ for (int epPass = 0; epPass < NumEndpointSelectorPasses; epPass++)
+ {
+ for (int px = 0; px < 16; px++)
+ rgbSelector.ContributePass(preWeightedRotatedRGB[px], epPass, ParallelMath::MakeFloat(1.0f));
+
+ rgbSelector.FinishPass(epPass);
+ }
+
+ MUInt15 alphaRange[2];
+
+ alphaRange[0] = alphaRange[1] = pixels[0][alphaChannel];
+ for (int px = 1; px < 16; px++)
+ {
+ alphaRange[0] = ParallelMath::Min(pixels[px][alphaChannel], alphaRange[0]);
+ alphaRange[1] = ParallelMath::Max(pixels[px][alphaChannel], alphaRange[1]);
+ }
+
+ int rgbPrec = 0;
+ int alphaPrec = 0;
+
+ if (mode == 4)
+ {
+ rgbPrec = indexSelector ? 3 : 2;
+ alphaPrec = indexSelector ? 2 : 3;
+ }
+ else
+ rgbPrec = alphaPrec = 2;
+
+ UnfinishedEndpoints<3> unfinishedRGB = rgbSelector.GetEndpoints(rotatedRGBWeights);
+
+ MFloat bestRGBError = ParallelMath::MakeFloat(FLT_MAX);
+ MFloat bestAlphaError = ParallelMath::MakeFloat(FLT_MAX);
+
+ MUInt15 bestRGBIndexes[16];
+ MUInt15 bestAlphaIndexes[16];
+ MUInt15 bestEP[2][4];
+
+ for (int px = 0; px < 16; px++)
+ bestRGBIndexes[px] = bestAlphaIndexes[px] = ParallelMath::MakeUInt15(0);
+
+ for (int tweak = 0; tweak < numTweakRounds; tweak++)
+ {
+ MUInt15 rgbEP[2][3];
+ MUInt15 alphaEP[2];
+
+ unfinishedRGB.FinishLDR(tweak, 1 << rgbPrec, rgbEP[0], rgbEP[1]);
+
+ TweakAlpha(alphaRange, tweak, 1 << alphaPrec, alphaEP);
+
+ for (int refine = 0; refine < numRefineRounds; refine++)
+ {
+ if (mode == 4)
+ CompressEndpoints4(rgbEP, alphaEP);
+ else
+ CompressEndpoints5(rgbEP, alphaEP);
+
+
+ IndexSelector<1> alphaIndexSelector;
+ IndexSelector<3> rgbIndexSelector;
+
+ {
+ MUInt15 alphaEPTemp[2][1] = { { alphaEP[0] },{ alphaEP[1] } };
+ alphaIndexSelector.Init<false>(uniformWeight, alphaEPTemp, 1 << alphaPrec);
+ }
+ rgbIndexSelector.Init<false>(rotatedRGBWeights, rgbEP, 1 << rgbPrec);
+
+ EndpointRefiner<3> rgbRefiner;
+ EndpointRefiner<1> alphaRefiner;
+
+ rgbRefiner.Init(1 << rgbPrec, rotatedRGBWeights);
+ alphaRefiner.Init(1 << alphaPrec, uniformWeight);
+
+ MFloat errorRGB = ParallelMath::MakeFloatZero();
+ MFloat errorA = ParallelMath::MakeFloatZero();
+
+ MUInt15 rgbIndexes[16];
+ MUInt15 alphaIndexes[16];
+
+ AggregatedError<3> rgbAggError;
+ AggregatedError<1> alphaAggError;
+
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 rgbIndex = rgbIndexSelector.SelectIndexLDR(floatRotatedRGB[px], rtn);
+ MUInt15 alphaIndex = alphaIndexSelector.SelectIndexLDR(floatPixels[px] + alphaChannel, rtn);
+
+ MUInt15 reconstructedRGB[3];
+ MUInt15 reconstructedAlpha[1];
+
+ rgbIndexSelector.ReconstructLDR_BC7(rgbIndex, reconstructedRGB);
+ alphaIndexSelector.ReconstructLDR_BC7(alphaIndex, reconstructedAlpha);
+
+ if (flags & cvtt::Flags::BC7_FastIndexing)
+ {
+ BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], rgbAggError);
+ BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, alphaAggError);
+ }
+ else
+ {
+ AggregatedError<3> baseRGBAggError;
+ AggregatedError<1> baseAlphaAggError;
+
+ BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], baseRGBAggError);
+ BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, baseAlphaAggError);
+
+ MFloat rgbError = baseRGBAggError.Finalize(flags, rotatedRGBWeightsSq);
+ MFloat alphaError = baseAlphaAggError.Finalize(flags, rotatedAlphaWeightSq);
+
+ MUInt15 altRGBIndexes[2];
+ MUInt15 altAlphaIndexes[2];
+
+ altRGBIndexes[0] = ParallelMath::Max(rgbIndex, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
+ altRGBIndexes[1] = ParallelMath::Min(rgbIndex + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << rgbPrec) - 1)));
+
+ altAlphaIndexes[0] = ParallelMath::Max(alphaIndex, ParallelMath::MakeUInt15(1)) - ParallelMath::MakeUInt15(1);
+ altAlphaIndexes[1] = ParallelMath::Min(alphaIndex + ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << alphaPrec) - 1)));
+
+ for (int ii = 0; ii < 2; ii++)
+ {
+ rgbIndexSelector.ReconstructLDR_BC7(altRGBIndexes[ii], reconstructedRGB);
+ alphaIndexSelector.ReconstructLDR_BC7(altAlphaIndexes[ii], reconstructedAlpha);
+
+ AggregatedError<3> altRGBAggError;
+ AggregatedError<1> altAlphaAggError;
+
+ BCCommon::ComputeErrorLDR<3>(flags, reconstructedRGB, rotatedRGB[px], altRGBAggError);
+ BCCommon::ComputeErrorLDR<1>(flags, reconstructedAlpha, pixels[px] + alphaChannel, altAlphaAggError);
+
+ MFloat altRGBError = altRGBAggError.Finalize(flags, rotatedRGBWeightsSq);
+ MFloat altAlphaError = altAlphaAggError.Finalize(flags, rotatedAlphaWeightSq);
+
+ ParallelMath::Int16CompFlag rgbBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altRGBError, rgbError));
+ ParallelMath::Int16CompFlag alphaBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(altAlphaError, alphaError));
+
+ rgbError = ParallelMath::Min(altRGBError, rgbError);
+ alphaError = ParallelMath::Min(altAlphaError, alphaError);
+
+ ParallelMath::ConditionalSet(rgbIndex, rgbBetter, altRGBIndexes[ii]);
+ ParallelMath::ConditionalSet(alphaIndex, alphaBetter, altAlphaIndexes[ii]);
+ }
+
+ errorRGB = errorRGB + rgbError;
+ errorA = errorA + alphaError;
+ }
+
+ if (refine != numRefineRounds - 1)
+ {
+ rgbRefiner.ContributeUnweightedPW(preWeightedRotatedRGB[px], rgbIndex);
+ alphaRefiner.ContributeUnweightedPW(floatPixels[px] + alphaChannel, alphaIndex);
+ }
+
+ if (flags & Flags::BC7_FastIndexing)
+ {
+ errorRGB = rgbAggError.Finalize(flags, rotatedRGBWeightsSq);
+ errorA = alphaAggError.Finalize(flags, rotatedAlphaWeightSq);
+ }
+
+ rgbIndexes[px] = rgbIndex;
+ alphaIndexes[px] = alphaIndex;
+ }
+
+ ParallelMath::FloatCompFlag rgbBetter = ParallelMath::Less(errorRGB, bestRGBError);
+ ParallelMath::FloatCompFlag alphaBetter = ParallelMath::Less(errorA, bestAlphaError);
+
+ ParallelMath::Int16CompFlag rgbBetterInt16 = ParallelMath::FloatFlagToInt16(rgbBetter);
+ ParallelMath::Int16CompFlag alphaBetterInt16 = ParallelMath::FloatFlagToInt16(alphaBetter);
+
+ if (ParallelMath::AnySet(rgbBetterInt16))
+ {
+ bestRGBError = ParallelMath::Min(errorRGB, bestRGBError);
+
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestRGBIndexes[px], rgbBetterInt16, rgbIndexes[px]);
+
+ for (int ep = 0; ep < 2; ep++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ ParallelMath::ConditionalSet(bestEP[ep][ch], rgbBetterInt16, rgbEP[ep][ch]);
+ }
+ }
+
+ if (ParallelMath::AnySet(alphaBetterInt16))
+ {
+ bestAlphaError = ParallelMath::Min(errorA, bestAlphaError);
+
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestAlphaIndexes[px], alphaBetterInt16, alphaIndexes[px]);
+
+ for (int ep = 0; ep < 2; ep++)
+ ParallelMath::ConditionalSet(bestEP[ep][3], alphaBetterInt16, alphaEP[ep]);
+ }
+
+ if (refine != numRefineRounds - 1)
+ {
+ rgbRefiner.GetRefinedEndpointsLDR(rgbEP, rtn);
+
+ MUInt15 alphaEPTemp[2][1];
+ alphaRefiner.GetRefinedEndpointsLDR(alphaEPTemp, rtn);
+
+ for (int i = 0; i < 2; i++)
+ alphaEP[i] = alphaEPTemp[i][0];
+ }
+ } // refine
+ } // tweak
+
+ MFloat combinedError = bestRGBError + bestAlphaError;
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(combinedError, work.m_error);
+ ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
+
+ work.m_error = ParallelMath::Min(combinedError, work.m_error);
+
+ ParallelMath::ConditionalSet(work.m_mode, errorBetter16, ParallelMath::MakeUInt15(mode));
+ ParallelMath::ConditionalSet(work.m_u.m_isr.m_rotation, errorBetter16, ParallelMath::MakeUInt15(rotation));
+ ParallelMath::ConditionalSet(work.m_u.m_isr.m_indexSelector, errorBetter16, ParallelMath::MakeUInt15(indexSelector));
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::ConditionalSet(work.m_indexes[px], errorBetter16, indexSelector ? bestAlphaIndexes[px] : bestRGBIndexes[px]);
+ ParallelMath::ConditionalSet(work.m_indexes2[px], errorBetter16, indexSelector ? bestRGBIndexes[px] : bestAlphaIndexes[px]);
+ }
+
+ for (int ep = 0; ep < 2; ep++)
+ for (int ch = 0; ch < 4; ch++)
+ ParallelMath::ConditionalSet(work.m_ep[0][ep][ch], errorBetter16, bestEP[ep][ch]);
+ }
+ }
+ }
+}
+
+template<class T>
+void cvtt::Internal::BC7Computer::Swap(T& a, T& b)
+{
+ T temp = a;
+ a = b;
+ b = temp;
+}
+
+void cvtt::Internal::BC7Computer::Pack(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds)
+{
+ MUInt15 pixels[16][4];
+ MFloat floatPixels[16][4];
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ ParallelMath::ConvertLDRInputs(inputs, px, ch, pixels[px][ch]);
+ }
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ floatPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]);
+ }
+
+ BC67::WorkInfo work;
+ memset(&work, 0, sizeof(work));
+
+ work.m_error = ParallelMath::MakeFloat(FLT_MAX);
+
+ {
+ ParallelMath::RoundTowardNearestForScope rtn;
+ TrySinglePlane(flags, pixels, floatPixels, channelWeights, encodingPlan, numRefineRounds, work, &rtn);
+ TryDualPlane(flags, pixels, floatPixels, channelWeights, encodingPlan, numRefineRounds, work, &rtn);
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ PackingVector pv;
+ pv.Init();
+
+ ParallelMath::ScalarUInt16 mode = ParallelMath::Extract(work.m_mode, block);
+ ParallelMath::ScalarUInt16 partition = ParallelMath::Extract(work.m_u.m_partition, block);
+ ParallelMath::ScalarUInt16 indexSelector = ParallelMath::Extract(work.m_u.m_isr.m_indexSelector, block);
+
+ const BC7Data::BC7ModeInfo& modeInfo = BC7Data::g_modes[mode];
+
+ ParallelMath::ScalarUInt16 indexes[16];
+ ParallelMath::ScalarUInt16 indexes2[16];
+ ParallelMath::ScalarUInt16 endPoints[3][2][4];
+
+ for (int i = 0; i < 16; i++)
+ {
+ indexes[i] = ParallelMath::Extract(work.m_indexes[i], block);
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ indexes2[i] = ParallelMath::Extract(work.m_indexes2[i], block);
+ }
+
+ for (int subset = 0; subset < 3; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ endPoints[subset][ep][ch] = ParallelMath::Extract(work.m_ep[subset][ep][ch], block);
+ }
+ }
+
+ int fixups[3] = { 0, 0, 0 };
+
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ {
+ bool flipRGB = ((indexes[0] & (1 << (modeInfo.m_indexBits - 1))) != 0);
+ bool flipAlpha = ((indexes2[0] & (1 << (modeInfo.m_alphaIndexBits - 1))) != 0);
+
+ if (flipRGB)
+ {
+ uint16_t highIndex = (1 << modeInfo.m_indexBits) - 1;
+ for (int px = 0; px < 16; px++)
+ indexes[px] = highIndex - indexes[px];
+ }
+
+ if (flipAlpha)
+ {
+ uint16_t highIndex = (1 << modeInfo.m_alphaIndexBits) - 1;
+ for (int px = 0; px < 16; px++)
+ indexes2[px] = highIndex - indexes2[px];
+ }
+
+ if (indexSelector)
+ Swap(flipRGB, flipAlpha);
+
+ if (flipRGB)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ Swap(endPoints[0][0][ch], endPoints[0][1][ch]);
+ }
+ if (flipAlpha)
+ Swap(endPoints[0][0][3], endPoints[0][1][3]);
+
+ }
+ else
+ {
+ if (modeInfo.m_numSubsets == 2)
+ fixups[1] = BC7Data::g_fixupIndexes2[partition];
+ else if (modeInfo.m_numSubsets == 3)
+ {
+ fixups[1] = BC7Data::g_fixupIndexes3[partition][0];
+ fixups[2] = BC7Data::g_fixupIndexes3[partition][1];
+ }
+
+ bool flip[3] = { false, false, false };
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ flip[subset] = ((indexes[fixups[subset]] & (1 << (modeInfo.m_indexBits - 1))) != 0);
+
+ if (flip[0] || flip[1] || flip[2])
+ {
+ uint16_t highIndex = (1 << modeInfo.m_indexBits) - 1;
+ for (int px = 0; px < 16; px++)
+ {
+ int subset = 0;
+ if (modeInfo.m_numSubsets == 2)
+ subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
+ else if (modeInfo.m_numSubsets == 3)
+ subset = (BC7Data::g_partitionMap2[partition] >> (px * 2)) & 3;
+
+ if (flip[subset])
+ indexes[px] = highIndex - indexes[px];
+ }
+
+ int maxCH = (modeInfo.m_alphaMode == BC7Data::AlphaMode_Combined) ? 4 : 3;
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ if (flip[subset])
+ for (int ch = 0; ch < maxCH; ch++)
+ Swap(endPoints[subset][0][ch], endPoints[subset][1][ch]);
+ }
+ }
+ }
+
+ pv.Pack(static_cast<uint8_t>(1 << mode), mode + 1);
+
+ if (modeInfo.m_partitionBits)
+ pv.Pack(partition, modeInfo.m_partitionBits);
+
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ {
+ ParallelMath::ScalarUInt16 rotation = ParallelMath::Extract(work.m_u.m_isr.m_rotation, block);
+ pv.Pack(rotation, 2);
+ }
+
+ if (modeInfo.m_hasIndexSelector)
+ pv.Pack(indexSelector, 1);
+
+ // Encode RGB
+ for (int ch = 0; ch < 3; ch++)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][ch];
+ epPart >>= (8 - modeInfo.m_rgbBits);
+
+ pv.Pack(epPart, modeInfo.m_rgbBits);
+ }
+ }
+ }
+
+ // Encode alpha
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][3];
+ epPart >>= (8 - modeInfo.m_alphaBits);
+
+ pv.Pack(epPart, modeInfo.m_alphaBits);
+ }
+ }
+ }
+
+ // Encode parity bits
+ if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerSubset)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ ParallelMath::ScalarUInt16 epPart = endPoints[subset][0][0];
+ epPart >>= (7 - modeInfo.m_rgbBits);
+ epPart &= 1;
+
+ pv.Pack(epPart, 1);
+ }
+ }
+ else if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerEndpoint)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ ParallelMath::ScalarUInt16 epPart = endPoints[subset][ep][0];
+ epPart >>= (7 - modeInfo.m_rgbBits);
+ epPart &= 1;
+
+ pv.Pack(epPart, 1);
+ }
+ }
+ }
+
+ // Encode indexes
+ for (int px = 0; px < 16; px++)
+ {
+ int bits = modeInfo.m_indexBits;
+ if ((px == 0) || (px == fixups[1]) || (px == fixups[2]))
+ bits--;
+
+ pv.Pack(indexes[px], bits);
+ }
+
+ // Encode secondary indexes
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ int bits = modeInfo.m_alphaIndexBits;
+ if (px == 0)
+ bits--;
+
+ pv.Pack(indexes2[px], bits);
+ }
+ }
+
+ pv.Flush(packedBlocks);
+
+ packedBlocks += 16;
+ }
+}
+
+void cvtt::Internal::BC7Computer::UnpackOne(PixelBlockU8 &output, const uint8_t* packedBlock)
+{
+ UnpackingVector pv;
+ pv.Init(packedBlock);
+
+ int mode = 8;
+ for (int i = 0; i < 8; i++)
+ {
+ if (pv.Unpack(1) == 1)
+ {
+ mode = i;
+ break;
+ }
+ }
+
+ if (mode > 7)
+ {
+ for (int px = 0; px < 16; px++)
+ for (int ch = 0; ch < 4; ch++)
+ output.m_pixels[px][ch] = 0;
+
+ return;
+ }
+
+ const BC7Data::BC7ModeInfo &modeInfo = BC7Data::g_modes[mode];
+
+ int partition = 0;
+ if (modeInfo.m_partitionBits)
+ partition = pv.Unpack(modeInfo.m_partitionBits);
+
+ int rotation = 0;
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ rotation = pv.Unpack(2);
+
+ int indexSelector = 0;
+ if (modeInfo.m_hasIndexSelector)
+ indexSelector = pv.Unpack(1);
+
+ // Resolve fixups
+ int fixups[3] = { 0, 0, 0 };
+
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_Separate)
+ {
+ if (modeInfo.m_numSubsets == 2)
+ fixups[1] = BC7Data::g_fixupIndexes2[partition];
+ else if (modeInfo.m_numSubsets == 3)
+ {
+ fixups[1] = BC7Data::g_fixupIndexes3[partition][0];
+ fixups[2] = BC7Data::g_fixupIndexes3[partition][1];
+ }
+ }
+
+ int endPoints[3][2][4];
+
+ // Decode RGB
+ for (int ch = 0; ch < 3; ch++)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ endPoints[subset][ep][ch] = (pv.Unpack(modeInfo.m_rgbBits) << (8 - modeInfo.m_rgbBits));
+ }
+ }
+
+ // Decode alpha
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ endPoints[subset][ep][3] = (pv.Unpack(modeInfo.m_alphaBits) << (8 - modeInfo.m_alphaBits));
+ }
+ }
+ else
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ endPoints[subset][ep][3] = 255;
+ }
+ }
+
+ int parityBits = 0;
+
+ // Decode parity bits
+ if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerSubset)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ int p = pv.Unpack(1);
+
+ for (int ep = 0; ep < 2; ep++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ endPoints[subset][ep][ch] |= p << (7 - modeInfo.m_rgbBits);
+
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ endPoints[subset][ep][3] |= p << (7 - modeInfo.m_alphaBits);
+ }
+ }
+
+ parityBits = 1;
+ }
+ else if (modeInfo.m_pBitMode == BC7Data::PBitMode_PerEndpoint)
+ {
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ int p = pv.Unpack(1);
+
+ for (int ch = 0; ch < 3; ch++)
+ endPoints[subset][ep][ch] |= p << (7 - modeInfo.m_rgbBits);
+
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ endPoints[subset][ep][3] |= p << (7 - modeInfo.m_alphaBits);
+ }
+ }
+
+ parityBits = 1;
+ }
+
+ // Fill endpoint bits
+ for (int subset = 0; subset < modeInfo.m_numSubsets; subset++)
+ {
+ for (int ep = 0; ep < 2; ep++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ endPoints[subset][ep][ch] |= (endPoints[subset][ep][ch] >> (modeInfo.m_rgbBits + parityBits));
+
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ endPoints[subset][ep][3] |= (endPoints[subset][ep][3] >> (modeInfo.m_alphaBits + parityBits));
+ }
+ }
+
+ int indexes[16];
+ int indexes2[16];
+
+ // Decode indexes
+ for (int px = 0; px < 16; px++)
+ {
+ int bits = modeInfo.m_indexBits;
+ if ((px == 0) || (px == fixups[1]) || (px == fixups[2]))
+ bits--;
+
+ indexes[px] = pv.Unpack(bits);
+ }
+
+ // Decode secondary indexes
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ int bits = modeInfo.m_alphaIndexBits;
+ if (px == 0)
+ bits--;
+
+ indexes2[px] = pv.Unpack(bits);
+ }
+ }
+ else
+ {
+ for (int px = 0; px < 16; px++)
+ indexes2[px] = 0;
+ }
+
+ const int *alphaWeights = BC7Data::g_weightTables[modeInfo.m_alphaIndexBits];
+ const int *rgbWeights = BC7Data::g_weightTables[modeInfo.m_indexBits];
+
+ // Decode each pixel
+ for (int px = 0; px < 16; px++)
+ {
+ int rgbWeight = 0;
+ int alphaWeight = 0;
+
+ int rgbIndex = indexes[px];
+
+ rgbWeight = rgbWeights[indexes[px]];
+
+ if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Combined)
+ alphaWeight = rgbWeight;
+ else if (modeInfo.m_alphaMode == BC7Data::AlphaMode_Separate)
+ alphaWeight = alphaWeights[indexes2[px]];
+
+ if (indexSelector == 1)
+ {
+ int temp = rgbWeight;
+ rgbWeight = alphaWeight;
+ alphaWeight = temp;
+ }
+
+ int pixel[4] = { 0, 0, 0, 255 };
+
+ int subset = 0;
+
+ if (modeInfo.m_numSubsets == 2)
+ subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
+ else if (modeInfo.m_numSubsets == 3)
+ subset = (BC7Data::g_partitionMap2[partition] >> (px * 2)) & 3;
+
+ for (int ch = 0; ch < 3; ch++)
+ pixel[ch] = ((64 - rgbWeight) * endPoints[subset][0][ch] + rgbWeight * endPoints[subset][1][ch] + 32) >> 6;
+
+ if (modeInfo.m_alphaMode != BC7Data::AlphaMode_None)
+ pixel[3] = ((64 - alphaWeight) * endPoints[subset][0][3] + alphaWeight * endPoints[subset][1][3] + 32) >> 6;
+
+ if (rotation != 0)
+ {
+ int ch = rotation - 1;
+ int temp = pixel[ch];
+ pixel[ch] = pixel[3];
+ pixel[3] = temp;
+ }
+
+ for (int ch = 0; ch < 4; ch++)
+ output.m_pixels[px][ch] = static_cast<uint8_t>(pixel[ch]);
+ }
+}
+
+cvtt::ParallelMath::SInt16 cvtt::Internal::BC6HComputer::QuantizeSingleEndpointElementSigned(const MSInt16 &elem2CL, int precision, const ParallelMath::RoundUpForScope* ru)
+{
+ assert(ParallelMath::AllSet(ParallelMath::Less(elem2CL, ParallelMath::MakeSInt16(31744))));
+ assert(ParallelMath::AllSet(ParallelMath::Less(ParallelMath::MakeSInt16(-31744), elem2CL)));
+
+ // Expand to full range
+ ParallelMath::Int16CompFlag isNegative = ParallelMath::Less(elem2CL, ParallelMath::MakeSInt16(0));
+ MUInt15 absElem = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Select(isNegative, ParallelMath::MakeSInt16(0) - elem2CL, elem2CL));
+
+ absElem = ParallelMath::RightShift(ParallelMath::RoundAndConvertToU15(ParallelMath::ToFloat(absElem) * 32.0f / 31.0f, ru), 16 - precision);
+
+ MSInt16 absElemS16 = ParallelMath::LosslessCast<MSInt16>::Cast(absElem);
+
+ return ParallelMath::Select(isNegative, ParallelMath::MakeSInt16(0) - absElemS16, absElemS16);
+}
+
+cvtt::ParallelMath::UInt15 cvtt::Internal::BC6HComputer::QuantizeSingleEndpointElementUnsigned(const MUInt15 &elem, int precision, const ParallelMath::RoundUpForScope* ru)
+{
+ MUInt16 expandedElem = ParallelMath::RoundAndConvertToU16(ParallelMath::Min(ParallelMath::ToFloat(elem) * 64.0f / 31.0f, ParallelMath::MakeFloat(65535.0f)), ru);
+ return ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(expandedElem, 16 - precision));
+}
+
+void cvtt::Internal::BC6HComputer::UnquantizeSingleEndpointElementSigned(const MSInt16 &comp, int precision, MSInt16 &outUnquantized, MSInt16 &outUnquantizedFinished2CL)
+{
+ MSInt16 zero = ParallelMath::MakeSInt16(0);
+
+ ParallelMath::Int16CompFlag negative = ParallelMath::Less(comp, zero);
+ MUInt15 absComp = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Select(negative, MSInt16(zero - comp), comp));
+
+ MSInt16 unq;
+ MUInt15 absUnq;
+
+ if (precision >= 16)
+ {
+ unq = comp;
+ absUnq = absComp;
+ }
+ else
+ {
+ MSInt16 maxCompMinusOne = ParallelMath::MakeSInt16(static_cast<int16_t>((1 << (precision - 1)) - 2));
+ ParallelMath::Int16CompFlag isZero = ParallelMath::Equal(comp, zero);
+ ParallelMath::Int16CompFlag isMax = ParallelMath::Less(maxCompMinusOne, comp);
+
+ absUnq = (absComp << (16 - precision)) + ParallelMath::MakeUInt15(static_cast<uint16_t>(0x4000 >> (precision - 1)));
+ ParallelMath::ConditionalSet(absUnq, isZero, ParallelMath::MakeUInt15(0));
+ ParallelMath::ConditionalSet(absUnq, isMax, ParallelMath::MakeUInt15(0x7fff));
+
+ unq = ParallelMath::ConditionalNegate(negative, ParallelMath::LosslessCast<MSInt16>::Cast(absUnq));
+ }
+
+ outUnquantized = unq;
+
+ MUInt15 funq = ParallelMath::ToUInt15(ParallelMath::RightShift(ParallelMath::XMultiply(absUnq, ParallelMath::MakeUInt15(31)), 5));
+
+ outUnquantizedFinished2CL = ParallelMath::ConditionalNegate(negative, ParallelMath::LosslessCast<MSInt16>::Cast(funq));
+}
+
+void cvtt::Internal::BC6HComputer::UnquantizeSingleEndpointElementUnsigned(const MUInt15 &comp, int precision, MUInt16 &outUnquantized, MUInt16 &outUnquantizedFinished)
+{
+ MUInt16 unq = ParallelMath::LosslessCast<MUInt16>::Cast(comp);
+ if (precision < 15)
+ {
+ MUInt15 zero = ParallelMath::MakeUInt15(0);
+ MUInt15 maxCompMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>((1 << precision) - 2));
+
+ ParallelMath::Int16CompFlag isZero = ParallelMath::Equal(comp, zero);
+ ParallelMath::Int16CompFlag isMax = ParallelMath::Less(maxCompMinusOne, comp);
+
+ unq = (ParallelMath::LosslessCast<MUInt16>::Cast(comp) << (16 - precision)) + ParallelMath::MakeUInt16(static_cast<uint16_t>(0x8000 >> precision));
+
+ ParallelMath::ConditionalSet(unq, isZero, ParallelMath::MakeUInt16(0));
+ ParallelMath::ConditionalSet(unq, isMax, ParallelMath::MakeUInt16(0xffff));
+ }
+
+ outUnquantized = unq;
+ outUnquantizedFinished = ParallelMath::ToUInt16(ParallelMath::RightShift(ParallelMath::XMultiply(unq, ParallelMath::MakeUInt15(31)), 6));
+}
+
+void cvtt::Internal::BC6HComputer::QuantizeEndpointsSigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ MSInt16 unquantizedEP[2][3];
+ MSInt16 finishedUnquantizedEP[2][3];
+
+ {
+ ParallelMath::RoundUpForScope ru;
+
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MSInt16 qee = QuantizeSingleEndpointElementSigned(endPoints[epi][ch], precision, &ru);
+ UnquantizeSingleEndpointElementSigned(qee, precision, unquantizedEP[epi][ch], finishedUnquantizedEP[epi][ch]);
+ quantizedEndPoints[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(qee);
+ }
+ }
+ }
+
+ indexSelector.Init(channelWeights, unquantizedEP, finishedUnquantizedEP, indexRange);
+ indexSelector.InitHDR(indexRange, true, fastIndexing, channelWeights);
+
+ MUInt15 halfRangeMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange / 2) - 1);
+
+ MUInt15 index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixelsColorSpace[fixupIndex], rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[fixupIndex], rtn);
+
+ ParallelMath::Int16CompFlag invert = ParallelMath::Less(halfRangeMinusOne, index);
+
+ if (ParallelMath::AnySet(invert))
+ {
+ ParallelMath::ConditionalSet(index, invert, MUInt15(ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange - 1)) - index));
+
+ indexSelector.ConditionalInvert(invert);
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MAInt16 firstEP = quantizedEndPoints[0][ch];
+ MAInt16 secondEP = quantizedEndPoints[1][ch];
+
+ quantizedEndPoints[0][ch] = ParallelMath::Select(invert, secondEP, firstEP);
+ quantizedEndPoints[1][ch] = ParallelMath::Select(invert, firstEP, secondEP);
+ }
+ }
+
+ indexes[fixupIndex] = index;
+}
+
+void cvtt::Internal::BC6HComputer::QuantizeEndpointsUnsigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ MUInt16 unquantizedEP[2][3];
+ MUInt16 finishedUnquantizedEP[2][3];
+
+ {
+ ParallelMath::RoundUpForScope ru;
+
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 qee = QuantizeSingleEndpointElementUnsigned(ParallelMath::LosslessCast<MUInt15>::Cast(endPoints[epi][ch]), precision, &ru);
+ UnquantizeSingleEndpointElementUnsigned(qee, precision, unquantizedEP[epi][ch], finishedUnquantizedEP[epi][ch]);
+ quantizedEndPoints[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(qee);
+ }
+ }
+ }
+
+ indexSelector.Init(channelWeights, unquantizedEP, finishedUnquantizedEP, indexRange);
+ indexSelector.InitHDR(indexRange, false, fastIndexing, channelWeights);
+
+ MUInt15 halfRangeMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange / 2) - 1);
+
+ MUInt15 index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixelsColorSpace[fixupIndex], rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[fixupIndex], rtn);
+
+ ParallelMath::Int16CompFlag invert = ParallelMath::Less(halfRangeMinusOne, index);
+
+ if (ParallelMath::AnySet(invert))
+ {
+ ParallelMath::ConditionalSet(index, invert, MUInt15(ParallelMath::MakeUInt15(static_cast<uint16_t>(indexRange - 1)) - index));
+
+ indexSelector.ConditionalInvert(invert);
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MAInt16 firstEP = quantizedEndPoints[0][ch];
+ MAInt16 secondEP = quantizedEndPoints[1][ch];
+
+ quantizedEndPoints[0][ch] = ParallelMath::Select(invert, secondEP, firstEP);
+ quantizedEndPoints[1][ch] = ParallelMath::Select(invert, firstEP, secondEP);
+ }
+ }
+
+ indexes[fixupIndex] = index;
+}
+
+void cvtt::Internal::BC6HComputer::EvaluatePartitionedLegality(const MAInt16 ep0[2][3], const MAInt16 ep1[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][2][3], ParallelMath::Int16CompFlag& outIsLegal)
+{
+ ParallelMath::Int16CompFlag allLegal = ParallelMath::MakeBoolInt16(true);
+
+ MAInt16 aSignificantMask = ParallelMath::MakeAInt16(static_cast<int16_t>((1 << aPrec) - 1));
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ outEncodedEPs[0][0][ch] = ep0[0][ch];
+ outEncodedEPs[0][1][ch] = ep0[1][ch];
+ outEncodedEPs[1][0][ch] = ep1[0][ch];
+ outEncodedEPs[1][1][ch] = ep1[1][ch];
+
+ if (isTransformed)
+ {
+ for (int subset = 0; subset < 2; subset++)
+ {
+ for (int epi = 0; epi < 2; epi++)
+ {
+ if (epi == 0 && subset == 0)
+ continue;
+
+ MAInt16 bReduced = (outEncodedEPs[subset][epi][ch] & aSignificantMask);
+
+ MSInt16 delta = ParallelMath::TruncateToPrecisionSigned(ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::AbstractSubtract(outEncodedEPs[subset][epi][ch], outEncodedEPs[0][0][ch])), bPrec[ch]);
+
+ outEncodedEPs[subset][epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(delta);
+
+ MAInt16 reconstructed = (ParallelMath::AbstractAdd(outEncodedEPs[subset][epi][ch], outEncodedEPs[0][0][ch]) & aSignificantMask);
+ allLegal = allLegal & ParallelMath::Equal(reconstructed, bReduced);
+ }
+ }
+ }
+
+ if (!ParallelMath::AnySet(allLegal))
+ break;
+ }
+
+ outIsLegal = allLegal;
+}
+
+void cvtt::Internal::BC6HComputer::EvaluateSingleLegality(const MAInt16 ep[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][3], ParallelMath::Int16CompFlag& outIsLegal)
+{
+ ParallelMath::Int16CompFlag allLegal = ParallelMath::MakeBoolInt16(true);
+
+ MAInt16 aSignificantMask = ParallelMath::MakeAInt16(static_cast<int16_t>((1 << aPrec) - 1));
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ outEncodedEPs[0][ch] = ep[0][ch];
+ outEncodedEPs[1][ch] = ep[1][ch];
+
+ if (isTransformed)
+ {
+ MAInt16 bReduced = (outEncodedEPs[1][ch] & aSignificantMask);
+
+ MSInt16 delta = ParallelMath::TruncateToPrecisionSigned(ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::AbstractSubtract(outEncodedEPs[1][ch], outEncodedEPs[0][ch])), bPrec[ch]);
+
+ outEncodedEPs[1][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(delta);
+
+ MAInt16 reconstructed = (ParallelMath::AbstractAdd(outEncodedEPs[1][ch], outEncodedEPs[0][ch]) & aSignificantMask);
+ allLegal = allLegal & ParallelMath::Equal(reconstructed, bReduced);
+ }
+ }
+
+ outIsLegal = allLegal;
+}
+
+void cvtt::Internal::BC6HComputer::Pack(uint32_t flags, const PixelBlockF16* inputs, uint8_t* packedBlocks, const float channelWeights[4], bool isSigned, int numTweakRounds, int numRefineRounds)
+{
+ if (numTweakRounds < 1)
+ numTweakRounds = 1;
+ else if (numTweakRounds > MaxTweakRounds)
+ numTweakRounds = MaxTweakRounds;
+
+ if (numRefineRounds < 1)
+ numRefineRounds = 1;
+ else if (numRefineRounds > MaxRefineRounds)
+ numRefineRounds = MaxRefineRounds;
+
+ bool fastIndexing = ((flags & cvtt::Flags::BC6H_FastIndexing) != 0);
+ float channelWeightsSq[3];
+
+ ParallelMath::RoundTowardNearestForScope rtn;
+
+ MSInt16 pixels[16][3];
+ MFloat floatPixels2CL[16][3];
+ MFloat floatPixelsLinearWeighted[16][3];
+
+ MSInt16 low15Bits = ParallelMath::MakeSInt16(32767);
+
+ for (int ch = 0; ch < 3; ch++)
+ channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MSInt16 pixelValue;
+ ParallelMath::ConvertHDRInputs(inputs, px, ch, pixelValue);
+
+ // Convert from sign+magnitude to 2CL
+ if (isSigned)
+ {
+ ParallelMath::Int16CompFlag negative = ParallelMath::Less(pixelValue, ParallelMath::MakeSInt16(0));
+ MSInt16 magnitude = (pixelValue & low15Bits);
+ ParallelMath::ConditionalSet(pixelValue, negative, ParallelMath::MakeSInt16(0) - magnitude);
+ pixelValue = ParallelMath::Max(pixelValue, ParallelMath::MakeSInt16(-31743));
+ }
+ else
+ pixelValue = ParallelMath::Max(pixelValue, ParallelMath::MakeSInt16(0));
+
+ pixelValue = ParallelMath::Min(pixelValue, ParallelMath::MakeSInt16(31743));
+
+ pixels[px][ch] = pixelValue;
+ floatPixels2CL[px][ch] = ParallelMath::ToFloat(pixelValue);
+ floatPixelsLinearWeighted[px][ch] = ParallelMath::TwosCLHalfToFloat(pixelValue) * channelWeights[ch];
+ }
+ }
+
+ MFloat preWeightedPixels[16][3];
+
+ BCCommon::PreWeightPixelsHDR<3>(preWeightedPixels, pixels, channelWeights);
+
+ MAInt16 bestEndPoints[2][2][3];
+ MUInt15 bestIndexes[16];
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt15 bestMode = ParallelMath::MakeUInt15(0);
+ MUInt15 bestPartition = ParallelMath::MakeUInt15(0);
+
+ for (int px = 0; px < 16; px++)
+ bestIndexes[px] = ParallelMath::MakeUInt15(0);
+
+ for (int subset = 0; subset < 2; subset++)
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 3; ch++)
+ bestEndPoints[subset][epi][ch] = ParallelMath::MakeAInt16(0);
+
+ UnfinishedEndpoints<3> partitionedUFEP[32][2];
+ UnfinishedEndpoints<3> singleUFEP;
+
+ // Generate UFEP for partitions
+ for (int p = 0; p < 32; p++)
+ {
+ int partitionMask = BC7Data::g_partitionMap[p];
+
+ EndpointSelector<3, 8> epSelectors[2];
+
+ for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ int subset = (partitionMask >> px) & 1;
+ epSelectors[subset].ContributePass(preWeightedPixels[px], pass, ParallelMath::MakeFloat(1.0f));
+ }
+
+ for (int subset = 0; subset < 2; subset++)
+ epSelectors[subset].FinishPass(pass);
+ }
+
+ for (int subset = 0; subset < 2; subset++)
+ partitionedUFEP[p][subset] = epSelectors[subset].GetEndpoints(channelWeights);
+ }
+
+ // Generate UFEP for single
+ {
+ EndpointSelector<3, 8> epSelector;
+
+ for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
+ {
+ for (int px = 0; px < 16; px++)
+ epSelector.ContributePass(preWeightedPixels[px], pass, ParallelMath::MakeFloat(1.0f));
+
+ epSelector.FinishPass(pass);
+ }
+
+ singleUFEP = epSelector.GetEndpoints(channelWeights);
+ }
+
+ for (int partitionedInt = 0; partitionedInt < 2; partitionedInt++)
+ {
+ bool partitioned = (partitionedInt == 1);
+
+ for (int aPrec = BC7Data::g_maxHDRPrecision; aPrec >= 0; aPrec--)
+ {
+ if (!BC7Data::g_hdrModesExistForPrecision[partitionedInt][aPrec])
+ continue;
+
+ int numPartitions = partitioned ? 32 : 1;
+ int numSubsets = partitioned ? 2 : 1;
+ int indexBits = partitioned ? 3 : 4;
+ int indexRange = (1 << indexBits);
+
+ for (int p = 0; p < numPartitions; p++)
+ {
+ int partitionMask = partitioned ? BC7Data::g_partitionMap[p] : 0;
+
+ const int MaxMetaRounds = MaxTweakRounds * MaxRefineRounds;
+
+ MAInt16 metaEndPointsQuantized[MaxMetaRounds][2][2][3];
+ MUInt15 metaIndexes[MaxMetaRounds][16];
+ MFloat metaError[MaxMetaRounds][2];
+
+ bool roundValid[MaxMetaRounds][2];
+
+ for (int r = 0; r < MaxMetaRounds; r++)
+ for (int subset = 0; subset < 2; subset++)
+ roundValid[r][subset] = true;
+
+ for (int subset = 0; subset < numSubsets; subset++)
+ {
+ for (int tweak = 0; tweak < MaxTweakRounds; tweak++)
+ {
+ EndpointRefiner<3> refiners[2];
+
+ bool abortRemainingRefines = false;
+ for (int refinePass = 0; refinePass < MaxRefineRounds; refinePass++)
+ {
+ int metaRound = tweak * MaxRefineRounds + refinePass;
+
+ if (tweak >= numTweakRounds || refinePass >= numRefineRounds)
+ abortRemainingRefines = true;
+
+ if (abortRemainingRefines)
+ {
+ roundValid[metaRound][subset] = false;
+ continue;
+ }
+
+ MAInt16(&mrQuantizedEndPoints)[2][2][3] = metaEndPointsQuantized[metaRound];
+ MUInt15(&mrIndexes)[16] = metaIndexes[metaRound];
+
+ MSInt16 endPointsColorSpace[2][3];
+
+ if (refinePass == 0)
+ {
+ UnfinishedEndpoints<3> ufep = partitioned ? partitionedUFEP[p][subset] : singleUFEP;
+
+ if (isSigned)
+ ufep.FinishHDRSigned(tweak, indexRange, endPointsColorSpace[0], endPointsColorSpace[1], &rtn);
+ else
+ ufep.FinishHDRUnsigned(tweak, indexRange, endPointsColorSpace[0], endPointsColorSpace[1], &rtn);
+ }
+ else
+ refiners[subset].GetRefinedEndpointsHDR(endPointsColorSpace, isSigned, &rtn);
+
+ refiners[subset].Init(indexRange, channelWeights);
+
+ int fixupIndex = (subset == 0) ? 0 : BC7Data::g_fixupIndexes2[p];
+
+ IndexSelectorHDR<3> indexSelector;
+ if (isSigned)
+ QuantizeEndpointsSigned(endPointsColorSpace, floatPixels2CL, floatPixelsLinearWeighted, mrQuantizedEndPoints[subset], mrIndexes, indexSelector, fixupIndex, aPrec, indexRange, channelWeights, fastIndexing, &rtn);
+ else
+ QuantizeEndpointsUnsigned(endPointsColorSpace, floatPixels2CL, floatPixelsLinearWeighted, mrQuantizedEndPoints[subset], mrIndexes, indexSelector, fixupIndex, aPrec, indexRange, channelWeights, fastIndexing, &rtn);
+
+ if (metaRound > 0)
+ {
+ ParallelMath::Int16CompFlag anySame = ParallelMath::MakeBoolInt16(false);
+
+ for (int prevRound = 0; prevRound < metaRound; prevRound++)
+ {
+ MAInt16(&prevRoundEPs)[2][3] = metaEndPointsQuantized[prevRound][subset];
+
+ ParallelMath::Int16CompFlag same = ParallelMath::MakeBoolInt16(true);
+
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 3; ch++)
+ same = (same & ParallelMath::Equal(prevRoundEPs[epi][ch], mrQuantizedEndPoints[subset][epi][ch]));
+
+ anySame = (anySame | same);
+ if (ParallelMath::AllSet(anySame))
+ break;
+ }
+
+ if (ParallelMath::AllSet(anySame))
+ {
+ roundValid[metaRound][subset] = false;
+ continue;
+ }
+ }
+
+ MFloat subsetError = ParallelMath::MakeFloatZero();
+
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ if (subset != ((partitionMask >> px) & 1))
+ continue;
+
+ MUInt15 index;
+ if (px == fixupIndex)
+ index = mrIndexes[px];
+ else
+ {
+ index = fastIndexing ? indexSelector.SelectIndexHDRFast(floatPixels2CL[px], &rtn) : indexSelector.SelectIndexHDRSlow(floatPixelsLinearWeighted[px], &rtn);
+ mrIndexes[px] = index;
+ }
+
+ MSInt16 reconstructed[3];
+ if (isSigned)
+ indexSelector.ReconstructHDRSigned(mrIndexes[px], reconstructed);
+ else
+ indexSelector.ReconstructHDRUnsigned(mrIndexes[px], reconstructed);
+
+ subsetError = subsetError + (fastIndexing ? BCCommon::ComputeErrorHDRFast<3>(flags, reconstructed, pixels[px], channelWeightsSq) : BCCommon::ComputeErrorHDRSlow<3>(flags, reconstructed, pixels[px], channelWeightsSq));
+
+ if (refinePass != numRefineRounds - 1)
+ refiners[subset].ContributeUnweightedPW(preWeightedPixels[px], index);
+ }
+ }
+
+ metaError[metaRound][subset] = subsetError;
+ }
+ }
+ }
+
+ // Now we have a bunch of attempts, but not all of them will fit in the delta coding scheme
+ int numMeta1 = partitioned ? MaxMetaRounds : 1;
+ for (int meta0 = 0; meta0 < MaxMetaRounds; meta0++)
+ {
+ if (!roundValid[meta0][0])
+ continue;
+
+ for (int meta1 = 0; meta1 < numMeta1; meta1++)
+ {
+ MFloat combinedError = metaError[meta0][0];
+ if (partitioned)
+ {
+ if (!roundValid[meta1][1])
+ continue;
+
+ combinedError = combinedError + metaError[meta1][1];
+ }
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(combinedError, bestError);
+ if (!ParallelMath::AnySet(errorBetter))
+ continue;
+
+ ParallelMath::Int16CompFlag needsCommit = ParallelMath::FloatFlagToInt16(errorBetter);
+
+ // Figure out if this is encodable
+ for (int mode = 0; mode < BC7Data::g_numHDRModes; mode++)
+ {
+ const BC7Data::BC6HModeInfo &modeInfo = BC7Data::g_hdrModes[mode];
+
+ if (modeInfo.m_partitioned != partitioned || modeInfo.m_aPrec != aPrec)
+ continue;
+
+ MAInt16 encodedEPs[2][2][3];
+ ParallelMath::Int16CompFlag isLegal;
+ if (partitioned)
+ EvaluatePartitionedLegality(metaEndPointsQuantized[meta0][0], metaEndPointsQuantized[meta1][1], modeInfo.m_aPrec, modeInfo.m_bPrec, modeInfo.m_transformed, encodedEPs, isLegal);
+ else
+ EvaluateSingleLegality(metaEndPointsQuantized[meta0][0], modeInfo.m_aPrec, modeInfo.m_bPrec, modeInfo.m_transformed, encodedEPs[0], isLegal);
+
+ ParallelMath::Int16CompFlag isLegalAndBetter = (ParallelMath::FloatFlagToInt16(errorBetter) & isLegal);
+ if (!ParallelMath::AnySet(isLegalAndBetter))
+ continue;
+
+ ParallelMath::FloatCompFlag isLegalAndBetterFloat = ParallelMath::Int16FlagToFloat(isLegalAndBetter);
+
+ ParallelMath::ConditionalSet(bestError, isLegalAndBetterFloat, combinedError);
+ ParallelMath::ConditionalSet(bestMode, isLegalAndBetter, ParallelMath::MakeUInt15(static_cast<uint16_t>(mode)));
+ ParallelMath::ConditionalSet(bestPartition, isLegalAndBetter, ParallelMath::MakeUInt15(static_cast<uint16_t>(p)));
+
+ for (int subset = 0; subset < numSubsets; subset++)
+ {
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ ParallelMath::ConditionalSet(bestEndPoints[subset][epi][ch], isLegalAndBetter, encodedEPs[subset][epi][ch]);
+ }
+ }
+
+ for (int px = 0; px < 16; px++)
+ {
+ int subset = ((partitionMask >> px) & 1);
+ if (subset == 0)
+ ParallelMath::ConditionalSet(bestIndexes[px], isLegalAndBetter, metaIndexes[meta0][px]);
+ else
+ ParallelMath::ConditionalSet(bestIndexes[px], isLegalAndBetter, metaIndexes[meta1][px]);
+ }
+
+ needsCommit = ParallelMath::AndNot(needsCommit, isLegalAndBetter);
+ if (!ParallelMath::AnySet(needsCommit))
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // At this point, everything should be set
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ ParallelMath::ScalarUInt16 mode = ParallelMath::Extract(bestMode, block);
+ ParallelMath::ScalarUInt16 partition = ParallelMath::Extract(bestPartition, block);
+ int32_t eps[2][2][3];
+ ParallelMath::ScalarUInt16 indexes[16];
+
+ const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode];
+
+ BC6H_IO::WriteFunc_t writeFunc = BC6H_IO::g_writeFuncs[mode];
+
+ const int headerBits = modeInfo.m_partitioned ? 82 : 65;
+
+ for (int subset = 0; subset < 2; subset++)
+ {
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ eps[subset][epi][ch] = ParallelMath::Extract(bestEndPoints[subset][epi][ch], block);
+ }
+ }
+
+ for (int px = 0; px < 16; px++)
+ indexes[px] = ParallelMath::Extract(bestIndexes[px], block);
+
+ uint16_t modeID = modeInfo.m_modeID;
+
+ PackingVector pv;
+
+ {
+ uint32_t header[3];
+ writeFunc(header, modeID, partition,
+ eps[0][0][0], eps[0][1][0], eps[1][0][0], eps[1][1][0],
+ eps[0][0][1], eps[0][1][1], eps[1][0][1], eps[1][1][1],
+ eps[0][0][2], eps[0][1][2], eps[1][0][2], eps[1][1][2]
+ );
+
+ pv.InitPacked(header, headerBits);
+ }
+
+ int fixupIndex1 = 0;
+ int indexBits = 4;
+ if (modeInfo.m_partitioned)
+ {
+ fixupIndex1 = BC7Data::g_fixupIndexes2[partition];
+ indexBits = 3;
+ }
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::ScalarUInt16 index = ParallelMath::Extract(bestIndexes[px], block);
+ if (px == 0 || px == fixupIndex1)
+ pv.Pack(index, indexBits - 1);
+ else
+ pv.Pack(index, indexBits);
+ }
+
+ pv.Flush(packedBlocks + 16 * block);
+ }
+}
+
+void cvtt::Internal::BC6HComputer::SignExtendSingle(int &v, int bits)
+{
+ if (v & (1 << (bits - 1)))
+ v |= -(1 << bits);
+}
+
+void cvtt::Internal::BC6HComputer::UnpackOne(PixelBlockF16 &output, const uint8_t *pBC, bool isSigned)
+{
+ UnpackingVector pv;
+ pv.Init(pBC);
+
+ int numModeBits = 2;
+ int modeBits = pv.Unpack(2);
+ if (modeBits != 0 && modeBits != 1)
+ {
+ modeBits |= pv.Unpack(3) << 2;
+ numModeBits += 3;
+ }
+
+ int mode = -1;
+ for (int possibleMode = 0; possibleMode < BC7Data::g_numHDRModes; possibleMode++)
+ {
+ if (BC7Data::g_hdrModes[possibleMode].m_modeID == modeBits)
+ {
+ mode = possibleMode;
+ break;
+ }
+ }
+
+ if (mode < 0)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ output.m_pixels[px][ch] = 0;
+ output.m_pixels[px][3] = 0x3c00; // 1.0
+ }
+ return;
+ }
+
+ const BC7Data::BC6HModeInfo& modeInfo = BC7Data::g_hdrModes[mode];
+ const int headerBits = modeInfo.m_partitioned ? 82 : 65;
+ const BC6H_IO::ReadFunc_t readFunc = BC6H_IO::g_readFuncs[mode];
+
+ uint16_t partition = 0;
+ int32_t eps[2][2][3];
+
+ for (int subset = 0; subset < 2; subset++)
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 3; ch++)
+ eps[subset][epi][ch] = 0;
+
+ {
+ uint32_t header[3];
+ uint16_t codedEPs[2][2][3];
+ pv.UnpackStart(header, headerBits);
+
+ readFunc(header, partition,
+ codedEPs[0][0][0], codedEPs[0][1][0], codedEPs[1][0][0], codedEPs[1][1][0],
+ codedEPs[0][0][1], codedEPs[0][1][1], codedEPs[1][0][1], codedEPs[1][1][1],
+ codedEPs[0][0][2], codedEPs[0][1][2], codedEPs[1][0][2], codedEPs[1][1][2]
+ );
+
+ for (int subset = 0; subset < 2; subset++)
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 3; ch++)
+ eps[subset][epi][ch] = codedEPs[subset][epi][ch];
+ }
+
+ uint16_t modeID = modeInfo.m_modeID;
+
+ int fixupIndex1 = 0;
+ int indexBits = 4;
+ int numSubsets = 1;
+ if (modeInfo.m_partitioned)
+ {
+ fixupIndex1 = BC7Data::g_fixupIndexes2[partition];
+ indexBits = 3;
+ numSubsets = 2;
+ }
+
+ int indexes[16];
+ for (int px = 0; px < 16; px++)
+ {
+ if (px == 0 || px == fixupIndex1)
+ indexes[px] = pv.Unpack(indexBits - 1);
+ else
+ indexes[px] = pv.Unpack(indexBits);
+ }
+
+ if (modeInfo.m_partitioned)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ if (isSigned)
+ SignExtendSingle(eps[0][0][ch], modeInfo.m_aPrec);
+ if (modeInfo.m_transformed || isSigned)
+ {
+ SignExtendSingle(eps[0][1][ch], modeInfo.m_bPrec[ch]);
+ SignExtendSingle(eps[1][0][ch], modeInfo.m_bPrec[ch]);
+ SignExtendSingle(eps[1][1][ch], modeInfo.m_bPrec[ch]);
+ }
+ }
+ }
+ else
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ if (isSigned)
+ SignExtendSingle(eps[0][0][ch], modeInfo.m_aPrec);
+ if (modeInfo.m_transformed || isSigned)
+ SignExtendSingle(eps[0][1][ch], modeInfo.m_bPrec[ch]);
+ }
+ }
+
+ int aPrec = modeInfo.m_aPrec;
+
+ if (modeInfo.m_transformed)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ int wrapMask = (1 << aPrec) - 1;
+
+ eps[0][1][ch] = ((eps[0][0][ch] + eps[0][1][ch]) & wrapMask);
+ if (isSigned)
+ SignExtendSingle(eps[0][1][ch], aPrec);
+
+ if (modeInfo.m_partitioned)
+ {
+ eps[1][0][ch] = ((eps[0][0][ch] + eps[1][0][ch]) & wrapMask);
+ eps[1][1][ch] = ((eps[0][0][ch] + eps[1][1][ch]) & wrapMask);
+
+ if (isSigned)
+ {
+ SignExtendSingle(eps[1][0][ch], aPrec);
+ SignExtendSingle(eps[1][1][ch], aPrec);
+ }
+ }
+ }
+ }
+
+ // Unquantize endpoints
+ for (int subset = 0; subset < numSubsets; subset++)
+ {
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ int &v = eps[subset][epi][ch];
+
+ if (isSigned)
+ {
+ if (aPrec >= 16)
+ {
+ // Nothing
+ }
+ else
+ {
+ bool s = false;
+ int comp = v;
+ if (v < 0)
+ {
+ s = true;
+ comp = -comp;
+ }
+
+ int unq = 0;
+ if (comp == 0)
+ unq = 0;
+ else if (comp >= ((1 << (aPrec - 1)) - 1))
+ unq = 0x7fff;
+ else
+ unq = ((comp << 15) + 0x4000) >> (aPrec - 1);
+
+ if (s)
+ unq = -unq;
+
+ v = unq;
+ }
+ }
+ else
+ {
+ if (aPrec >= 15)
+ {
+ // Nothing
+ }
+ else if (v == 0)
+ {
+ // Nothing
+ }
+ else if (v == ((1 << aPrec) - 1))
+ v = 0xffff;
+ else
+ v = ((v << 16) + 0x8000) >> aPrec;
+ }
+ }
+ }
+ }
+
+ const int *weights = BC7Data::g_weightTables[indexBits];
+
+ for (int px = 0; px < 16; px++)
+ {
+ int subset = 0;
+ if (modeInfo.m_partitioned)
+ subset = (BC7Data::g_partitionMap[partition] >> px) & 1;
+
+ int w = weights[indexes[px]];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ int comp = ((64 - w) * eps[subset][0][ch] + w * eps[subset][1][ch] + 32) >> 6;
+
+ if (isSigned)
+ {
+ if (comp < 0)
+ comp = -(((-comp) * 31) >> 5);
+ else
+ comp = (comp * 31) >> 5;
+
+ int s = 0;
+ if (comp < 0)
+ {
+ s = 0x8000;
+ comp = -comp;
+ }
+
+ output.m_pixels[px][ch] = static_cast<uint16_t>(s | comp);
+ }
+ else
+ {
+ comp = (comp * 31) >> 6;
+ output.m_pixels[px][ch] = static_cast<uint16_t>(comp);
+ }
+ }
+ output.m_pixels[px][3] = 0x3c00; // 1.0
+ }
+}
+
+void cvtt::Kernels::ConfigureBC7EncodingPlanFromQuality(BC7EncodingPlan &encodingPlan, int quality)
+{
+ static const int kMaxQuality = 100;
+
+ if (quality < 1)
+ quality = 1;
+ else if (quality > kMaxQuality)
+ quality = kMaxQuality;
+
+ const int numRGBModes = cvtt::Tables::BC7Prio::g_bc7NumPrioCodesRGB * quality / kMaxQuality;
+ const int numRGBAModes = cvtt::Tables::BC7Prio::g_bc7NumPrioCodesRGBA * quality / kMaxQuality;
+
+ const uint16_t *prioLists[] = { cvtt::Tables::BC7Prio::g_bc7PrioCodesRGB, cvtt::Tables::BC7Prio::g_bc7PrioCodesRGBA };
+ const int prioListSizes[] = { numRGBModes, numRGBAModes };
+
+ BC7FineTuningParams ftParams;
+ memset(&ftParams, 0, sizeof(ftParams));
+
+ for (int listIndex = 0; listIndex < 2; listIndex++)
+ {
+ int prioListSize = prioListSizes[listIndex];
+ const uint16_t *prioList = prioLists[listIndex];
+
+ for (int prioIndex = 0; prioIndex < prioListSize; prioIndex++)
+ {
+ const uint16_t packedMode = prioList[prioIndex];
+
+ uint8_t seedPoints = static_cast<uint8_t>(cvtt::Tables::BC7Prio::UnpackSeedPointCount(packedMode));
+ int mode = cvtt::Tables::BC7Prio::UnpackMode(packedMode);
+
+ switch (mode)
+ {
+ case 0:
+ ftParams.mode0SP[cvtt::Tables::BC7Prio::UnpackPartition(packedMode)] = seedPoints;
+ break;
+ case 1:
+ ftParams.mode1SP[cvtt::Tables::BC7Prio::UnpackPartition(packedMode)] = seedPoints;
+ break;
+ case 2:
+ ftParams.mode2SP[cvtt::Tables::BC7Prio::UnpackPartition(packedMode)] = seedPoints;
+ break;
+ case 3:
+ ftParams.mode3SP[cvtt::Tables::BC7Prio::UnpackPartition(packedMode)] = seedPoints;
+ break;
+ case 4:
+ ftParams.mode4SP[cvtt::Tables::BC7Prio::UnpackRotation(packedMode)][cvtt::Tables::BC7Prio::UnpackIndexSelector(packedMode)] = seedPoints;
+ break;
+ case 5:
+ ftParams.mode5SP[cvtt::Tables::BC7Prio::UnpackRotation(packedMode)] = seedPoints;
+ break;
+ case 6:
+ ftParams.mode6SP = seedPoints;
+ break;
+ case 7:
+ ftParams.mode7SP[cvtt::Tables::BC7Prio::UnpackPartition(packedMode)] = seedPoints;
+ break;
+ }
+ }
+ }
+
+ ConfigureBC7EncodingPlanFromFineTuningParams(encodingPlan, ftParams);
+}
+
+// Generates a BC7 encoding plan from fine-tuning parameters.
+bool cvtt::Kernels::ConfigureBC7EncodingPlanFromFineTuningParams(BC7EncodingPlan &encodingPlan, const BC7FineTuningParams &params)
+{
+ memset(&encodingPlan, 0, sizeof(encodingPlan));
+
+ // Mode 0
+ for (int partition = 0; partition < 16; partition++)
+ {
+ uint8_t sp = params.mode0SP[partition];
+ if (sp == 0)
+ continue;
+
+ encodingPlan.mode0PartitionEnabled |= static_cast<uint16_t>(1) << partition;
+
+ for (int subset = 0; subset < 3; subset++)
+ {
+ int shape = cvtt::Internal::BC7Data::g_shapes3[partition][subset];
+ encodingPlan.seedPointsForShapeRGB[shape] = std::max(encodingPlan.seedPointsForShapeRGB[shape], sp);
+ }
+ }
+
+ // Mode 1
+ for (int partition = 0; partition < 64; partition++)
+ {
+ uint8_t sp = params.mode1SP[partition];
+ if (sp == 0)
+ continue;
+
+ encodingPlan.mode1PartitionEnabled |= static_cast<uint64_t>(1) << partition;
+
+ for (int subset = 0; subset < 2; subset++)
+ {
+ int shape = cvtt::Internal::BC7Data::g_shapes2[partition][subset];
+ encodingPlan.seedPointsForShapeRGB[shape] = std::max(encodingPlan.seedPointsForShapeRGB[shape], sp);
+ }
+ }
+
+ // Mode 2
+ for (int partition = 0; partition < 64; partition++)
+ {
+ uint8_t sp = params.mode2SP[partition];
+ if (sp == 0)
+ continue;
+
+ encodingPlan.mode2PartitionEnabled |= static_cast<uint64_t>(1) << partition;
+
+ for (int subset = 0; subset < 3; subset++)
+ {
+ int shape = cvtt::Internal::BC7Data::g_shapes3[partition][subset];
+ encodingPlan.seedPointsForShapeRGB[shape] = std::max(encodingPlan.seedPointsForShapeRGB[shape], sp);
+ }
+ }
+
+ // Mode 3
+ for (int partition = 0; partition < 64; partition++)
+ {
+ uint8_t sp = params.mode3SP[partition];
+ if (sp == 0)
+ continue;
+
+ encodingPlan.mode3PartitionEnabled |= static_cast<uint64_t>(1) << partition;
+
+ for (int subset = 0; subset < 2; subset++)
+ {
+ int shape = cvtt::Internal::BC7Data::g_shapes2[partition][subset];
+ encodingPlan.seedPointsForShapeRGB[shape] = std::max(encodingPlan.seedPointsForShapeRGB[shape], sp);
+ }
+ }
+
+ // Mode 4
+ for (int rotation = 0; rotation < 4; rotation++)
+ {
+ for (int indexMode = 0; indexMode < 2; indexMode++)
+ encodingPlan.mode4SP[rotation][indexMode] = params.mode4SP[rotation][indexMode];
+ }
+
+ // Mode 5
+ for (int rotation = 0; rotation < 4; rotation++)
+ encodingPlan.mode5SP[rotation] = params.mode5SP[rotation];
+
+ // Mode 6
+ {
+ uint8_t sp = params.mode6SP;
+ if (sp != 0)
+ {
+ encodingPlan.mode6Enabled = true;
+
+ int shape = cvtt::Internal::BC7Data::g_shapes1[0][0];
+ encodingPlan.seedPointsForShapeRGBA[shape] = std::max(encodingPlan.seedPointsForShapeRGBA[shape], sp);
+ }
+ }
+
+ // Mode 7
+ for (int partition = 0; partition < 64; partition++)
+ {
+ uint8_t sp = params.mode7SP[partition];
+ if (sp == 0)
+ continue;
+
+ encodingPlan.mode7RGBAPartitionEnabled |= static_cast<uint64_t>(1) << partition;
+
+ for (int subset = 0; subset < 2; subset++)
+ {
+ int shape = cvtt::Internal::BC7Data::g_shapes2[partition][subset];
+ encodingPlan.seedPointsForShapeRGBA[shape] = std::max(encodingPlan.seedPointsForShapeRGBA[shape], sp);
+ }
+ }
+
+ for (int i = 0; i < BC7EncodingPlan::kNumRGBShapes; i++)
+ {
+ if (encodingPlan.seedPointsForShapeRGB[i] > 0)
+ {
+ encodingPlan.rgbShapeList[encodingPlan.rgbNumShapesToEvaluate] = i;
+ encodingPlan.rgbNumShapesToEvaluate++;
+ }
+ }
+
+ for (int i = 0; i < BC7EncodingPlan::kNumRGBAShapes; i++)
+ {
+ if (encodingPlan.seedPointsForShapeRGBA[i] > 0)
+ {
+ encodingPlan.rgbaShapeList[encodingPlan.rgbaNumShapesToEvaluate] = i;
+ encodingPlan.rgbaNumShapesToEvaluate++;
+ }
+ }
+
+ encodingPlan.mode7RGBPartitionEnabled = (encodingPlan.mode7RGBAPartitionEnabled & ~encodingPlan.mode3PartitionEnabled);
+
+ return true;
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_BC67.h b/thirdparty/cvtt/ConvectionKernels_BC67.h
new file mode 100644
index 0000000000..b929711187
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC67.h
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "ConvectionKernels_ParallelMath.h"
+
+
+namespace cvtt
+{
+ namespace Tables
+ {
+ namespace BC7SC
+ {
+ struct Table;
+ }
+ }
+
+ namespace Internal
+ {
+ namespace BC67
+ {
+ struct WorkInfo;
+ }
+
+ template<int TVectorSize>
+ class IndexSelectorHDR;
+ }
+
+ struct PixelBlockU8;
+}
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ class BC7Computer
+ {
+ public:
+ static void Pack(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds);
+ static void UnpackOne(PixelBlockU8 &output, const uint8_t* packedBlock);
+
+ private:
+ static const int MaxTweakRounds = 4;
+
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+ typedef ParallelMath::Float MFloat;
+
+ static void TweakAlpha(const MUInt15 original[2], int tweak, int range, MUInt15 result[2]);
+ static void Quantize(MUInt15* color, int bits, int channels);
+ static void QuantizeP(MUInt15* color, int bits, uint16_t p, int channels);
+ static void Unquantize(MUInt15* color, int bits, int channels);
+ static void CompressEndpoints0(MUInt15 ep[2][4], uint16_t p[2]);
+ static void CompressEndpoints1(MUInt15 ep[2][4], uint16_t p);
+ static void CompressEndpoints2(MUInt15 ep[2][4]);
+ static void CompressEndpoints3(MUInt15 ep[2][4], uint16_t p[2]);
+ static void CompressEndpoints4(MUInt15 epRGB[2][3], MUInt15 epA[2]);
+ static void CompressEndpoints5(MUInt15 epRGB[2][3], MUInt15 epA[2]);
+ static void CompressEndpoints6(MUInt15 ep[2][4], uint16_t p[2]);
+ static void CompressEndpoints7(MUInt15 ep[2][4], uint16_t p[2]);
+ static void TrySingleColorRGBAMultiTable(uint32_t flags, const MUInt15 pixels[16][4], const MFloat average[4], int numRealChannels, const uint8_t *fragmentStart, int shapeLength, const MFloat &staticAlphaError, const ParallelMath::Int16CompFlag punchThroughInvalid[4], MFloat& shapeBestError, MUInt15 shapeBestEP[2][4], MUInt15 *fragmentBestIndexes, const float *channelWeightsSq, const cvtt::Tables::BC7SC::Table*const* tables, int numTables, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void TrySinglePlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds, BC67::WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void TryDualPlane(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const float channelWeights[4], const BC7EncodingPlan &encodingPlan, int numRefineRounds, BC67::WorkInfo& work, const ParallelMath::RoundTowardNearestForScope *rtn);
+
+ template<class T>
+ static void Swap(T& a, T& b);
+ };
+
+
+ class BC6HComputer
+ {
+ public:
+ static void Pack(uint32_t flags, const PixelBlockF16* inputs, uint8_t* packedBlocks, const float channelWeights[4], bool isSigned, int numTweakRounds, int numRefineRounds);
+ static void UnpackOne(PixelBlockF16 &output, const uint8_t *pBC, bool isSigned);
+
+ private:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::AInt16 MAInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+ typedef ParallelMath::UInt31 MUInt31;
+
+ static const int MaxTweakRounds = 4;
+ static const int MaxRefineRounds = 3;
+
+ static MSInt16 QuantizeSingleEndpointElementSigned(const MSInt16 &elem2CL, int precision, const ParallelMath::RoundUpForScope* ru);
+ static MUInt15 QuantizeSingleEndpointElementUnsigned(const MUInt15 &elem, int precision, const ParallelMath::RoundUpForScope* ru);
+ static void UnquantizeSingleEndpointElementSigned(const MSInt16 &comp, int precision, MSInt16 &outUnquantized, MSInt16 &outUnquantizedFinished2CL);
+ static void UnquantizeSingleEndpointElementUnsigned(const MUInt15 &comp, int precision, MUInt16 &outUnquantized, MUInt16 &outUnquantizedFinished);
+ static void QuantizeEndpointsSigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void QuantizeEndpointsUnsigned(const MSInt16 endPoints[2][3], const MFloat floatPixelsColorSpace[16][3], const MFloat floatPixelsLinearWeighted[16][3], MAInt16 quantizedEndPoints[2][3], MUInt15 indexes[16], IndexSelectorHDR<3> &indexSelector, int fixupIndex, int precision, int indexRange, const float *channelWeights, bool fastIndexing, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void EvaluatePartitionedLegality(const MAInt16 ep0[2][3], const MAInt16 ep1[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][2][3], ParallelMath::Int16CompFlag& outIsLegal);
+ static void EvaluateSingleLegality(const MAInt16 ep[2][3], int aPrec, const int bPrec[3], bool isTransformed, MAInt16 outEncodedEPs[2][3], ParallelMath::Int16CompFlag& outIsLegal);
+ static void SignExtendSingle(int &v, int bits);
+ };
+ }
+}
diff --git a/thirdparty/cvtt/ConvectionKernels_BC6H_IO.cpp b/thirdparty/cvtt/ConvectionKernels_BC6H_IO.cpp
new file mode 100644
index 0000000000..753b6f9000
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC6H_IO.cpp
@@ -0,0 +1,881 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_BC6H_IO.h"
+
+namespace cvtt
+{
+ namespace BC6H_IO
+ {
+ void WriteMode0(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x3u) | ((gy >> 2) & 0x4u) | ((by >> 1) & 0x8u) | (bz & 0x10u) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0xf8u) | ((gz << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x3e000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0xf800000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x3eu) | ((bz << 4) & 0x40u) | ((rz << 7) & 0xf80u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode1(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x3u) | ((gy >> 3) & 0x4u) | ((gz >> 1) & 0x18u) | ((rw << 5) & 0xfe0u) | ((bz << 12) & 0x3000u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0x3f8000u) | ((by << 17) & 0x400000u) | ((bz << 21) & 0x800000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bz >> 3) & 0x1u) | ((bz >> 4) & 0x2u) | ((bz >> 2) & 0x4u) | ((rx << 3) & 0x1f8u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x7e000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0x1f800000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x7eu) | ((rz << 7) & 0x1f80u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode2(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0xf8u) | ((rw >> 2) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x1e000u) | ((gw << 7) & 0x20000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0x7800000u) | ((bw << 17) & 0x8000000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x3eu) | ((bz << 4) & 0x40u) | ((rz << 7) & 0xf80u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode3(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0x78u) | ((rw >> 3) & 0x80u) | ((gz << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x3e000u) | ((gw << 8) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0x7800000u) | ((bw << 17) & 0x8000000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x1eu) | ((bz << 5) & 0x20u) | ((bz << 4) & 0x40u) | ((rz << 7) & 0x780u) | ((gy << 7) & 0x800u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode4(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0x78u) | ((rw >> 3) & 0x80u) | ((by << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x1e000u) | ((gw << 7) & 0x20000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0xf800000u) | ((bw << 18) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x1eu) | ((bz << 4) & 0x60u) | ((rz << 7) & 0x780u) | ((bz << 7) & 0x800u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode5(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x3fe0u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0xff8000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x3u) | ((bz >> 2) & 0x4u) | ((rx << 3) & 0xf8u) | ((gz << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x3e000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0xf800000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x3eu) | ((bz << 4) & 0x40u) | ((rz << 7) & 0xf80u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode6(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x1fe0u) | ((gz << 9) & 0x2000u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0x7f8000u) | ((bz << 21) & 0x800000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x1u) | ((bz >> 2) & 0x6u) | ((rx << 3) & 0x1f8u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x3e000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0xf800000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x7eu) | ((rz << 7) & 0x1f80u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode7(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x1fe0u) | ((bz << 13) & 0x2000u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0x7f8000u) | ((gy << 18) & 0x800000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x1u) | ((gz >> 4) & 0x2u) | ((bz >> 2) & 0x4u) | ((rx << 3) & 0xf8u) | ((gz << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x7e000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0xf800000u) | ((bz << 27) & 0x10000000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x3eu) | ((bz << 4) & 0x40u) | ((rz << 7) & 0xf80u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode8(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x1fe0u) | ((bz << 12) & 0x2000u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0x7f8000u) | ((by << 18) & 0x800000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x1u) | ((bz >> 4) & 0x2u) | ((bz >> 2) & 0x4u) | ((rx << 3) & 0xf8u) | ((gz << 4) & 0x100u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x3e000u) | ((bz << 18) & 0x40000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0x1f800000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x3eu) | ((bz << 4) & 0x40u) | ((rz << 7) & 0xf80u) | ((bz << 9) & 0x1000u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode9(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7e0u) | ((gz << 7) & 0x800u) | ((bz << 12) & 0x3000u) | ((by << 10) & 0x4000u) | ((gw << 15) & 0x1f8000u) | ((gy << 16) & 0x200000u) | ((by << 17) & 0x400000u) | ((bz << 21) & 0x800000u) | ((gy << 20) & 0x1000000u) | ((bw << 25) & 0x7e000000u) | ((gz << 26) & 0x80000000u);
+ encoded[1] = ((bz >> 3) & 0x1u) | ((bz >> 4) & 0x2u) | ((bz >> 2) & 0x4u) | ((rx << 3) & 0x1f8u) | ((gy << 9) & 0x1e00u) | ((gx << 13) & 0x7e000u) | ((gz << 19) & 0x780000u) | ((bx << 23) & 0x1f800000u) | ((by << 29) & 0xe0000000u);
+ encoded[2] = ((by >> 3) & 0x1u) | ((ry << 1) & 0x7eu) | ((rz << 7) & 0x1f80u) | ((d << 13) & 0x3e000u);
+ }
+
+ void WriteMode10(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0x1ff8u) | ((gx << 13) & 0x7fe000u) | ((bx << 23) & 0xff800000u);
+ encoded[2] = ((bx >> 9) & 0x1u);
+ }
+
+ void WriteMode11(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0xff8u) | ((rw << 2) & 0x1000u) | ((gx << 13) & 0x3fe000u) | ((gw << 12) & 0x400000u) | ((bx << 23) & 0xff800000u);
+ encoded[2] = ((bw >> 10) & 0x1u);
+ }
+
+ void WriteMode12(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0x7f8u) | (rw & 0x800u) | ((rw << 2) & 0x1000u) | ((gx << 13) & 0x1fe000u) | ((gw << 10) & 0x200000u) | ((gw << 12) & 0x400000u) | ((bx << 23) & 0x7f800000u) | ((bw << 20) & 0x80000000u);
+ encoded[2] = ((bw >> 10) & 0x1u);
+ }
+
+ void WriteMode13(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz)
+ {
+ encoded[0] = (m & 0x1fu) | ((rw << 5) & 0x7fe0u) | ((gw << 15) & 0x1ff8000u) | ((bw << 25) & 0xfe000000u);
+ encoded[1] = ((bw >> 7) & 0x7u) | ((rx << 3) & 0x78u) | ((rw >> 8) & 0x80u) | ((rw >> 6) & 0x100u) | ((rw >> 4) & 0x200u) | ((rw >> 2) & 0x400u) | (rw & 0x800u) | ((rw << 2) & 0x1000u) | ((gx << 13) & 0x1e000u) | ((gw << 2) & 0x20000u) | ((gw << 4) & 0x40000u) | ((gw << 6) & 0x80000u) | ((gw << 8) & 0x100000u) | ((gw << 10) & 0x200000u) | ((gw << 12) & 0x400000u) | ((bx << 23) & 0x7800000u) | ((bw << 12) & 0x8000000u) | ((bw << 14) & 0x10000000u) | ((bw << 16) & 0x20000000u) | ((bw << 18) & 0x40000000u) | ((bw << 20) & 0x80000000u);
+ encoded[2] = ((bw >> 10) & 0x1u);
+ }
+
+ void ReadMode0(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ gy |= ((encoded[0] << 2) & 0x10u);
+ by |= ((encoded[0] << 1) & 0x10u);
+ bz |= (encoded[0] & 0x10u);
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0x1fu);
+ gz |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x1fu);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x1fu);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x1fu);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0x1fu);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode1(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ gy |= ((encoded[0] << 3) & 0x20u);
+ gz |= ((encoded[0] << 1) & 0x30u);
+ rw |= ((encoded[0] >> 5) & 0x7fu);
+ bz |= ((encoded[0] >> 12) & 0x3u);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0x7fu);
+ by |= ((encoded[0] >> 17) & 0x20u);
+ bz |= ((encoded[0] >> 21) & 0x4u);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bz |= ((encoded[1] << 3) & 0x8u);
+ bz |= ((encoded[1] << 4) & 0x20u);
+ bz |= ((encoded[1] << 2) & 0x10u);
+ rx |= ((encoded[1] >> 3) & 0x3fu);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x3fu);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x3fu);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x3fu);
+ rz |= ((encoded[2] >> 7) & 0x3fu);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode2(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0x1fu);
+ rw |= ((encoded[1] << 2) & 0x400u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0xfu);
+ gw |= ((encoded[1] >> 7) & 0x400u);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0xfu);
+ bw |= ((encoded[1] >> 17) & 0x400u);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x1fu);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0x1fu);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode3(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0xfu);
+ rw |= ((encoded[1] << 3) & 0x400u);
+ gz |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x1fu);
+ gw |= ((encoded[1] >> 8) & 0x400u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0xfu);
+ bw |= ((encoded[1] >> 17) & 0x400u);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0xfu);
+ bz |= ((encoded[2] >> 5) & 0x1u);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0xfu);
+ gy |= ((encoded[2] >> 7) & 0x10u);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode4(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0xfu);
+ rw |= ((encoded[1] << 3) & 0x400u);
+ by |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0xfu);
+ gw |= ((encoded[1] >> 7) & 0x400u);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x1fu);
+ bw |= ((encoded[1] >> 18) & 0x400u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0xfu);
+ bz |= ((encoded[2] >> 4) & 0x6u);
+ rz |= ((encoded[2] >> 7) & 0xfu);
+ bz |= ((encoded[2] >> 7) & 0x10u);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode5(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x1ffu);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0x1ffu);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x180u);
+ bz |= ((encoded[1] << 2) & 0x10u);
+ rx |= ((encoded[1] >> 3) & 0x1fu);
+ gz |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x1fu);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x1fu);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x1fu);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0x1fu);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode6(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0xffu);
+ gz |= ((encoded[0] >> 9) & 0x10u);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0xffu);
+ bz |= ((encoded[0] >> 21) & 0x4u);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x80u);
+ bz |= ((encoded[1] << 2) & 0x18u);
+ rx |= ((encoded[1] >> 3) & 0x3fu);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x1fu);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x1fu);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x3fu);
+ rz |= ((encoded[2] >> 7) & 0x3fu);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode7(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0xffu);
+ bz |= ((encoded[0] >> 13) & 0x1u);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0xffu);
+ gy |= ((encoded[0] >> 18) & 0x20u);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x80u);
+ gz |= ((encoded[1] << 4) & 0x20u);
+ bz |= ((encoded[1] << 2) & 0x10u);
+ rx |= ((encoded[1] >> 3) & 0x1fu);
+ gz |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x3fu);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x1fu);
+ bz |= ((encoded[1] >> 27) & 0x2u);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x1fu);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0x1fu);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode8(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0xffu);
+ bz |= ((encoded[0] >> 12) & 0x2u);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0xffu);
+ by |= ((encoded[0] >> 18) & 0x20u);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x80u);
+ bz |= ((encoded[1] << 4) & 0x20u);
+ bz |= ((encoded[1] << 2) & 0x10u);
+ rx |= ((encoded[1] >> 3) & 0x1fu);
+ gz |= ((encoded[1] >> 4) & 0x10u);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x1fu);
+ bz |= ((encoded[1] >> 18) & 0x1u);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x3fu);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x1fu);
+ bz |= ((encoded[2] >> 4) & 0x4u);
+ rz |= ((encoded[2] >> 7) & 0x1fu);
+ bz |= ((encoded[2] >> 9) & 0x8u);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode9(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3fu);
+ gz |= ((encoded[0] >> 7) & 0x10u);
+ bz |= ((encoded[0] >> 12) & 0x3u);
+ by |= ((encoded[0] >> 10) & 0x10u);
+ gw |= ((encoded[0] >> 15) & 0x3fu);
+ gy |= ((encoded[0] >> 16) & 0x20u);
+ by |= ((encoded[0] >> 17) & 0x20u);
+ bz |= ((encoded[0] >> 21) & 0x4u);
+ gy |= ((encoded[0] >> 20) & 0x10u);
+ bw |= ((encoded[0] >> 25) & 0x3fu);
+ gz |= ((encoded[0] >> 26) & 0x20u);
+ bz |= ((encoded[1] << 3) & 0x8u);
+ bz |= ((encoded[1] << 4) & 0x20u);
+ bz |= ((encoded[1] << 2) & 0x10u);
+ rx |= ((encoded[1] >> 3) & 0x3fu);
+ gy |= ((encoded[1] >> 9) & 0xfu);
+ gx |= ((encoded[1] >> 13) & 0x3fu);
+ gz |= ((encoded[1] >> 19) & 0xfu);
+ bx |= ((encoded[1] >> 23) & 0x3fu);
+ by |= ((encoded[1] >> 29) & 0x7u);
+ by |= ((encoded[2] << 3) & 0x8u);
+ ry |= ((encoded[2] >> 1) & 0x3fu);
+ rz |= ((encoded[2] >> 7) & 0x3fu);
+ d |= ((encoded[2] >> 13) & 0x1fu);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode10(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0x3ffu);
+ gx |= ((encoded[1] >> 13) & 0x3ffu);
+ bx |= ((encoded[1] >> 23) & 0x1ffu);
+ bx |= ((encoded[2] << 9) & 0x200u);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode11(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0x1ffu);
+ rw |= ((encoded[1] >> 2) & 0x400u);
+ gx |= ((encoded[1] >> 13) & 0x1ffu);
+ gw |= ((encoded[1] >> 12) & 0x400u);
+ bx |= ((encoded[1] >> 23) & 0x1ffu);
+ bw |= ((encoded[2] << 10) & 0x400u);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode12(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0xffu);
+ rw |= (encoded[1] & 0x800u);
+ rw |= ((encoded[1] >> 2) & 0x400u);
+ gx |= ((encoded[1] >> 13) & 0xffu);
+ gw |= ((encoded[1] >> 10) & 0x800u);
+ gw |= ((encoded[1] >> 12) & 0x400u);
+ bx |= ((encoded[1] >> 23) & 0xffu);
+ bw |= ((encoded[1] >> 20) & 0x800u);
+ bw |= ((encoded[2] << 10) & 0x400u);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ void ReadMode13(const uint32_t *encoded, uint16_t &outD, uint16_t &outRW, uint16_t &outRX, uint16_t &outRY, uint16_t &outRZ, uint16_t &outGW, uint16_t &outGX, uint16_t &outGY, uint16_t &outGZ, uint16_t &outBW, uint16_t &outBX, uint16_t &outBY, uint16_t &outBZ)
+ {
+ uint16_t d = 0;
+ uint16_t rw = 0;
+ uint16_t rx = 0;
+ uint16_t ry = 0;
+ uint16_t rz = 0;
+ uint16_t gw = 0;
+ uint16_t gx = 0;
+ uint16_t gy = 0;
+ uint16_t gz = 0;
+ uint16_t bw = 0;
+ uint16_t bx = 0;
+ uint16_t by = 0;
+ uint16_t bz = 0;
+ rw |= ((encoded[0] >> 5) & 0x3ffu);
+ gw |= ((encoded[0] >> 15) & 0x3ffu);
+ bw |= ((encoded[0] >> 25) & 0x7fu);
+ bw |= ((encoded[1] << 7) & 0x380u);
+ rx |= ((encoded[1] >> 3) & 0xfu);
+ rw |= ((encoded[1] << 8) & 0x8000u);
+ rw |= ((encoded[1] << 6) & 0x4000u);
+ rw |= ((encoded[1] << 4) & 0x2000u);
+ rw |= ((encoded[1] << 2) & 0x1000u);
+ rw |= (encoded[1] & 0x800u);
+ rw |= ((encoded[1] >> 2) & 0x400u);
+ gx |= ((encoded[1] >> 13) & 0xfu);
+ gw |= ((encoded[1] >> 2) & 0x8000u);
+ gw |= ((encoded[1] >> 4) & 0x4000u);
+ gw |= ((encoded[1] >> 6) & 0x2000u);
+ gw |= ((encoded[1] >> 8) & 0x1000u);
+ gw |= ((encoded[1] >> 10) & 0x800u);
+ gw |= ((encoded[1] >> 12) & 0x400u);
+ bx |= ((encoded[1] >> 23) & 0xfu);
+ bw |= ((encoded[1] >> 12) & 0x8000u);
+ bw |= ((encoded[1] >> 14) & 0x4000u);
+ bw |= ((encoded[1] >> 16) & 0x2000u);
+ bw |= ((encoded[1] >> 18) & 0x1000u);
+ bw |= ((encoded[1] >> 20) & 0x800u);
+ bw |= ((encoded[2] << 10) & 0x400u);
+ outD = d;
+ outRW = rw;
+ outRX = rx;
+ outRY = ry;
+ outRZ = rz;
+ outGW = gw;
+ outGX = gx;
+ outGY = gy;
+ outGZ = gz;
+ outBW = bw;
+ outBX = bx;
+ outBY = by;
+ outBZ = bz;
+ }
+
+ const ReadFunc_t g_readFuncs[14] =
+ {
+ ReadMode0,
+ ReadMode1,
+ ReadMode2,
+ ReadMode3,
+ ReadMode4,
+ ReadMode5,
+ ReadMode6,
+ ReadMode7,
+ ReadMode8,
+ ReadMode9,
+ ReadMode10,
+ ReadMode11,
+ ReadMode12,
+ ReadMode13
+ };
+
+ const WriteFunc_t g_writeFuncs[14] =
+ {
+ WriteMode0,
+ WriteMode1,
+ WriteMode2,
+ WriteMode3,
+ WriteMode4,
+ WriteMode5,
+ WriteMode6,
+ WriteMode7,
+ WriteMode8,
+ WriteMode9,
+ WriteMode10,
+ WriteMode11,
+ WriteMode12,
+ WriteMode13
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_BC6H_IO.h b/thirdparty/cvtt/ConvectionKernels_BC6H_IO.h
new file mode 100644
index 0000000000..a7bb517b54
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC6H_IO.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <stdint.h>
+#include "ConvectionKernels_BC6H_IO.h"
+
+namespace cvtt
+{
+ namespace BC6H_IO
+ {
+ typedef void (*ReadFunc_t)(const uint32_t *encoded, uint16_t &d, uint16_t &rw, uint16_t &rx, uint16_t &ry, uint16_t &rz, uint16_t &gw, uint16_t &gx, uint16_t &gy, uint16_t &gz, uint16_t &bw, uint16_t &bx, uint16_t &by, uint16_t &bz);
+ typedef void (*WriteFunc_t)(uint32_t *encoded, uint16_t m, uint16_t d, uint16_t rw, uint16_t rx, uint16_t ry, uint16_t rz, uint16_t gw, uint16_t gx, uint16_t gy, uint16_t gz, uint16_t bw, uint16_t bx, uint16_t by, uint16_t bz);
+
+ extern const ReadFunc_t g_readFuncs[14];
+ extern const WriteFunc_t g_writeFuncs[14];
+ }
+}
diff --git a/thirdparty/cvtt/ConvectionKernels_BC7_Prio.h b/thirdparty/cvtt/ConvectionKernels_BC7_Prio.h
new file mode 100644
index 0000000000..1880e22d0f
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC7_Prio.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+
+namespace cvtt { namespace Tables { namespace BC7Prio {
+ extern const uint16_t *g_bc7PrioCodesRGB;
+ extern const int g_bc7NumPrioCodesRGB;
+
+ extern const uint16_t *g_bc7PrioCodesRGBA;
+ extern const int g_bc7NumPrioCodesRGBA;
+
+ int UnpackMode(uint16_t packed);
+ int UnpackSeedPointCount(uint16_t packed);
+ int UnpackPartition(uint16_t packed);
+ int UnpackRotation(uint16_t packed);
+ int UnpackIndexSelector(uint16_t packed);
+}}}
diff --git a/thirdparty/cvtt/ConvectionKernels_BC7_PrioData.cpp b/thirdparty/cvtt/ConvectionKernels_BC7_PrioData.cpp
new file mode 100644
index 0000000000..5b3134f860
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BC7_PrioData.cpp
@@ -0,0 +1,1301 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_BC7_Prio.h"
+
+#define BC7_PARTITION_BITS 6
+#define BC7_PARTITION_OFFSET_BITS 0
+
+#define BC7_ROTATION_BITS 2
+#define BC7_ROTATION_OFFSET_BITS 0
+
+#define BC7_INDEX_MODE_BITS 1
+#define BC7_INDEX_MODE_OFFSET_BITS (BC7_ROTATION_OFFSET_BITS + BC7_ROTATION_BITS)
+
+#define BC7_MODE_BITS 3
+#define BC7_MODE_OFFSET_BITS (BC7_PARTITION_OFFSET_BITS + BC7_PARTITION_BITS)
+#define BC7_SEED_POINT_COUNT_BITS 2
+#define BC7_SEED_POINT_COUNT_OFFSET_BITS (BC7_MODE_BITS + BC7_MODE_OFFSET_BITS)
+
+
+
+#define BC7_MODE_PRIO_DUAL_PLANE(subData) \
+ ( \
+ ((subData / 10) << BC7_ROTATION_OFFSET_BITS) | \
+ ((subData % 10) << BC7_INDEX_MODE_OFFSET_BITS) \
+ )
+
+#define BC7_MODE_PRIO_CODE(seedPointCount, mode, subData) \
+ (\
+ ((seedPointCount - 1) << BC7_SEED_POINT_COUNT_OFFSET_BITS) | \
+ (mode << BC7_MODE_OFFSET_BITS) | \
+ ((mode == 4 || mode == 5) ? BC7_MODE_PRIO_DUAL_PLANE(subData) : (subData << BC7_PARTITION_OFFSET_BITS)) \
+ )
+
+namespace cvtt { namespace Tables { namespace BC7Prio {
+ const uint16_t g_bc7PrioCodesRGBData[] =
+ {
+ BC7_MODE_PRIO_CODE(1, 1, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 0),
+ BC7_MODE_PRIO_CODE(1, 0, 3),
+ BC7_MODE_PRIO_CODE(1, 0, 1),
+ BC7_MODE_PRIO_CODE(1, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 0, 9),
+ BC7_MODE_PRIO_CODE(1, 1, 6),
+ BC7_MODE_PRIO_CODE(1, 1, 1),
+ BC7_MODE_PRIO_CODE(1, 1, 2),
+ BC7_MODE_PRIO_CODE(1, 0, 15),
+ BC7_MODE_PRIO_CODE(1, 1, 7),
+ BC7_MODE_PRIO_CODE(1, 1, 16),
+ BC7_MODE_PRIO_CODE(1, 1, 15),
+ BC7_MODE_PRIO_CODE(1, 1, 14),
+ BC7_MODE_PRIO_CODE(1, 0, 13),
+ BC7_MODE_PRIO_CODE(1, 0, 14),
+ BC7_MODE_PRIO_CODE(1, 0, 11),
+ BC7_MODE_PRIO_CODE(1, 1, 22),
+ BC7_MODE_PRIO_CODE(1, 0, 8),
+ BC7_MODE_PRIO_CODE(1, 0, 10),
+ BC7_MODE_PRIO_CODE(1, 1, 8),
+ BC7_MODE_PRIO_CODE(1, 3, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 19),
+ BC7_MODE_PRIO_CODE(1, 4, 31),
+ BC7_MODE_PRIO_CODE(1, 1, 10),
+ BC7_MODE_PRIO_CODE(1, 1, 23),
+ BC7_MODE_PRIO_CODE(1, 1, 3),
+ BC7_MODE_PRIO_CODE(2, 1, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 9),
+ BC7_MODE_PRIO_CODE(2, 1, 0),
+ BC7_MODE_PRIO_CODE(1, 1, 20),
+ BC7_MODE_PRIO_CODE(1, 1, 21),
+ BC7_MODE_PRIO_CODE(1, 4, 11),
+ BC7_MODE_PRIO_CODE(1, 1, 29),
+ BC7_MODE_PRIO_CODE(1, 1, 26),
+ BC7_MODE_PRIO_CODE(1, 5, 30),
+ BC7_MODE_PRIO_CODE(1, 0, 4),
+ BC7_MODE_PRIO_CODE(2, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 0, 0),
+ BC7_MODE_PRIO_CODE(2, 0, 10),
+ BC7_MODE_PRIO_CODE(3, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 1, 11),
+ BC7_MODE_PRIO_CODE(1, 4, 10),
+ BC7_MODE_PRIO_CODE(2, 0, 8),
+ BC7_MODE_PRIO_CODE(2, 0, 11),
+ BC7_MODE_PRIO_CODE(2, 0, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 4),
+ BC7_MODE_PRIO_CODE(3, 1, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 12),
+ BC7_MODE_PRIO_CODE(1, 1, 18),
+ BC7_MODE_PRIO_CODE(1, 3, 0),
+ BC7_MODE_PRIO_CODE(1, 0, 5),
+ BC7_MODE_PRIO_CODE(1, 1, 17),
+ BC7_MODE_PRIO_CODE(1, 1, 25),
+ BC7_MODE_PRIO_CODE(1, 0, 7),
+ BC7_MODE_PRIO_CODE(3, 0, 10),
+ BC7_MODE_PRIO_CODE(1, 1, 5),
+ BC7_MODE_PRIO_CODE(2, 1, 10),
+ BC7_MODE_PRIO_CODE(1, 1, 24),
+ BC7_MODE_PRIO_CODE(3, 0, 8),
+ BC7_MODE_PRIO_CODE(3, 1, 0),
+ BC7_MODE_PRIO_CODE(2, 1, 15),
+ BC7_MODE_PRIO_CODE(2, 1, 14),
+ BC7_MODE_PRIO_CODE(3, 0, 13),
+ BC7_MODE_PRIO_CODE(3, 0, 11),
+ BC7_MODE_PRIO_CODE(2, 1, 16),
+ BC7_MODE_PRIO_CODE(2, 0, 14),
+ BC7_MODE_PRIO_CODE(2, 1, 3),
+ BC7_MODE_PRIO_CODE(4, 0, 10),
+ BC7_MODE_PRIO_CODE(2, 1, 1),
+ BC7_MODE_PRIO_CODE(1, 0, 2),
+ BC7_MODE_PRIO_CODE(2, 1, 2),
+ BC7_MODE_PRIO_CODE(4, 0, 8),
+ BC7_MODE_PRIO_CODE(1, 0, 12),
+ BC7_MODE_PRIO_CODE(4, 1, 13),
+ BC7_MODE_PRIO_CODE(1, 5, 10),
+ BC7_MODE_PRIO_CODE(2, 0, 15),
+ BC7_MODE_PRIO_CODE(1, 0, 6),
+ BC7_MODE_PRIO_CODE(1, 1, 35),
+ BC7_MODE_PRIO_CODE(2, 1, 23),
+ BC7_MODE_PRIO_CODE(4, 0, 13),
+ BC7_MODE_PRIO_CODE(4, 0, 11),
+ BC7_MODE_PRIO_CODE(1, 2, 17),
+ BC7_MODE_PRIO_CODE(2, 1, 6),
+ BC7_MODE_PRIO_CODE(2, 1, 7),
+ BC7_MODE_PRIO_CODE(4, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 2, 16),
+ BC7_MODE_PRIO_CODE(2, 1, 19),
+ BC7_MODE_PRIO_CODE(1, 1, 30),
+ BC7_MODE_PRIO_CODE(2, 3, 13),
+ BC7_MODE_PRIO_CODE(3, 0, 14),
+ BC7_MODE_PRIO_CODE(2, 1, 29),
+ BC7_MODE_PRIO_CODE(2, 1, 21),
+ BC7_MODE_PRIO_CODE(4, 1, 0),
+ BC7_MODE_PRIO_CODE(3, 0, 15),
+ BC7_MODE_PRIO_CODE(2, 0, 3),
+ BC7_MODE_PRIO_CODE(1, 1, 28),
+ BC7_MODE_PRIO_CODE(1, 4, 30),
+ BC7_MODE_PRIO_CODE(2, 0, 4),
+ BC7_MODE_PRIO_CODE(1, 2, 63),
+ BC7_MODE_PRIO_CODE(4, 0, 14),
+ BC7_MODE_PRIO_CODE(2, 1, 26),
+ BC7_MODE_PRIO_CODE(2, 0, 1),
+ BC7_MODE_PRIO_CODE(3, 0, 3),
+ BC7_MODE_PRIO_CODE(1, 1, 61),
+ BC7_MODE_PRIO_CODE(2, 0, 7),
+ BC7_MODE_PRIO_CODE(2, 0, 5),
+ BC7_MODE_PRIO_CODE(3, 1, 10),
+ BC7_MODE_PRIO_CODE(2, 4, 31),
+ BC7_MODE_PRIO_CODE(2, 0, 9),
+ BC7_MODE_PRIO_CODE(2, 1, 11),
+ BC7_MODE_PRIO_CODE(4, 0, 15),
+ BC7_MODE_PRIO_CODE(3, 1, 14),
+ BC7_MODE_PRIO_CODE(2, 0, 0),
+ BC7_MODE_PRIO_CODE(3, 1, 15),
+ BC7_MODE_PRIO_CODE(2, 3, 0),
+ BC7_MODE_PRIO_CODE(3, 0, 1),
+ BC7_MODE_PRIO_CODE(1, 1, 60),
+ BC7_MODE_PRIO_CODE(2, 1, 12),
+ BC7_MODE_PRIO_CODE(3, 1, 1),
+ BC7_MODE_PRIO_CODE(3, 0, 5),
+ BC7_MODE_PRIO_CODE(1, 1, 27),
+ BC7_MODE_PRIO_CODE(2, 1, 18),
+ BC7_MODE_PRIO_CODE(3, 0, 9),
+ BC7_MODE_PRIO_CODE(3, 1, 3),
+ BC7_MODE_PRIO_CODE(2, 0, 2),
+ BC7_MODE_PRIO_CODE(3, 1, 16),
+ BC7_MODE_PRIO_CODE(3, 1, 2),
+ BC7_MODE_PRIO_CODE(1, 1, 31),
+ BC7_MODE_PRIO_CODE(3, 0, 7),
+ BC7_MODE_PRIO_CODE(2, 1, 17),
+ BC7_MODE_PRIO_CODE(1, 5, 20),
+ BC7_MODE_PRIO_CODE(2, 1, 4),
+ BC7_MODE_PRIO_CODE(1, 1, 62),
+ BC7_MODE_PRIO_CODE(2, 0, 12),
+ BC7_MODE_PRIO_CODE(3, 0, 4),
+ BC7_MODE_PRIO_CODE(4, 0, 4),
+ BC7_MODE_PRIO_CODE(1, 1, 33),
+ BC7_MODE_PRIO_CODE(3, 1, 23),
+ BC7_MODE_PRIO_CODE(2, 1, 5),
+ BC7_MODE_PRIO_CODE(2, 0, 6),
+ BC7_MODE_PRIO_CODE(2, 1, 24),
+ BC7_MODE_PRIO_CODE(1, 1, 59),
+ BC7_MODE_PRIO_CODE(1, 1, 63),
+ BC7_MODE_PRIO_CODE(3, 0, 0),
+ BC7_MODE_PRIO_CODE(1, 1, 52),
+ BC7_MODE_PRIO_CODE(4, 0, 7),
+ BC7_MODE_PRIO_CODE(2, 1, 22),
+ BC7_MODE_PRIO_CODE(4, 0, 3),
+ BC7_MODE_PRIO_CODE(1, 2, 10),
+ BC7_MODE_PRIO_CODE(3, 1, 7),
+ BC7_MODE_PRIO_CODE(4, 0, 9),
+ BC7_MODE_PRIO_CODE(2, 1, 8),
+ BC7_MODE_PRIO_CODE(4, 0, 1),
+ BC7_MODE_PRIO_CODE(3, 0, 12),
+ BC7_MODE_PRIO_CODE(4, 0, 5),
+ BC7_MODE_PRIO_CODE(3, 1, 6),
+ BC7_MODE_PRIO_CODE(4, 1, 14),
+ BC7_MODE_PRIO_CODE(1, 3, 15),
+ BC7_MODE_PRIO_CODE(1, 1, 56),
+ BC7_MODE_PRIO_CODE(3, 0, 6),
+ BC7_MODE_PRIO_CODE(3, 0, 2),
+ BC7_MODE_PRIO_CODE(1, 1, 32),
+ BC7_MODE_PRIO_CODE(4, 1, 10),
+ BC7_MODE_PRIO_CODE(1, 2, 8),
+ BC7_MODE_PRIO_CODE(2, 1, 9),
+ BC7_MODE_PRIO_CODE(1, 2, 18),
+ BC7_MODE_PRIO_CODE(4, 1, 15),
+ BC7_MODE_PRIO_CODE(4, 0, 6),
+ BC7_MODE_PRIO_CODE(3, 1, 29),
+ BC7_MODE_PRIO_CODE(2, 1, 25),
+ BC7_MODE_PRIO_CODE(3, 4, 31),
+ BC7_MODE_PRIO_CODE(3, 3, 13),
+ BC7_MODE_PRIO_CODE(4, 0, 0),
+ BC7_MODE_PRIO_CODE(3, 1, 19),
+ BC7_MODE_PRIO_CODE(4, 0, 12),
+ BC7_MODE_PRIO_CODE(4, 1, 1),
+ BC7_MODE_PRIO_CODE(4, 0, 2),
+ BC7_MODE_PRIO_CODE(1, 3, 2),
+ BC7_MODE_PRIO_CODE(1, 2, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 58),
+ BC7_MODE_PRIO_CODE(1, 3, 14),
+ BC7_MODE_PRIO_CODE(4, 1, 3),
+ BC7_MODE_PRIO_CODE(3, 1, 21),
+ BC7_MODE_PRIO_CODE(2, 2, 8),
+ BC7_MODE_PRIO_CODE(1, 2, 19),
+ BC7_MODE_PRIO_CODE(4, 1, 16),
+ BC7_MODE_PRIO_CODE(4, 1, 2),
+ BC7_MODE_PRIO_CODE(2, 2, 16),
+ BC7_MODE_PRIO_CODE(2, 2, 10),
+ BC7_MODE_PRIO_CODE(2, 1, 20),
+ BC7_MODE_PRIO_CODE(1, 2, 11),
+ BC7_MODE_PRIO_CODE(1, 1, 54),
+ BC7_MODE_PRIO_CODE(1, 1, 47),
+ BC7_MODE_PRIO_CODE(1, 3, 1),
+ BC7_MODE_PRIO_CODE(1, 2, 21),
+ BC7_MODE_PRIO_CODE(1, 2, 62),
+ BC7_MODE_PRIO_CODE(2, 2, 11),
+ BC7_MODE_PRIO_CODE(3, 1, 26),
+ BC7_MODE_PRIO_CODE(1, 1, 53),
+ BC7_MODE_PRIO_CODE(2, 1, 35),
+ BC7_MODE_PRIO_CODE(2, 2, 13),
+ BC7_MODE_PRIO_CODE(4, 1, 23),
+ BC7_MODE_PRIO_CODE(4, 1, 6),
+ BC7_MODE_PRIO_CODE(4, 1, 7),
+ BC7_MODE_PRIO_CODE(1, 2, 25),
+ BC7_MODE_PRIO_CODE(1, 1, 57),
+ BC7_MODE_PRIO_CODE(2, 1, 60),
+ BC7_MODE_PRIO_CODE(1, 2, 20),
+ BC7_MODE_PRIO_CODE(3, 1, 8),
+ BC7_MODE_PRIO_CODE(4, 1, 29),
+ BC7_MODE_PRIO_CODE(4, 1, 19),
+ BC7_MODE_PRIO_CODE(3, 2, 8),
+ BC7_MODE_PRIO_CODE(2, 4, 11),
+ BC7_MODE_PRIO_CODE(4, 1, 21),
+ BC7_MODE_PRIO_CODE(3, 2, 10),
+ BC7_MODE_PRIO_CODE(2, 1, 61),
+ BC7_MODE_PRIO_CODE(2, 1, 30),
+ BC7_MODE_PRIO_CODE(3, 1, 12),
+ BC7_MODE_PRIO_CODE(3, 1, 11),
+ BC7_MODE_PRIO_CODE(2, 1, 63),
+ BC7_MODE_PRIO_CODE(2, 3, 1),
+ BC7_MODE_PRIO_CODE(2, 1, 28),
+ BC7_MODE_PRIO_CODE(2, 1, 62),
+ BC7_MODE_PRIO_CODE(3, 2, 13),
+ BC7_MODE_PRIO_CODE(2, 2, 63),
+ BC7_MODE_PRIO_CODE(2, 1, 33),
+ BC7_MODE_PRIO_CODE(2, 4, 10),
+ BC7_MODE_PRIO_CODE(3, 1, 18),
+ BC7_MODE_PRIO_CODE(2, 5, 30),
+ BC7_MODE_PRIO_CODE(3, 1, 5),
+ BC7_MODE_PRIO_CODE(2, 2, 17),
+ BC7_MODE_PRIO_CODE(1, 1, 55),
+ BC7_MODE_PRIO_CODE(3, 1, 17),
+ BC7_MODE_PRIO_CODE(2, 3, 2),
+ BC7_MODE_PRIO_CODE(1, 4, 21),
+ BC7_MODE_PRIO_CODE(3, 2, 11),
+ BC7_MODE_PRIO_CODE(4, 1, 11),
+ BC7_MODE_PRIO_CODE(2, 1, 27),
+ BC7_MODE_PRIO_CODE(1, 2, 59),
+ BC7_MODE_PRIO_CODE(4, 1, 26),
+ BC7_MODE_PRIO_CODE(3, 1, 9),
+ BC7_MODE_PRIO_CODE(2, 3, 14),
+ BC7_MODE_PRIO_CODE(3, 1, 4),
+ BC7_MODE_PRIO_CODE(3, 1, 24),
+ BC7_MODE_PRIO_CODE(3, 1, 25),
+ BC7_MODE_PRIO_CODE(3, 3, 0),
+ BC7_MODE_PRIO_CODE(3, 4, 11),
+ BC7_MODE_PRIO_CODE(4, 1, 12),
+ BC7_MODE_PRIO_CODE(2, 1, 32),
+ BC7_MODE_PRIO_CODE(2, 3, 15),
+ BC7_MODE_PRIO_CODE(4, 2, 10),
+ BC7_MODE_PRIO_CODE(1, 2, 60),
+ BC7_MODE_PRIO_CODE(1, 2, 32),
+ BC7_MODE_PRIO_CODE(1, 1, 40),
+ BC7_MODE_PRIO_CODE(4, 1, 18),
+ BC7_MODE_PRIO_CODE(2, 1, 59),
+ BC7_MODE_PRIO_CODE(4, 1, 5),
+ BC7_MODE_PRIO_CODE(3, 1, 22),
+ BC7_MODE_PRIO_CODE(3, 2, 16),
+ BC7_MODE_PRIO_CODE(3, 1, 20),
+ BC7_MODE_PRIO_CODE(4, 1, 4),
+ BC7_MODE_PRIO_CODE(2, 1, 31),
+ BC7_MODE_PRIO_CODE(4, 1, 17),
+ BC7_MODE_PRIO_CODE(1, 2, 24),
+ BC7_MODE_PRIO_CODE(4, 1, 24),
+ BC7_MODE_PRIO_CODE(2, 1, 58),
+ BC7_MODE_PRIO_CODE(4, 2, 8),
+ BC7_MODE_PRIO_CODE(1, 2, 22),
+ BC7_MODE_PRIO_CODE(1, 2, 23),
+ BC7_MODE_PRIO_CODE(1, 3, 10),
+ BC7_MODE_PRIO_CODE(1, 1, 41),
+ BC7_MODE_PRIO_CODE(2, 2, 18),
+ BC7_MODE_PRIO_CODE(4, 1, 25),
+ BC7_MODE_PRIO_CODE(3, 1, 61),
+ BC7_MODE_PRIO_CODE(1, 3, 29),
+ BC7_MODE_PRIO_CODE(1, 2, 57),
+ BC7_MODE_PRIO_CODE(2, 2, 19),
+ BC7_MODE_PRIO_CODE(1, 2, 53),
+ BC7_MODE_PRIO_CODE(1, 2, 55),
+ BC7_MODE_PRIO_CODE(3, 2, 63),
+ BC7_MODE_PRIO_CODE(3, 1, 60),
+ BC7_MODE_PRIO_CODE(4, 1, 8),
+ BC7_MODE_PRIO_CODE(2, 1, 56),
+ BC7_MODE_PRIO_CODE(3, 1, 35),
+ BC7_MODE_PRIO_CODE(4, 4, 31),
+ BC7_MODE_PRIO_CODE(4, 1, 9),
+ BC7_MODE_PRIO_CODE(1, 1, 46),
+ BC7_MODE_PRIO_CODE(1, 2, 58),
+ BC7_MODE_PRIO_CODE(2, 3, 29),
+ BC7_MODE_PRIO_CODE(1, 1, 45),
+ BC7_MODE_PRIO_CODE(4, 2, 13),
+ BC7_MODE_PRIO_CODE(1, 1, 42),
+ BC7_MODE_PRIO_CODE(1, 3, 3),
+ BC7_MODE_PRIO_CODE(4, 2, 11),
+ BC7_MODE_PRIO_CODE(3, 1, 63),
+ BC7_MODE_PRIO_CODE(3, 1, 30),
+ BC7_MODE_PRIO_CODE(1, 1, 36),
+ BC7_MODE_PRIO_CODE(3, 1, 62),
+ BC7_MODE_PRIO_CODE(1, 1, 43),
+ BC7_MODE_PRIO_CODE(1, 3, 21),
+ BC7_MODE_PRIO_CODE(3, 2, 17),
+ BC7_MODE_PRIO_CODE(1, 2, 14),
+ BC7_MODE_PRIO_CODE(1, 1, 48),
+ BC7_MODE_PRIO_CODE(2, 1, 57),
+ BC7_MODE_PRIO_CODE(2, 1, 52),
+ BC7_MODE_PRIO_CODE(1, 2, 61),
+ BC7_MODE_PRIO_CODE(3, 1, 33),
+ BC7_MODE_PRIO_CODE(1, 1, 51),
+ BC7_MODE_PRIO_CODE(4, 1, 20),
+ BC7_MODE_PRIO_CODE(1, 3, 8),
+ BC7_MODE_PRIO_CODE(4, 1, 22),
+ BC7_MODE_PRIO_CODE(1, 3, 19),
+ BC7_MODE_PRIO_CODE(1, 2, 36),
+ BC7_MODE_PRIO_CODE(2, 5, 10),
+ BC7_MODE_PRIO_CODE(3, 1, 28),
+ BC7_MODE_PRIO_CODE(2, 2, 14),
+ BC7_MODE_PRIO_CODE(1, 1, 49),
+ BC7_MODE_PRIO_CODE(1, 2, 33),
+ BC7_MODE_PRIO_CODE(1, 3, 9),
+ BC7_MODE_PRIO_CODE(2, 2, 20),
+ BC7_MODE_PRIO_CODE(1, 3, 26),
+ BC7_MODE_PRIO_CODE(2, 1, 53),
+ BC7_MODE_PRIO_CODE(4, 3, 13),
+ BC7_MODE_PRIO_CODE(2, 2, 21),
+ BC7_MODE_PRIO_CODE(3, 4, 10),
+ BC7_MODE_PRIO_CODE(4, 1, 60),
+ BC7_MODE_PRIO_CODE(2, 1, 54),
+ BC7_MODE_PRIO_CODE(1, 2, 29),
+ BC7_MODE_PRIO_CODE(2, 1, 47),
+ BC7_MODE_PRIO_CODE(1, 2, 52),
+ BC7_MODE_PRIO_CODE(3, 1, 32),
+ BC7_MODE_PRIO_CODE(1, 2, 40),
+ BC7_MODE_PRIO_CODE(1, 2, 31),
+ BC7_MODE_PRIO_CODE(3, 1, 27),
+ BC7_MODE_PRIO_CODE(3, 2, 18),
+ BC7_MODE_PRIO_CODE(2, 3, 10),
+ BC7_MODE_PRIO_CODE(2, 1, 55),
+ BC7_MODE_PRIO_CODE(4, 1, 61),
+ BC7_MODE_PRIO_CODE(3, 2, 14),
+ BC7_MODE_PRIO_CODE(3, 1, 31),
+ BC7_MODE_PRIO_CODE(1, 2, 34),
+ BC7_MODE_PRIO_CODE(3, 2, 19),
+ BC7_MODE_PRIO_CODE(2, 3, 21),
+ BC7_MODE_PRIO_CODE(2, 4, 30),
+ BC7_MODE_PRIO_CODE(1, 2, 15),
+ BC7_MODE_PRIO_CODE(2, 3, 26),
+ BC7_MODE_PRIO_CODE(1, 2, 28),
+ BC7_MODE_PRIO_CODE(4, 2, 16),
+ BC7_MODE_PRIO_CODE(2, 2, 15),
+ BC7_MODE_PRIO_CODE(2, 1, 40),
+ BC7_MODE_PRIO_CODE(2, 2, 22),
+ BC7_MODE_PRIO_CODE(4, 1, 33),
+ BC7_MODE_PRIO_CODE(1, 3, 7),
+ BC7_MODE_PRIO_CODE(1, 1, 50),
+ BC7_MODE_PRIO_CODE(2, 1, 41),
+ BC7_MODE_PRIO_CODE(1, 2, 9),
+ BC7_MODE_PRIO_CODE(1, 2, 39),
+ BC7_MODE_PRIO_CODE(2, 2, 25),
+ BC7_MODE_PRIO_CODE(1, 3, 6),
+ BC7_MODE_PRIO_CODE(3, 2, 21),
+ BC7_MODE_PRIO_CODE(1, 1, 37),
+ BC7_MODE_PRIO_CODE(2, 2, 58),
+ BC7_MODE_PRIO_CODE(3, 3, 29),
+ BC7_MODE_PRIO_CODE(4, 1, 62),
+ BC7_MODE_PRIO_CODE(1, 2, 35),
+ BC7_MODE_PRIO_CODE(3, 1, 59),
+ BC7_MODE_PRIO_CODE(4, 1, 28),
+ BC7_MODE_PRIO_CODE(1, 3, 23),
+ BC7_MODE_PRIO_CODE(4, 1, 30),
+ BC7_MODE_PRIO_CODE(2, 1, 45),
+ BC7_MODE_PRIO_CODE(1, 3, 16),
+ BC7_MODE_PRIO_CODE(4, 1, 35),
+ BC7_MODE_PRIO_CODE(2, 1, 46),
+ BC7_MODE_PRIO_CODE(1, 2, 38),
+ BC7_MODE_PRIO_CODE(4, 1, 63),
+ BC7_MODE_PRIO_CODE(1, 3, 22),
+ BC7_MODE_PRIO_CODE(1, 2, 30),
+ BC7_MODE_PRIO_CODE(2, 2, 31),
+ BC7_MODE_PRIO_CODE(1, 3, 20),
+ BC7_MODE_PRIO_CODE(2, 2, 9),
+ BC7_MODE_PRIO_CODE(2, 3, 3),
+ BC7_MODE_PRIO_CODE(3, 2, 22),
+ BC7_MODE_PRIO_CODE(2, 1, 42),
+ BC7_MODE_PRIO_CODE(2, 2, 62),
+ BC7_MODE_PRIO_CODE(3, 2, 20),
+ BC7_MODE_PRIO_CODE(4, 1, 32),
+ BC7_MODE_PRIO_CODE(2, 1, 43),
+ BC7_MODE_PRIO_CODE(3, 1, 58),
+ BC7_MODE_PRIO_CODE(2, 3, 19),
+ BC7_MODE_PRIO_CODE(2, 2, 32),
+ BC7_MODE_PRIO_CODE(2, 2, 57),
+ BC7_MODE_PRIO_CODE(4, 1, 27),
+ BC7_MODE_PRIO_CODE(2, 2, 34),
+ BC7_MODE_PRIO_CODE(4, 1, 58),
+ BC7_MODE_PRIO_CODE(1, 2, 12),
+ BC7_MODE_PRIO_CODE(2, 2, 12),
+ BC7_MODE_PRIO_CODE(1, 4, 20),
+ BC7_MODE_PRIO_CODE(1, 2, 56),
+ BC7_MODE_PRIO_CODE(2, 1, 48),
+ BC7_MODE_PRIO_CODE(2, 1, 36),
+ BC7_MODE_PRIO_CODE(4, 3, 0),
+ BC7_MODE_PRIO_CODE(2, 2, 24),
+ BC7_MODE_PRIO_CODE(3, 1, 40),
+ BC7_MODE_PRIO_CODE(3, 2, 9),
+ BC7_MODE_PRIO_CODE(3, 1, 56),
+ BC7_MODE_PRIO_CODE(3, 2, 15),
+ BC7_MODE_PRIO_CODE(2, 3, 7),
+ BC7_MODE_PRIO_CODE(1, 2, 37),
+ BC7_MODE_PRIO_CODE(2, 2, 35),
+ BC7_MODE_PRIO_CODE(3, 1, 52),
+ BC7_MODE_PRIO_CODE(2, 3, 6),
+ BC7_MODE_PRIO_CODE(3, 1, 57),
+ BC7_MODE_PRIO_CODE(4, 1, 31),
+ BC7_MODE_PRIO_CODE(4, 4, 11),
+ BC7_MODE_PRIO_CODE(1, 1, 44),
+ BC7_MODE_PRIO_CODE(3, 3, 1),
+ BC7_MODE_PRIO_CODE(1, 2, 54),
+ BC7_MODE_PRIO_CODE(2, 1, 50),
+ BC7_MODE_PRIO_CODE(3, 3, 15),
+ BC7_MODE_PRIO_CODE(2, 1, 51),
+ BC7_MODE_PRIO_CODE(1, 2, 27),
+ BC7_MODE_PRIO_CODE(3, 4, 30),
+ BC7_MODE_PRIO_CODE(3, 3, 14),
+ BC7_MODE_PRIO_CODE(3, 2, 25),
+ BC7_MODE_PRIO_CODE(2, 3, 9),
+ BC7_MODE_PRIO_CODE(2, 2, 60),
+ BC7_MODE_PRIO_CODE(2, 1, 49),
+ BC7_MODE_PRIO_CODE(1, 2, 6),
+ BC7_MODE_PRIO_CODE(2, 2, 23),
+ BC7_MODE_PRIO_CODE(3, 2, 12),
+ BC7_MODE_PRIO_CODE(3, 3, 2),
+ BC7_MODE_PRIO_CODE(4, 2, 14),
+ BC7_MODE_PRIO_CODE(2, 3, 16),
+ BC7_MODE_PRIO_CODE(1, 2, 51),
+ BC7_MODE_PRIO_CODE(1, 3, 11),
+ BC7_MODE_PRIO_CODE(1, 2, 4),
+ BC7_MODE_PRIO_CODE(4, 2, 17),
+ BC7_MODE_PRIO_CODE(1, 3, 12),
+ BC7_MODE_PRIO_CODE(3, 1, 43),
+ BC7_MODE_PRIO_CODE(2, 4, 21),
+ BC7_MODE_PRIO_CODE(4, 1, 56),
+ BC7_MODE_PRIO_CODE(3, 1, 53),
+ BC7_MODE_PRIO_CODE(3, 1, 47),
+ BC7_MODE_PRIO_CODE(2, 2, 61),
+ BC7_MODE_PRIO_CODE(2, 2, 55),
+ BC7_MODE_PRIO_CODE(2, 3, 23),
+ BC7_MODE_PRIO_CODE(3, 1, 42),
+ BC7_MODE_PRIO_CODE(2, 3, 8),
+ BC7_MODE_PRIO_CODE(3, 1, 55),
+ BC7_MODE_PRIO_CODE(4, 1, 59),
+ BC7_MODE_PRIO_CODE(3, 2, 60),
+ BC7_MODE_PRIO_CODE(2, 3, 20),
+ BC7_MODE_PRIO_CODE(3, 2, 57),
+ BC7_MODE_PRIO_CODE(3, 1, 54),
+ BC7_MODE_PRIO_CODE(3, 2, 35),
+ BC7_MODE_PRIO_CODE(1, 1, 38),
+ BC7_MODE_PRIO_CODE(1, 2, 5),
+ BC7_MODE_PRIO_CODE(2, 2, 5),
+ BC7_MODE_PRIO_CODE(2, 2, 6),
+ BC7_MODE_PRIO_CODE(3, 2, 23),
+ BC7_MODE_PRIO_CODE(2, 2, 59),
+ BC7_MODE_PRIO_CODE(3, 2, 5),
+ BC7_MODE_PRIO_CODE(4, 1, 42),
+ BC7_MODE_PRIO_CODE(2, 1, 37),
+ BC7_MODE_PRIO_CODE(3, 2, 59),
+ BC7_MODE_PRIO_CODE(4, 2, 9),
+ BC7_MODE_PRIO_CODE(2, 2, 4),
+ BC7_MODE_PRIO_CODE(2, 2, 56),
+ BC7_MODE_PRIO_CODE(1, 3, 33),
+ BC7_MODE_PRIO_CODE(2, 3, 33),
+ BC7_MODE_PRIO_CODE(2, 3, 22),
+ BC7_MODE_PRIO_CODE(2, 3, 12),
+ BC7_MODE_PRIO_CODE(4, 1, 40),
+ BC7_MODE_PRIO_CODE(3, 2, 34),
+ BC7_MODE_PRIO_CODE(3, 2, 56),
+ BC7_MODE_PRIO_CODE(3, 3, 26),
+ BC7_MODE_PRIO_CODE(1, 2, 7),
+ BC7_MODE_PRIO_CODE(2, 2, 7),
+ BC7_MODE_PRIO_CODE(3, 2, 7),
+ BC7_MODE_PRIO_CODE(2, 2, 36),
+ BC7_MODE_PRIO_CODE(3, 2, 36),
+ BC7_MODE_PRIO_CODE(4, 1, 52),
+ BC7_MODE_PRIO_CODE(2, 2, 33),
+ BC7_MODE_PRIO_CODE(3, 1, 45),
+ BC7_MODE_PRIO_CODE(1, 3, 4),
+ BC7_MODE_PRIO_CODE(4, 2, 15),
+ BC7_MODE_PRIO_CODE(3, 1, 41),
+ BC7_MODE_PRIO_CODE(2, 2, 54),
+ BC7_MODE_PRIO_CODE(3, 2, 4),
+ BC7_MODE_PRIO_CODE(2, 5, 20),
+ BC7_MODE_PRIO_CODE(3, 2, 62),
+ BC7_MODE_PRIO_CODE(1, 3, 35),
+ BC7_MODE_PRIO_CODE(4, 1, 41),
+ BC7_MODE_PRIO_CODE(3, 2, 6),
+ BC7_MODE_PRIO_CODE(2, 2, 52),
+ BC7_MODE_PRIO_CODE(3, 1, 46),
+ BC7_MODE_PRIO_CODE(1, 1, 39),
+ BC7_MODE_PRIO_CODE(3, 2, 33),
+ BC7_MODE_PRIO_CODE(1, 3, 5),
+ BC7_MODE_PRIO_CODE(3, 1, 48),
+ BC7_MODE_PRIO_CODE(3, 2, 24),
+ BC7_MODE_PRIO_CODE(3, 2, 32),
+ BC7_MODE_PRIO_CODE(3, 3, 33),
+ BC7_MODE_PRIO_CODE(1, 3, 17),
+ BC7_MODE_PRIO_CODE(4, 1, 57),
+ BC7_MODE_PRIO_CODE(1, 3, 25),
+ BC7_MODE_PRIO_CODE(2, 3, 11),
+ BC7_MODE_PRIO_CODE(1, 3, 61),
+ BC7_MODE_PRIO_CODE(4, 1, 43),
+ BC7_MODE_PRIO_CODE(1, 3, 60),
+ BC7_MODE_PRIO_CODE(2, 3, 60),
+ BC7_MODE_PRIO_CODE(2, 2, 28),
+ BC7_MODE_PRIO_CODE(3, 2, 28),
+ BC7_MODE_PRIO_CODE(4, 1, 55),
+ BC7_MODE_PRIO_CODE(2, 3, 5),
+ BC7_MODE_PRIO_CODE(3, 1, 51),
+ BC7_MODE_PRIO_CODE(4, 1, 53),
+ BC7_MODE_PRIO_CODE(4, 1, 54),
+ BC7_MODE_PRIO_CODE(1, 3, 32),
+ BC7_MODE_PRIO_CODE(1, 3, 24),
+ BC7_MODE_PRIO_CODE(4, 1, 47),
+ BC7_MODE_PRIO_CODE(2, 2, 51),
+ BC7_MODE_PRIO_CODE(4, 2, 12),
+ BC7_MODE_PRIO_CODE(2, 3, 61),
+ BC7_MODE_PRIO_CODE(3, 4, 21),
+ BC7_MODE_PRIO_CODE(2, 3, 32),
+ BC7_MODE_PRIO_CODE(3, 1, 36),
+ BC7_MODE_PRIO_CODE(3, 1, 49),
+ BC7_MODE_PRIO_CODE(1, 3, 18),
+ BC7_MODE_PRIO_CODE(4, 3, 29),
+ BC7_MODE_PRIO_CODE(4, 2, 63),
+ BC7_MODE_PRIO_CODE(2, 2, 27),
+ BC7_MODE_PRIO_CODE(2, 3, 17),
+ BC7_MODE_PRIO_CODE(3, 1, 50),
+ BC7_MODE_PRIO_CODE(3, 2, 61),
+ BC7_MODE_PRIO_CODE(1, 3, 63),
+ BC7_MODE_PRIO_CODE(2, 3, 63),
+ BC7_MODE_PRIO_CODE(3, 2, 27),
+ BC7_MODE_PRIO_CODE(4, 1, 46),
+ BC7_MODE_PRIO_CODE(1, 2, 26),
+ BC7_MODE_PRIO_CODE(2, 3, 4),
+ BC7_MODE_PRIO_CODE(2, 3, 18),
+ BC7_MODE_PRIO_CODE(4, 1, 45),
+ BC7_MODE_PRIO_CODE(4, 1, 51),
+ BC7_MODE_PRIO_CODE(1, 2, 1),
+ BC7_MODE_PRIO_CODE(4, 2, 6),
+ BC7_MODE_PRIO_CODE(1, 3, 62),
+ BC7_MODE_PRIO_CODE(2, 3, 62),
+ BC7_MODE_PRIO_CODE(2, 1, 44),
+ BC7_MODE_PRIO_CODE(4, 1, 49),
+ BC7_MODE_PRIO_CODE(3, 5, 30),
+ BC7_MODE_PRIO_CODE(2, 3, 25),
+ BC7_MODE_PRIO_CODE(1, 2, 49),
+ BC7_MODE_PRIO_CODE(4, 1, 48),
+ BC7_MODE_PRIO_CODE(3, 3, 3),
+ BC7_MODE_PRIO_CODE(3, 1, 37),
+ BC7_MODE_PRIO_CODE(1, 2, 0),
+ BC7_MODE_PRIO_CODE(2, 2, 0),
+ BC7_MODE_PRIO_CODE(2, 3, 35),
+ BC7_MODE_PRIO_CODE(2, 3, 24),
+ BC7_MODE_PRIO_CODE(2, 2, 53),
+ BC7_MODE_PRIO_CODE(3, 2, 53),
+ BC7_MODE_PRIO_CODE(4, 2, 59),
+ BC7_MODE_PRIO_CODE(3, 3, 10),
+ BC7_MODE_PRIO_CODE(1, 2, 3),
+ BC7_MODE_PRIO_CODE(2, 2, 3),
+ BC7_MODE_PRIO_CODE(3, 2, 3),
+ BC7_MODE_PRIO_CODE(3, 3, 32),
+ BC7_MODE_PRIO_CODE(1, 2, 46),
+ BC7_MODE_PRIO_CODE(4, 2, 62),
+ BC7_MODE_PRIO_CODE(4, 2, 60),
+ BC7_MODE_PRIO_CODE(2, 2, 30),
+ BC7_MODE_PRIO_CODE(1, 3, 47),
+ BC7_MODE_PRIO_CODE(4, 2, 36),
+ BC7_MODE_PRIO_CODE(2, 2, 1),
+ BC7_MODE_PRIO_CODE(3, 2, 1),
+ BC7_MODE_PRIO_CODE(3, 2, 58),
+ BC7_MODE_PRIO_CODE(4, 1, 36),
+ BC7_MODE_PRIO_CODE(3, 3, 16),
+ BC7_MODE_PRIO_CODE(2, 3, 47),
+ BC7_MODE_PRIO_CODE(2, 2, 39),
+ BC7_MODE_PRIO_CODE(4, 1, 50),
+ BC7_MODE_PRIO_CODE(4, 2, 21),
+ BC7_MODE_PRIO_CODE(2, 1, 38),
+ BC7_MODE_PRIO_CODE(4, 4, 21),
+ BC7_MODE_PRIO_CODE(3, 3, 23),
+ BC7_MODE_PRIO_CODE(1, 2, 43),
+ BC7_MODE_PRIO_CODE(1, 2, 41),
+ BC7_MODE_PRIO_CODE(2, 2, 41),
+ BC7_MODE_PRIO_CODE(1, 3, 28),
+ BC7_MODE_PRIO_CODE(4, 2, 35),
+ BC7_MODE_PRIO_CODE(4, 3, 26),
+ BC7_MODE_PRIO_CODE(1, 3, 59),
+ BC7_MODE_PRIO_CODE(1, 1, 34),
+ BC7_MODE_PRIO_CODE(2, 2, 29),
+ BC7_MODE_PRIO_CODE(3, 2, 29),
+ BC7_MODE_PRIO_CODE(3, 2, 52),
+ BC7_MODE_PRIO_CODE(1, 3, 58),
+ BC7_MODE_PRIO_CODE(4, 5, 30),
+ BC7_MODE_PRIO_CODE(4, 3, 33),
+ BC7_MODE_PRIO_CODE(3, 2, 30),
+ BC7_MODE_PRIO_CODE(1, 2, 44),
+ BC7_MODE_PRIO_CODE(1, 2, 2),
+ BC7_MODE_PRIO_CODE(2, 2, 2),
+ BC7_MODE_PRIO_CODE(3, 2, 2),
+ BC7_MODE_PRIO_CODE(1, 2, 47),
+ BC7_MODE_PRIO_CODE(2, 2, 47),
+ BC7_MODE_PRIO_CODE(3, 3, 7),
+ BC7_MODE_PRIO_CODE(2, 3, 58),
+ BC7_MODE_PRIO_CODE(3, 2, 55),
+ BC7_MODE_PRIO_CODE(4, 2, 4),
+ BC7_MODE_PRIO_CODE(3, 2, 0),
+ BC7_MODE_PRIO_CODE(1, 3, 31),
+ BC7_MODE_PRIO_CODE(3, 2, 31),
+ BC7_MODE_PRIO_CODE(3, 3, 12),
+ BC7_MODE_PRIO_CODE(3, 2, 51),
+ BC7_MODE_PRIO_CODE(2, 1, 39),
+ BC7_MODE_PRIO_CODE(1, 3, 48),
+ BC7_MODE_PRIO_CODE(1, 3, 27),
+ BC7_MODE_PRIO_CODE(4, 2, 25),
+ BC7_MODE_PRIO_CODE(4, 2, 22),
+ BC7_MODE_PRIO_CODE(4, 2, 18),
+ BC7_MODE_PRIO_CODE(2, 2, 44),
+ BC7_MODE_PRIO_CODE(2, 3, 28),
+ BC7_MODE_PRIO_CODE(3, 1, 44),
+ BC7_MODE_PRIO_CODE(2, 1, 34),
+ BC7_MODE_PRIO_CODE(3, 5, 10),
+ BC7_MODE_PRIO_CODE(4, 4, 10),
+ BC7_MODE_PRIO_CODE(3, 2, 54),
+ BC7_MODE_PRIO_CODE(4, 2, 7),
+ BC7_MODE_PRIO_CODE(4, 2, 20),
+ BC7_MODE_PRIO_CODE(2, 2, 37),
+ BC7_MODE_PRIO_CODE(3, 3, 6),
+ BC7_MODE_PRIO_CODE(2, 2, 43),
+ BC7_MODE_PRIO_CODE(2, 3, 59),
+ BC7_MODE_PRIO_CODE(1, 3, 30),
+ BC7_MODE_PRIO_CODE(4, 2, 5),
+ BC7_MODE_PRIO_CODE(4, 2, 61),
+ BC7_MODE_PRIO_CODE(4, 2, 19),
+ BC7_MODE_PRIO_CODE(4, 2, 23),
+ BC7_MODE_PRIO_CODE(3, 2, 39),
+ BC7_MODE_PRIO_CODE(2, 3, 27),
+ BC7_MODE_PRIO_CODE(1, 3, 57),
+ BC7_MODE_PRIO_CODE(2, 3, 57),
+ BC7_MODE_PRIO_CODE(3, 3, 21),
+ BC7_MODE_PRIO_CODE(3, 3, 11),
+ BC7_MODE_PRIO_CODE(3, 1, 39),
+ BC7_MODE_PRIO_CODE(2, 3, 48),
+ BC7_MODE_PRIO_CODE(4, 1, 37),
+ BC7_MODE_PRIO_CODE(3, 3, 19),
+ BC7_MODE_PRIO_CODE(3, 1, 38),
+ BC7_MODE_PRIO_CODE(2, 2, 38),
+ BC7_MODE_PRIO_CODE(2, 3, 31),
+ BC7_MODE_PRIO_CODE(2, 2, 40),
+ BC7_MODE_PRIO_CODE(3, 2, 40),
+ BC7_MODE_PRIO_CODE(1, 3, 56),
+ BC7_MODE_PRIO_CODE(4, 5, 10),
+ BC7_MODE_PRIO_CODE(2, 3, 56),
+ BC7_MODE_PRIO_CODE(4, 1, 38),
+ BC7_MODE_PRIO_CODE(1, 3, 41),
+ BC7_MODE_PRIO_CODE(1, 3, 50),
+ BC7_MODE_PRIO_CODE(2, 3, 30),
+ BC7_MODE_PRIO_CODE(3, 3, 8),
+ BC7_MODE_PRIO_CODE(4, 2, 24),
+ BC7_MODE_PRIO_CODE(3, 3, 9),
+ BC7_MODE_PRIO_CODE(3, 1, 34),
+ BC7_MODE_PRIO_CODE(4, 1, 34),
+ BC7_MODE_PRIO_CODE(2, 3, 50),
+ BC7_MODE_PRIO_CODE(1, 3, 43),
+ BC7_MODE_PRIO_CODE(1, 3, 40),
+ BC7_MODE_PRIO_CODE(1, 3, 51),
+ BC7_MODE_PRIO_CODE(2, 3, 51),
+ BC7_MODE_PRIO_CODE(1, 3, 45),
+ BC7_MODE_PRIO_CODE(2, 3, 45),
+ BC7_MODE_PRIO_CODE(2, 3, 40),
+ BC7_MODE_PRIO_CODE(3, 3, 20),
+ BC7_MODE_PRIO_CODE(2, 3, 41),
+ BC7_MODE_PRIO_CODE(3, 2, 44),
+ BC7_MODE_PRIO_CODE(2, 3, 43),
+ BC7_MODE_PRIO_CODE(4, 2, 57),
+ BC7_MODE_PRIO_CODE(2, 4, 20),
+ BC7_MODE_PRIO_CODE(3, 3, 4),
+ BC7_MODE_PRIO_CODE(3, 3, 61),
+ BC7_MODE_PRIO_CODE(1, 3, 46),
+ BC7_MODE_PRIO_CODE(2, 3, 46),
+ BC7_MODE_PRIO_CODE(4, 3, 1),
+ BC7_MODE_PRIO_CODE(3, 3, 22),
+ BC7_MODE_PRIO_CODE(1, 3, 49),
+ BC7_MODE_PRIO_CODE(2, 3, 49),
+ BC7_MODE_PRIO_CODE(4, 3, 15),
+ BC7_MODE_PRIO_CODE(3, 3, 5),
+ BC7_MODE_PRIO_CODE(4, 1, 44),
+ BC7_MODE_PRIO_CODE(4, 3, 14),
+ BC7_MODE_PRIO_CODE(4, 3, 2),
+ BC7_MODE_PRIO_CODE(3, 3, 60),
+ BC7_MODE_PRIO_CODE(1, 3, 53),
+ BC7_MODE_PRIO_CODE(2, 3, 53),
+ BC7_MODE_PRIO_CODE(4, 3, 32),
+ BC7_MODE_PRIO_CODE(3, 3, 24),
+ BC7_MODE_PRIO_CODE(3, 3, 63),
+ BC7_MODE_PRIO_CODE(3, 2, 37),
+ BC7_MODE_PRIO_CODE(1, 3, 52),
+ BC7_MODE_PRIO_CODE(2, 3, 52),
+ BC7_MODE_PRIO_CODE(4, 4, 30),
+ BC7_MODE_PRIO_CODE(4, 2, 34),
+ BC7_MODE_PRIO_CODE(1, 3, 54),
+ BC7_MODE_PRIO_CODE(3, 3, 62),
+ BC7_MODE_PRIO_CODE(3, 3, 18),
+ BC7_MODE_PRIO_CODE(3, 2, 41),
+ BC7_MODE_PRIO_CODE(4, 2, 58),
+ BC7_MODE_PRIO_CODE(1, 3, 42),
+ BC7_MODE_PRIO_CODE(2, 3, 42),
+ BC7_MODE_PRIO_CODE(4, 2, 0),
+ BC7_MODE_PRIO_CODE(4, 2, 55),
+ BC7_MODE_PRIO_CODE(2, 3, 54),
+ BC7_MODE_PRIO_CODE(3, 2, 47),
+ BC7_MODE_PRIO_CODE(4, 2, 53),
+ BC7_MODE_PRIO_CODE(3, 3, 25),
+ BC7_MODE_PRIO_CODE(3, 4, 20),
+ BC7_MODE_PRIO_CODE(4, 2, 33),
+ BC7_MODE_PRIO_CODE(1, 3, 55),
+ BC7_MODE_PRIO_CODE(2, 3, 55),
+ BC7_MODE_PRIO_CODE(4, 2, 32),
+ BC7_MODE_PRIO_CODE(3, 2, 43),
+ BC7_MODE_PRIO_CODE(3, 3, 17),
+ BC7_MODE_PRIO_CODE(3, 5, 20),
+ BC7_MODE_PRIO_CODE(4, 5, 20),
+ BC7_MODE_PRIO_CODE(1, 3, 36),
+ BC7_MODE_PRIO_CODE(2, 3, 36),
+ BC7_MODE_PRIO_CODE(4, 2, 54),
+ BC7_MODE_PRIO_CODE(2, 2, 49),
+ BC7_MODE_PRIO_CODE(3, 2, 49),
+ BC7_MODE_PRIO_CODE(4, 1, 39),
+ BC7_MODE_PRIO_CODE(4, 2, 3),
+ BC7_MODE_PRIO_CODE(3, 3, 35),
+ BC7_MODE_PRIO_CODE(4, 2, 52),
+ BC7_MODE_PRIO_CODE(4, 2, 1),
+ BC7_MODE_PRIO_CODE(1, 2, 50),
+ BC7_MODE_PRIO_CODE(4, 2, 49),
+ BC7_MODE_PRIO_CODE(4, 3, 16),
+ BC7_MODE_PRIO_CODE(2, 2, 50),
+ BC7_MODE_PRIO_CODE(3, 2, 50),
+ BC7_MODE_PRIO_CODE(4, 2, 31),
+ BC7_MODE_PRIO_CODE(4, 3, 3),
+ BC7_MODE_PRIO_CODE(1, 2, 48),
+ BC7_MODE_PRIO_CODE(2, 2, 48),
+ BC7_MODE_PRIO_CODE(3, 2, 48),
+ BC7_MODE_PRIO_CODE(3, 3, 28),
+ BC7_MODE_PRIO_CODE(4, 3, 9),
+ BC7_MODE_PRIO_CODE(1, 3, 38),
+ BC7_MODE_PRIO_CODE(4, 3, 10),
+ BC7_MODE_PRIO_CODE(3, 3, 31),
+ BC7_MODE_PRIO_CODE(4, 2, 51),
+ BC7_MODE_PRIO_CODE(1, 3, 37),
+ BC7_MODE_PRIO_CODE(2, 3, 37),
+ BC7_MODE_PRIO_CODE(3, 3, 50),
+ BC7_MODE_PRIO_CODE(2, 3, 38),
+ BC7_MODE_PRIO_CODE(4, 3, 20),
+ BC7_MODE_PRIO_CODE(3, 3, 41),
+ BC7_MODE_PRIO_CODE(3, 3, 56),
+ BC7_MODE_PRIO_CODE(4, 3, 6),
+ BC7_MODE_PRIO_CODE(4, 3, 8),
+ BC7_MODE_PRIO_CODE(4, 2, 37),
+ BC7_MODE_PRIO_CODE(3, 3, 58),
+ BC7_MODE_PRIO_CODE(3, 3, 59),
+ BC7_MODE_PRIO_CODE(4, 2, 56),
+ BC7_MODE_PRIO_CODE(1, 3, 39),
+ BC7_MODE_PRIO_CODE(2, 3, 39),
+ BC7_MODE_PRIO_CODE(4, 2, 43),
+ BC7_MODE_PRIO_CODE(1, 3, 44),
+ BC7_MODE_PRIO_CODE(2, 3, 44),
+ BC7_MODE_PRIO_CODE(4, 3, 7),
+ BC7_MODE_PRIO_CODE(3, 3, 27),
+ BC7_MODE_PRIO_CODE(4, 3, 23),
+ BC7_MODE_PRIO_CODE(3, 3, 45),
+ BC7_MODE_PRIO_CODE(4, 3, 22),
+ BC7_MODE_PRIO_CODE(3, 3, 30),
+ BC7_MODE_PRIO_CODE(3, 3, 48),
+ BC7_MODE_PRIO_CODE(3, 3, 51),
+ BC7_MODE_PRIO_CODE(1, 2, 42),
+ BC7_MODE_PRIO_CODE(2, 2, 42),
+ BC7_MODE_PRIO_CODE(3, 2, 42),
+ BC7_MODE_PRIO_CODE(4, 3, 19),
+ BC7_MODE_PRIO_CODE(4, 3, 21),
+ BC7_MODE_PRIO_CODE(2, 2, 46),
+ BC7_MODE_PRIO_CODE(3, 3, 36),
+ BC7_MODE_PRIO_CODE(4, 2, 28),
+ BC7_MODE_PRIO_CODE(3, 3, 49),
+ BC7_MODE_PRIO_CODE(3, 3, 53),
+ BC7_MODE_PRIO_CODE(3, 3, 55),
+ BC7_MODE_PRIO_CODE(2, 2, 26),
+ BC7_MODE_PRIO_CODE(3, 2, 26),
+ BC7_MODE_PRIO_CODE(4, 2, 30),
+ BC7_MODE_PRIO_CODE(3, 3, 52),
+ BC7_MODE_PRIO_CODE(4, 2, 41),
+ BC7_MODE_PRIO_CODE(4, 2, 29),
+ BC7_MODE_PRIO_CODE(1, 3, 34),
+ BC7_MODE_PRIO_CODE(2, 3, 34),
+ BC7_MODE_PRIO_CODE(4, 2, 44),
+ BC7_MODE_PRIO_CODE(3, 3, 43),
+ BC7_MODE_PRIO_CODE(4, 2, 47),
+ BC7_MODE_PRIO_CODE(4, 3, 18),
+ BC7_MODE_PRIO_CODE(4, 3, 17),
+ BC7_MODE_PRIO_CODE(3, 3, 47),
+ BC7_MODE_PRIO_CODE(4, 3, 11),
+ BC7_MODE_PRIO_CODE(3, 3, 57),
+ BC7_MODE_PRIO_CODE(3, 2, 38),
+ BC7_MODE_PRIO_CODE(3, 3, 46),
+ BC7_MODE_PRIO_CODE(4, 3, 25),
+ BC7_MODE_PRIO_CODE(4, 3, 4),
+ BC7_MODE_PRIO_CODE(3, 3, 42),
+ BC7_MODE_PRIO_CODE(4, 3, 61),
+ BC7_MODE_PRIO_CODE(4, 2, 48),
+ BC7_MODE_PRIO_CODE(4, 3, 5),
+ BC7_MODE_PRIO_CODE(3, 3, 54),
+ BC7_MODE_PRIO_CODE(4, 4, 20),
+ BC7_MODE_PRIO_CODE(4, 3, 24),
+ BC7_MODE_PRIO_CODE(4, 3, 12),
+ BC7_MODE_PRIO_CODE(4, 2, 40),
+ BC7_MODE_PRIO_CODE(3, 3, 40),
+ BC7_MODE_PRIO_CODE(3, 3, 44),
+ BC7_MODE_PRIO_CODE(4, 3, 63),
+ BC7_MODE_PRIO_CODE(4, 3, 50),
+ BC7_MODE_PRIO_CODE(4, 2, 50),
+ BC7_MODE_PRIO_CODE(4, 3, 60),
+ BC7_MODE_PRIO_CODE(4, 2, 39),
+ BC7_MODE_PRIO_CODE(4, 3, 62),
+ BC7_MODE_PRIO_CODE(4, 3, 49),
+ BC7_MODE_PRIO_CODE(4, 3, 58),
+ BC7_MODE_PRIO_CODE(4, 3, 47),
+ BC7_MODE_PRIO_CODE(4, 3, 56),
+ BC7_MODE_PRIO_CODE(4, 2, 26),
+ BC7_MODE_PRIO_CODE(4, 2, 27),
+ BC7_MODE_PRIO_CODE(3, 3, 37),
+ BC7_MODE_PRIO_CODE(4, 3, 57),
+ BC7_MODE_PRIO_CODE(4, 3, 48),
+ BC7_MODE_PRIO_CODE(4, 3, 31),
+ BC7_MODE_PRIO_CODE(4, 3, 51),
+ BC7_MODE_PRIO_CODE(4, 3, 28),
+ BC7_MODE_PRIO_CODE(4, 3, 53),
+ BC7_MODE_PRIO_CODE(3, 3, 39),
+ BC7_MODE_PRIO_CODE(4, 3, 40),
+ BC7_MODE_PRIO_CODE(4, 3, 27),
+ BC7_MODE_PRIO_CODE(4, 2, 2),
+ BC7_MODE_PRIO_CODE(3, 3, 34),
+ BC7_MODE_PRIO_CODE(4, 2, 38),
+ BC7_MODE_PRIO_CODE(4, 3, 54),
+ BC7_MODE_PRIO_CODE(3, 3, 38),
+ BC7_MODE_PRIO_CODE(4, 3, 52),
+ BC7_MODE_PRIO_CODE(4, 3, 30),
+ BC7_MODE_PRIO_CODE(4, 3, 59),
+ BC7_MODE_PRIO_CODE(1, 2, 45),
+ BC7_MODE_PRIO_CODE(4, 3, 45),
+ BC7_MODE_PRIO_CODE(4, 2, 42),
+ BC7_MODE_PRIO_CODE(4, 3, 35),
+ BC7_MODE_PRIO_CODE(4, 3, 41),
+ BC7_MODE_PRIO_CODE(3, 2, 46),
+ BC7_MODE_PRIO_CODE(4, 2, 46),
+ BC7_MODE_PRIO_CODE(4, 3, 46),
+ BC7_MODE_PRIO_CODE(2, 2, 45),
+ BC7_MODE_PRIO_CODE(4, 3, 43),
+ BC7_MODE_PRIO_CODE(4, 3, 37),
+ BC7_MODE_PRIO_CODE(4, 3, 38),
+ BC7_MODE_PRIO_CODE(4, 3, 36),
+ BC7_MODE_PRIO_CODE(4, 3, 42),
+ BC7_MODE_PRIO_CODE(4, 3, 34),
+ BC7_MODE_PRIO_CODE(4, 3, 39),
+ BC7_MODE_PRIO_CODE(4, 3, 55),
+ BC7_MODE_PRIO_CODE(4, 3, 44),
+ BC7_MODE_PRIO_CODE(3, 2, 45),
+ BC7_MODE_PRIO_CODE(1, 4, 0),
+ BC7_MODE_PRIO_CODE(1, 4, 1),
+ BC7_MODE_PRIO_CODE(1, 5, 0),
+ BC7_MODE_PRIO_CODE(4, 2, 45),
+ BC7_MODE_PRIO_CODE(2, 4, 0),
+ BC7_MODE_PRIO_CODE(2, 4, 1),
+ BC7_MODE_PRIO_CODE(2, 5, 0),
+ BC7_MODE_PRIO_CODE(3, 4, 0),
+ BC7_MODE_PRIO_CODE(3, 4, 1),
+ BC7_MODE_PRIO_CODE(3, 5, 0),
+ BC7_MODE_PRIO_CODE(4, 4, 0),
+ BC7_MODE_PRIO_CODE(4, 4, 1),
+ BC7_MODE_PRIO_CODE(4, 5, 0),
+ };
+
+ const uint16_t *g_bc7PrioCodesRGB = g_bc7PrioCodesRGBData;
+ const int g_bc7NumPrioCodesRGB = sizeof(g_bc7PrioCodesRGBData) / sizeof(g_bc7PrioCodesRGBData[0]);
+
+ const uint16_t g_bc7PrioCodesRGBAData[] =
+ {
+ BC7_MODE_PRIO_CODE(1, 4, 1),
+ BC7_MODE_PRIO_CODE(1, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 4, 31),
+ BC7_MODE_PRIO_CODE(1, 4, 11),
+ BC7_MODE_PRIO_CODE(1, 4, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 13),
+ BC7_MODE_PRIO_CODE(1, 5, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 0),
+ BC7_MODE_PRIO_CODE(2, 4, 1),
+ BC7_MODE_PRIO_CODE(3, 4, 1),
+ BC7_MODE_PRIO_CODE(2, 4, 0),
+ BC7_MODE_PRIO_CODE(2, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 6),
+ BC7_MODE_PRIO_CODE(1, 4, 10),
+ BC7_MODE_PRIO_CODE(1, 7, 15),
+ BC7_MODE_PRIO_CODE(1, 7, 14),
+ BC7_MODE_PRIO_CODE(1, 4, 30),
+ BC7_MODE_PRIO_CODE(1, 7, 7),
+ BC7_MODE_PRIO_CODE(3, 6, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 19),
+ BC7_MODE_PRIO_CODE(3, 4, 0),
+ BC7_MODE_PRIO_CODE(2, 7, 13),
+ BC7_MODE_PRIO_CODE(1, 5, 30),
+ BC7_MODE_PRIO_CODE(1, 7, 2),
+ BC7_MODE_PRIO_CODE(1, 7, 1),
+ BC7_MODE_PRIO_CODE(1, 7, 21),
+ BC7_MODE_PRIO_CODE(4, 4, 1),
+ BC7_MODE_PRIO_CODE(1, 4, 21),
+ BC7_MODE_PRIO_CODE(2, 4, 31),
+ BC7_MODE_PRIO_CODE(1, 7, 10),
+ BC7_MODE_PRIO_CODE(1, 7, 3),
+ BC7_MODE_PRIO_CODE(4, 6, 0),
+ BC7_MODE_PRIO_CODE(3, 7, 13),
+ BC7_MODE_PRIO_CODE(1, 7, 16),
+ BC7_MODE_PRIO_CODE(1, 7, 8),
+ BC7_MODE_PRIO_CODE(2, 5, 0),
+ BC7_MODE_PRIO_CODE(2, 7, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 23),
+ BC7_MODE_PRIO_CODE(1, 7, 9),
+ BC7_MODE_PRIO_CODE(2, 4, 11),
+ BC7_MODE_PRIO_CODE(3, 4, 31),
+ BC7_MODE_PRIO_CODE(1, 7, 20),
+ BC7_MODE_PRIO_CODE(1, 7, 22),
+ BC7_MODE_PRIO_CODE(4, 4, 0),
+ BC7_MODE_PRIO_CODE(1, 5, 10),
+ BC7_MODE_PRIO_CODE(4, 7, 13),
+ BC7_MODE_PRIO_CODE(3, 7, 0),
+ BC7_MODE_PRIO_CODE(1, 7, 12),
+ BC7_MODE_PRIO_CODE(1, 7, 29),
+ BC7_MODE_PRIO_CODE(3, 4, 11),
+ BC7_MODE_PRIO_CODE(1, 7, 11),
+ BC7_MODE_PRIO_CODE(1, 7, 18),
+ BC7_MODE_PRIO_CODE(1, 7, 4),
+ BC7_MODE_PRIO_CODE(2, 7, 15),
+ BC7_MODE_PRIO_CODE(2, 7, 14),
+ BC7_MODE_PRIO_CODE(1, 7, 5),
+ BC7_MODE_PRIO_CODE(1, 7, 25),
+ BC7_MODE_PRIO_CODE(1, 7, 17),
+ BC7_MODE_PRIO_CODE(1, 7, 24),
+ BC7_MODE_PRIO_CODE(1, 7, 26),
+ BC7_MODE_PRIO_CODE(3, 5, 0),
+ BC7_MODE_PRIO_CODE(2, 7, 2),
+ BC7_MODE_PRIO_CODE(1, 5, 20),
+ BC7_MODE_PRIO_CODE(2, 7, 1),
+ BC7_MODE_PRIO_CODE(2, 7, 29),
+ BC7_MODE_PRIO_CODE(2, 4, 10),
+ BC7_MODE_PRIO_CODE(4, 7, 0),
+ BC7_MODE_PRIO_CODE(2, 7, 6),
+ BC7_MODE_PRIO_CODE(2, 7, 7),
+ BC7_MODE_PRIO_CODE(3, 7, 14),
+ BC7_MODE_PRIO_CODE(3, 7, 15),
+ BC7_MODE_PRIO_CODE(4, 4, 31),
+ BC7_MODE_PRIO_CODE(2, 7, 21),
+ BC7_MODE_PRIO_CODE(2, 4, 30),
+ BC7_MODE_PRIO_CODE(2, 4, 21),
+ BC7_MODE_PRIO_CODE(3, 7, 29),
+ BC7_MODE_PRIO_CODE(2, 7, 19),
+ BC7_MODE_PRIO_CODE(2, 7, 10),
+ BC7_MODE_PRIO_CODE(3, 7, 1),
+ BC7_MODE_PRIO_CODE(4, 7, 29),
+ BC7_MODE_PRIO_CODE(3, 7, 7),
+ BC7_MODE_PRIO_CODE(1, 4, 20),
+ BC7_MODE_PRIO_CODE(3, 7, 2),
+ BC7_MODE_PRIO_CODE(2, 7, 16),
+ BC7_MODE_PRIO_CODE(2, 7, 3),
+ BC7_MODE_PRIO_CODE(2, 5, 30),
+ BC7_MODE_PRIO_CODE(2, 7, 23),
+ BC7_MODE_PRIO_CODE(3, 7, 6),
+ BC7_MODE_PRIO_CODE(2, 7, 12),
+ BC7_MODE_PRIO_CODE(1, 7, 61),
+ BC7_MODE_PRIO_CODE(4, 4, 11),
+ BC7_MODE_PRIO_CODE(3, 4, 10),
+ BC7_MODE_PRIO_CODE(3, 7, 10),
+ BC7_MODE_PRIO_CODE(2, 7, 8),
+ BC7_MODE_PRIO_CODE(2, 7, 22),
+ BC7_MODE_PRIO_CODE(2, 7, 26),
+ BC7_MODE_PRIO_CODE(3, 4, 30),
+ BC7_MODE_PRIO_CODE(2, 7, 9),
+ BC7_MODE_PRIO_CODE(3, 7, 19),
+ BC7_MODE_PRIO_CODE(2, 7, 25),
+ BC7_MODE_PRIO_CODE(3, 4, 21),
+ BC7_MODE_PRIO_CODE(2, 7, 24),
+ BC7_MODE_PRIO_CODE(1, 7, 60),
+ BC7_MODE_PRIO_CODE(2, 7, 11),
+ BC7_MODE_PRIO_CODE(2, 7, 18),
+ BC7_MODE_PRIO_CODE(2, 7, 17),
+ BC7_MODE_PRIO_CODE(2, 7, 4),
+ BC7_MODE_PRIO_CODE(2, 7, 5),
+ BC7_MODE_PRIO_CODE(3, 7, 3),
+ BC7_MODE_PRIO_CODE(3, 7, 16),
+ BC7_MODE_PRIO_CODE(3, 7, 26),
+ BC7_MODE_PRIO_CODE(3, 7, 21),
+ BC7_MODE_PRIO_CODE(1, 7, 62),
+ BC7_MODE_PRIO_CODE(2, 7, 20),
+ BC7_MODE_PRIO_CODE(3, 7, 23),
+ BC7_MODE_PRIO_CODE(1, 7, 33),
+ BC7_MODE_PRIO_CODE(2, 7, 33),
+ BC7_MODE_PRIO_CODE(3, 7, 33),
+ BC7_MODE_PRIO_CODE(4, 7, 33),
+ BC7_MODE_PRIO_CODE(3, 7, 11),
+ BC7_MODE_PRIO_CODE(3, 7, 12),
+ BC7_MODE_PRIO_CODE(4, 7, 26),
+ BC7_MODE_PRIO_CODE(3, 7, 25),
+ BC7_MODE_PRIO_CODE(1, 7, 63),
+ BC7_MODE_PRIO_CODE(2, 5, 10),
+ BC7_MODE_PRIO_CODE(3, 7, 8),
+ BC7_MODE_PRIO_CODE(4, 5, 0),
+ BC7_MODE_PRIO_CODE(3, 7, 24),
+ BC7_MODE_PRIO_CODE(3, 7, 22),
+ BC7_MODE_PRIO_CODE(3, 7, 9),
+ BC7_MODE_PRIO_CODE(1, 7, 32),
+ BC7_MODE_PRIO_CODE(2, 7, 61),
+ BC7_MODE_PRIO_CODE(3, 7, 4),
+ BC7_MODE_PRIO_CODE(3, 5, 30),
+ BC7_MODE_PRIO_CODE(3, 7, 20),
+ BC7_MODE_PRIO_CODE(1, 7, 35),
+ BC7_MODE_PRIO_CODE(4, 7, 14),
+ BC7_MODE_PRIO_CODE(3, 7, 5),
+ BC7_MODE_PRIO_CODE(3, 7, 18),
+ BC7_MODE_PRIO_CODE(1, 7, 30),
+ BC7_MODE_PRIO_CODE(1, 7, 43),
+ BC7_MODE_PRIO_CODE(4, 4, 21),
+ BC7_MODE_PRIO_CODE(4, 7, 15),
+ BC7_MODE_PRIO_CODE(3, 7, 17),
+ BC7_MODE_PRIO_CODE(2, 7, 32),
+ BC7_MODE_PRIO_CODE(3, 7, 32),
+ BC7_MODE_PRIO_CODE(2, 5, 20),
+ BC7_MODE_PRIO_CODE(4, 7, 1),
+ BC7_MODE_PRIO_CODE(4, 7, 2),
+ BC7_MODE_PRIO_CODE(1, 7, 28),
+ BC7_MODE_PRIO_CODE(1, 7, 54),
+ BC7_MODE_PRIO_CODE(4, 7, 32),
+ BC7_MODE_PRIO_CODE(1, 7, 27),
+ BC7_MODE_PRIO_CODE(4, 4, 10),
+ BC7_MODE_PRIO_CODE(3, 5, 10),
+ BC7_MODE_PRIO_CODE(2, 7, 60),
+ BC7_MODE_PRIO_CODE(2, 4, 20),
+ BC7_MODE_PRIO_CODE(2, 7, 63),
+ BC7_MODE_PRIO_CODE(4, 4, 30),
+ BC7_MODE_PRIO_CODE(2, 7, 62),
+ BC7_MODE_PRIO_CODE(1, 7, 41),
+ BC7_MODE_PRIO_CODE(1, 7, 58),
+ BC7_MODE_PRIO_CODE(3, 7, 60),
+ BC7_MODE_PRIO_CODE(1, 7, 40),
+ BC7_MODE_PRIO_CODE(1, 7, 55),
+ BC7_MODE_PRIO_CODE(2, 7, 35),
+ BC7_MODE_PRIO_CODE(4, 7, 8),
+ BC7_MODE_PRIO_CODE(4, 7, 6),
+ BC7_MODE_PRIO_CODE(1, 7, 53),
+ BC7_MODE_PRIO_CODE(4, 7, 9),
+ BC7_MODE_PRIO_CODE(3, 7, 61),
+ BC7_MODE_PRIO_CODE(3, 4, 20),
+ BC7_MODE_PRIO_CODE(4, 7, 22),
+ BC7_MODE_PRIO_CODE(4, 7, 20),
+ BC7_MODE_PRIO_CODE(3, 7, 62),
+ BC7_MODE_PRIO_CODE(4, 7, 7),
+ BC7_MODE_PRIO_CODE(1, 7, 42),
+ BC7_MODE_PRIO_CODE(1, 7, 52),
+ BC7_MODE_PRIO_CODE(4, 5, 30),
+ BC7_MODE_PRIO_CODE(1, 7, 56),
+ BC7_MODE_PRIO_CODE(1, 7, 31),
+ BC7_MODE_PRIO_CODE(3, 5, 20),
+ BC7_MODE_PRIO_CODE(1, 7, 48),
+ BC7_MODE_PRIO_CODE(2, 7, 28),
+ BC7_MODE_PRIO_CODE(3, 7, 28),
+ BC7_MODE_PRIO_CODE(4, 7, 19),
+ BC7_MODE_PRIO_CODE(3, 7, 35),
+ BC7_MODE_PRIO_CODE(1, 7, 59),
+ BC7_MODE_PRIO_CODE(2, 7, 30),
+ BC7_MODE_PRIO_CODE(3, 7, 63),
+ BC7_MODE_PRIO_CODE(4, 7, 21),
+ BC7_MODE_PRIO_CODE(4, 7, 10),
+ BC7_MODE_PRIO_CODE(4, 7, 3),
+ BC7_MODE_PRIO_CODE(1, 7, 47),
+ BC7_MODE_PRIO_CODE(1, 7, 37),
+ BC7_MODE_PRIO_CODE(4, 5, 10),
+ BC7_MODE_PRIO_CODE(4, 7, 23),
+ BC7_MODE_PRIO_CODE(1, 7, 57),
+ BC7_MODE_PRIO_CODE(4, 7, 17),
+ BC7_MODE_PRIO_CODE(1, 7, 45),
+ BC7_MODE_PRIO_CODE(4, 7, 24),
+ BC7_MODE_PRIO_CODE(4, 7, 60),
+ BC7_MODE_PRIO_CODE(1, 7, 50),
+ BC7_MODE_PRIO_CODE(2, 7, 41),
+ BC7_MODE_PRIO_CODE(4, 7, 25),
+ BC7_MODE_PRIO_CODE(3, 7, 30),
+ BC7_MODE_PRIO_CODE(2, 7, 59),
+ BC7_MODE_PRIO_CODE(2, 7, 55),
+ BC7_MODE_PRIO_CODE(4, 7, 18),
+ BC7_MODE_PRIO_CODE(4, 7, 12),
+ BC7_MODE_PRIO_CODE(4, 7, 5),
+ BC7_MODE_PRIO_CODE(3, 7, 59),
+ BC7_MODE_PRIO_CODE(1, 7, 51),
+ BC7_MODE_PRIO_CODE(4, 7, 16),
+ BC7_MODE_PRIO_CODE(4, 7, 11),
+ BC7_MODE_PRIO_CODE(2, 7, 58),
+ BC7_MODE_PRIO_CODE(3, 7, 41),
+ BC7_MODE_PRIO_CODE(4, 4, 20),
+ BC7_MODE_PRIO_CODE(4, 7, 4),
+ BC7_MODE_PRIO_CODE(1, 7, 49),
+ BC7_MODE_PRIO_CODE(2, 7, 27),
+ BC7_MODE_PRIO_CODE(3, 7, 27),
+ BC7_MODE_PRIO_CODE(4, 7, 62),
+ BC7_MODE_PRIO_CODE(3, 7, 58),
+ BC7_MODE_PRIO_CODE(4, 5, 20),
+ BC7_MODE_PRIO_CODE(2, 7, 53),
+ BC7_MODE_PRIO_CODE(3, 7, 53),
+ BC7_MODE_PRIO_CODE(2, 7, 40),
+ BC7_MODE_PRIO_CODE(3, 7, 40),
+ BC7_MODE_PRIO_CODE(2, 7, 31),
+ BC7_MODE_PRIO_CODE(3, 7, 31),
+ BC7_MODE_PRIO_CODE(4, 7, 61),
+ BC7_MODE_PRIO_CODE(1, 7, 36),
+ BC7_MODE_PRIO_CODE(4, 7, 63),
+ BC7_MODE_PRIO_CODE(1, 7, 46),
+ BC7_MODE_PRIO_CODE(3, 7, 55),
+ BC7_MODE_PRIO_CODE(2, 7, 52),
+ BC7_MODE_PRIO_CODE(2, 7, 56),
+ BC7_MODE_PRIO_CODE(2, 7, 42),
+ BC7_MODE_PRIO_CODE(2, 7, 37),
+ BC7_MODE_PRIO_CODE(2, 7, 57),
+ BC7_MODE_PRIO_CODE(3, 7, 57),
+ BC7_MODE_PRIO_CODE(2, 7, 45),
+ BC7_MODE_PRIO_CODE(4, 7, 57),
+ BC7_MODE_PRIO_CODE(2, 7, 49),
+ BC7_MODE_PRIO_CODE(3, 7, 42),
+ BC7_MODE_PRIO_CODE(2, 7, 43),
+ BC7_MODE_PRIO_CODE(3, 7, 43),
+ BC7_MODE_PRIO_CODE(4, 7, 28),
+ BC7_MODE_PRIO_CODE(2, 7, 48),
+ BC7_MODE_PRIO_CODE(3, 7, 52),
+ BC7_MODE_PRIO_CODE(3, 7, 49),
+ BC7_MODE_PRIO_CODE(4, 7, 59),
+ BC7_MODE_PRIO_CODE(4, 7, 40),
+ BC7_MODE_PRIO_CODE(4, 7, 27),
+ BC7_MODE_PRIO_CODE(3, 7, 45),
+ BC7_MODE_PRIO_CODE(4, 7, 55),
+ BC7_MODE_PRIO_CODE(3, 7, 56),
+ BC7_MODE_PRIO_CODE(4, 7, 42),
+ BC7_MODE_PRIO_CODE(2, 7, 54),
+ BC7_MODE_PRIO_CODE(3, 7, 54),
+ BC7_MODE_PRIO_CODE(4, 7, 54),
+ BC7_MODE_PRIO_CODE(2, 7, 47),
+ BC7_MODE_PRIO_CODE(3, 7, 47),
+ BC7_MODE_PRIO_CODE(4, 7, 43),
+ BC7_MODE_PRIO_CODE(4, 7, 31),
+ BC7_MODE_PRIO_CODE(3, 7, 37),
+ BC7_MODE_PRIO_CODE(3, 7, 48),
+ BC7_MODE_PRIO_CODE(4, 7, 48),
+ BC7_MODE_PRIO_CODE(4, 7, 45),
+ BC7_MODE_PRIO_CODE(4, 7, 47),
+ BC7_MODE_PRIO_CODE(2, 7, 36),
+ BC7_MODE_PRIO_CODE(1, 7, 44),
+ BC7_MODE_PRIO_CODE(4, 7, 35),
+ BC7_MODE_PRIO_CODE(4, 7, 58),
+ BC7_MODE_PRIO_CODE(3, 7, 36),
+ BC7_MODE_PRIO_CODE(2, 7, 50),
+ BC7_MODE_PRIO_CODE(3, 7, 50),
+ BC7_MODE_PRIO_CODE(4, 7, 50),
+ BC7_MODE_PRIO_CODE(4, 7, 52),
+ BC7_MODE_PRIO_CODE(1, 7, 39),
+ BC7_MODE_PRIO_CODE(1, 7, 34),
+ BC7_MODE_PRIO_CODE(1, 7, 38),
+ BC7_MODE_PRIO_CODE(2, 7, 38),
+ BC7_MODE_PRIO_CODE(3, 7, 38),
+ BC7_MODE_PRIO_CODE(4, 7, 30),
+ BC7_MODE_PRIO_CODE(2, 7, 51),
+ BC7_MODE_PRIO_CODE(4, 7, 41),
+ BC7_MODE_PRIO_CODE(4, 7, 53),
+ BC7_MODE_PRIO_CODE(2, 7, 46),
+ BC7_MODE_PRIO_CODE(3, 7, 46),
+ BC7_MODE_PRIO_CODE(4, 7, 49),
+ BC7_MODE_PRIO_CODE(4, 7, 56),
+ BC7_MODE_PRIO_CODE(4, 7, 37),
+ BC7_MODE_PRIO_CODE(2, 7, 44),
+ BC7_MODE_PRIO_CODE(3, 7, 44),
+ BC7_MODE_PRIO_CODE(4, 7, 36),
+ BC7_MODE_PRIO_CODE(2, 7, 39),
+ BC7_MODE_PRIO_CODE(2, 7, 34),
+ BC7_MODE_PRIO_CODE(4, 7, 38),
+ BC7_MODE_PRIO_CODE(3, 7, 51),
+ BC7_MODE_PRIO_CODE(4, 7, 51),
+ BC7_MODE_PRIO_CODE(4, 7, 46),
+ BC7_MODE_PRIO_CODE(4, 7, 44),
+ BC7_MODE_PRIO_CODE(3, 7, 39),
+ BC7_MODE_PRIO_CODE(3, 7, 34),
+ BC7_MODE_PRIO_CODE(4, 7, 39),
+ BC7_MODE_PRIO_CODE(4, 7, 34),
+ };
+
+ const uint16_t *g_bc7PrioCodesRGBA = g_bc7PrioCodesRGBAData;
+ const int g_bc7NumPrioCodesRGBA = sizeof(g_bc7PrioCodesRGBAData) / sizeof(g_bc7PrioCodesRGBA[0]);
+
+ int UnpackMode(uint16_t packed)
+ {
+ return static_cast<int>((packed >> BC7_MODE_OFFSET_BITS) & ((1 << BC7_MODE_BITS) - 1));
+ }
+
+ int UnpackSeedPointCount(uint16_t packed)
+ {
+ return static_cast<int>((packed >> BC7_SEED_POINT_COUNT_OFFSET_BITS) & ((1 << BC7_SEED_POINT_COUNT_BITS) - 1)) + 1;
+ }
+
+ int UnpackPartition(uint16_t packed)
+ {
+ return static_cast<int>((packed >> BC7_PARTITION_OFFSET_BITS) & ((1 << BC7_PARTITION_BITS) - 1));
+ }
+
+ int UnpackRotation(uint16_t packed)
+ {
+ return static_cast<int>((packed >> BC7_ROTATION_OFFSET_BITS) & ((1 << BC7_ROTATION_BITS) - 1));
+ }
+
+ int UnpackIndexSelector(uint16_t packed)
+ {
+ return static_cast<int>((packed >> BC7_INDEX_MODE_OFFSET_BITS) & ((1 << BC7_INDEX_MODE_BITS) - 1));
+ }
+}}}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_BC7_SingleColor.h b/thirdparty/cvtt/ConvectionKernels_BC7_SingleColor.h
index b5564c0dab..b45ba5eca8 100644
--- a/thirdparty/cvtt/ConvectionKernels_BC7_SingleColor.h
+++ b/thirdparty/cvtt/ConvectionKernels_BC7_SingleColor.h
@@ -1,6 +1,8 @@
#pragma once
#include <stdint.h>
+// This file is generated by the MakeTables app. Do not edit this file manually.
+
namespace cvtt { namespace Tables { namespace BC7SC {
struct TableEntry
diff --git a/thirdparty/cvtt/ConvectionKernels_BCCommon.cpp b/thirdparty/cvtt/ConvectionKernels_BCCommon.cpp
new file mode 100644
index 0000000000..be16d1db06
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BCCommon.cpp
@@ -0,0 +1,46 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_BCCommon.h"
+
+int cvtt::Internal::BCCommon::TweakRoundsForRange(int range)
+{
+ if (range == 3)
+ return 3;
+ return 4;
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_BCCommon.h b/thirdparty/cvtt/ConvectionKernels_BCCommon.h
new file mode 100644
index 0000000000..3e13151acd
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_BCCommon.h
@@ -0,0 +1,104 @@
+#pragma once
+#ifndef __CVTT_BCCOMMON_H__
+#define __CVTT_BCCOMMON_H__
+
+#include "ConvectionKernels_AggregatedError.h"
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ class BCCommon
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::AInt16 MAInt16;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+
+ static int TweakRoundsForRange(int range);
+
+ template<int TVectorSize>
+ static void ComputeErrorLDR(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], int numRealChannels, AggregatedError<TVectorSize> &aggError)
+ {
+ for (int ch = 0; ch < numRealChannels; ch++)
+ aggError.Add(ParallelMath::SqDiffUInt8(reconstructed[ch], original[ch]), ch);
+ }
+
+ template<int TVectorSize>
+ static void ComputeErrorLDR(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], AggregatedError<TVectorSize> &aggError)
+ {
+ ComputeErrorLDR<TVectorSize>(flags, reconstructed, original, TVectorSize, aggError);
+ }
+
+ template<int TVectorSize>
+ static MFloat ComputeErrorLDRSimple(uint32_t flags, const MUInt15 reconstructed[TVectorSize], const MUInt15 original[TVectorSize], int numRealChannels, const float *channelWeightsSq)
+ {
+ AggregatedError<TVectorSize> aggError;
+ ComputeErrorLDR<TVectorSize>(flags, reconstructed, original, numRealChannels, aggError);
+ return aggError.Finalize(flags, channelWeightsSq);
+ }
+
+ template<int TVectorSize>
+ static MFloat ComputeErrorHDRFast(uint32_t flags, const MSInt16 reconstructed[TVectorSize], const MSInt16 original[TVectorSize], const float channelWeightsSq[TVectorSize])
+ {
+ MFloat error = ParallelMath::MakeFloatZero();
+ if (flags & Flags::Uniform)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ error = error + ParallelMath::SqDiffSInt16(reconstructed[ch], original[ch]);
+ }
+ else
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ error = error + ParallelMath::SqDiffSInt16(reconstructed[ch], original[ch]) * ParallelMath::MakeFloat(channelWeightsSq[ch]);
+ }
+
+ return error;
+ }
+
+ template<int TVectorSize>
+ static MFloat ComputeErrorHDRSlow(uint32_t flags, const MSInt16 reconstructed[TVectorSize], const MSInt16 original[TVectorSize], const float channelWeightsSq[TVectorSize])
+ {
+ MFloat error = ParallelMath::MakeFloatZero();
+ if (flags & Flags::Uniform)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ error = error + ParallelMath::SqDiff2CL(reconstructed[ch], original[ch]);
+ }
+ else
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ error = error + ParallelMath::SqDiff2CL(reconstructed[ch], original[ch]) * ParallelMath::MakeFloat(channelWeightsSq[ch]);
+ }
+
+ return error;
+ }
+
+ template<int TChannelCount>
+ static void PreWeightPixelsLDR(MFloat preWeightedPixels[16][TChannelCount], const MUInt15 pixels[16][TChannelCount], const float channelWeights[TChannelCount])
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < TChannelCount; ch++)
+ preWeightedPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]) * channelWeights[ch];
+ }
+ }
+
+ template<int TChannelCount>
+ static void PreWeightPixelsHDR(MFloat preWeightedPixels[16][TChannelCount], const MSInt16 pixels[16][TChannelCount], const float channelWeights[TChannelCount])
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < TChannelCount; ch++)
+ preWeightedPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]) * channelWeights[ch];
+ }
+ }
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_Config.h b/thirdparty/cvtt/ConvectionKernels_Config.h
new file mode 100644
index 0000000000..e79d32b1da
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_Config.h
@@ -0,0 +1,12 @@
+#pragma once
+#ifndef __CVTT_CONFIG_H__
+#define __CVTT_CONFIG_H__
+
+#if (defined(_M_IX86_FP) && _M_IX86_FP >= 2) || defined(_M_X64) || defined(__SSE2__)
+#define CVTT_USE_SSE2
+#endif
+
+// Define this to compile everything as a single source file
+//#define CVTT_SINGLE_FILE
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_ETC.cpp b/thirdparty/cvtt/ConvectionKernels_ETC.cpp
new file mode 100644
index 0000000000..cb202a6e9c
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ETC.cpp
@@ -0,0 +1,3147 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels.h"
+#include "ConvectionKernels_ETC.h"
+#include "ConvectionKernels_ETC1.h"
+#include "ConvectionKernels_ETC2.h"
+#include "ConvectionKernels_ETC2_Rounding.h"
+#include "ConvectionKernels_ParallelMath.h"
+#include "ConvectionKernels_FakeBT709_Rounding.h"
+
+#include <cmath>
+
+const int cvtt::Internal::ETCComputer::g_flipTables[2][2][8] =
+{
+ {
+ { 0, 1, 4, 5, 8, 9, 12, 13 },
+ { 2, 3, 6, 7, 10, 11, 14, 15 }
+ },
+ {
+ { 0, 1, 2, 3, 4, 5, 6, 7 },
+ { 8, 9, 10, 11, 12, 13, 14, 15 }
+ },
+};
+
+cvtt::ParallelMath::Float cvtt::Internal::ETCComputer::ComputeErrorUniform(const MUInt15 pixelA[3], const MUInt15 pixelB[3])
+{
+ MSInt16 d0 = ParallelMath::LosslessCast<MSInt16>::Cast(pixelA[0]) - ParallelMath::LosslessCast<MSInt16>::Cast(pixelB[0]);
+ MFloat fd0 = ParallelMath::ToFloat(d0);
+ MFloat error = fd0 * fd0;
+ for (int ch = 1; ch < 3; ch++)
+ {
+ MSInt16 d = ParallelMath::LosslessCast<MSInt16>::Cast(pixelA[ch]) - ParallelMath::LosslessCast<MSInt16>::Cast(pixelB[ch]);
+ MFloat fd = ParallelMath::ToFloat(d);
+ error = error + fd * fd;
+ }
+ return error;
+}
+
+cvtt::ParallelMath::Float cvtt::Internal::ETCComputer::ComputeErrorWeighted(const MUInt15 reconstructed[3], const MFloat preWeightedPixel[3], const Options options)
+{
+ MFloat dr = ParallelMath::ToFloat(reconstructed[0]) * options.redWeight - preWeightedPixel[0];
+ MFloat dg = ParallelMath::ToFloat(reconstructed[1]) * options.greenWeight - preWeightedPixel[1];
+ MFloat db = ParallelMath::ToFloat(reconstructed[2]) * options.blueWeight - preWeightedPixel[2];
+
+ return dr * dr + dg * dg + db * db;
+}
+
+cvtt::ParallelMath::Float cvtt::Internal::ETCComputer::ComputeErrorFakeBT709(const MUInt15 reconstructed[3], const MFloat preWeightedPixel[3])
+{
+ MFloat yuv[3];
+ ConvertToFakeBT709(yuv, reconstructed);
+
+ MFloat dy = yuv[0] - preWeightedPixel[0];
+ MFloat du = yuv[1] - preWeightedPixel[1];
+ MFloat dv = yuv[2] - preWeightedPixel[2];
+
+ return dy * dy + du * du + dv * dv;
+}
+
+void cvtt::Internal::ETCComputer::TestHalfBlock(MFloat &outError, MUInt16 &outSelectors, MUInt15 quantizedPackedColor, const MUInt15 pixels[8][3], const MFloat preWeightedPixels[8][3], const MSInt16 modifiers[4], bool isDifferential, const Options &options)
+{
+ MUInt15 quantized[3];
+ MUInt15 unquantized[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ quantized[ch] = (ParallelMath::RightShift(quantizedPackedColor, (ch * 5)) & ParallelMath::MakeUInt15(31));
+
+ if (isDifferential)
+ unquantized[ch] = (quantized[ch] << 3) | ParallelMath::RightShift(quantized[ch], 2);
+ else
+ unquantized[ch] = (quantized[ch] << 4) | quantized[ch];
+ }
+
+ MUInt16 selectors = ParallelMath::MakeUInt16(0);
+ MFloat totalError = ParallelMath::MakeFloatZero();
+
+ MUInt15 u15_255 = ParallelMath::MakeUInt15(255);
+ MSInt16 s16_zero = ParallelMath::MakeSInt16(0);
+
+ MUInt15 unquantizedModified[4][3];
+ for (unsigned int s = 0; s < 4; s++)
+ for (int ch = 0; ch < 3; ch++)
+ unquantizedModified[s][ch] = ParallelMath::Min(ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::ToSInt16(unquantized[ch]) + modifiers[s], s16_zero)), u15_255);
+
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ for (int px = 0; px < 8; px++)
+ {
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt16 bestSelector = ParallelMath::MakeUInt16(0);
+
+ for (unsigned int s = 0; s < 4; s++)
+ {
+ MFloat error;
+ if (isFakeBT709)
+ error = ComputeErrorFakeBT709(unquantizedModified[s], preWeightedPixels[px]);
+ else if (isUniform)
+ error = ComputeErrorUniform(pixels[px], unquantizedModified[s]);
+ else
+ error = ComputeErrorWeighted(unquantizedModified[s], preWeightedPixels[px], options);
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
+ bestSelector = ParallelMath::Select(ParallelMath::FloatFlagToInt16(errorBetter), ParallelMath::MakeUInt16(s), bestSelector);
+ bestError = ParallelMath::Min(error, bestError);
+ }
+
+ totalError = totalError + bestError;
+ selectors = selectors | (bestSelector << (px * 2));
+ }
+
+ outError = totalError;
+ outSelectors = selectors;
+}
+
+void cvtt::Internal::ETCComputer::TestHalfBlockPunchthrough(MFloat &outError, MUInt16 &outSelectors, MUInt15 quantizedPackedColor, const MUInt15 pixels[8][3], const MFloat preWeightedPixels[8][3], const ParallelMath::Int16CompFlag isTransparent[8], const MUInt15 modifier, const Options &options)
+{
+ MUInt15 quantized[3];
+ MUInt15 unquantized[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ quantized[ch] = (ParallelMath::RightShift(quantizedPackedColor, (ch * 5)) & ParallelMath::MakeUInt15(31));
+ unquantized[ch] = (quantized[ch] << 3) | ParallelMath::RightShift(quantized[ch], 2);
+ }
+
+ MUInt16 selectors = ParallelMath::MakeUInt16(0);
+ MFloat totalError = ParallelMath::MakeFloatZero();
+
+ MUInt15 u15_255 = ParallelMath::MakeUInt15(255);
+ MSInt16 s16_zero = ParallelMath::MakeSInt16(0);
+
+ MUInt15 unquantizedModified[3][3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ unquantizedModified[0][ch] = ParallelMath::Max(unquantized[ch], modifier) - modifier;
+ unquantizedModified[1][ch] = unquantized[ch];
+ unquantizedModified[2][ch] = ParallelMath::Min(unquantized[ch] + modifier, u15_255);
+ }
+
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ for (int px = 0; px < 8; px++)
+ {
+ ParallelMath::FloatCompFlag isTransparentFloat = ParallelMath::Int16FlagToFloat(isTransparent[px]);
+
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt15 bestSelector = ParallelMath::MakeUInt15(0);
+
+ for (unsigned int s = 0; s < 3; s++)
+ {
+ MFloat error;
+ if (isFakeBT709)
+ error = ComputeErrorFakeBT709(unquantizedModified[s], preWeightedPixels[px]);
+ else if (isUniform)
+ error = ComputeErrorUniform(pixels[px], unquantizedModified[s]);
+ else
+ error = ComputeErrorWeighted(unquantizedModified[s], preWeightedPixels[px], options);
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
+ bestSelector = ParallelMath::Select(ParallelMath::FloatFlagToInt16(errorBetter), ParallelMath::MakeUInt15(s), bestSelector);
+ bestError = ParallelMath::Min(error, bestError);
+ }
+
+ // Annoying quirk: The ETC encoding machinery assumes that selectors are in the table order in the spec, which isn't
+ // the same as their encoding bits, so the transparent index is actually 1 and the valid indexes are 0, 2, and 3.
+
+ // Remap selector 1 to 2, and 2 to 3
+ bestSelector = ParallelMath::Min(ParallelMath::MakeUInt15(3), bestSelector << 1);
+
+ // Mark zero transparent as
+ ParallelMath::ConditionalSet(bestError, isTransparentFloat, ParallelMath::MakeFloatZero());
+ ParallelMath::ConditionalSet(bestSelector, isTransparent[px], ParallelMath::MakeUInt15(1));
+
+ totalError = totalError + bestError;
+ selectors = selectors | (ParallelMath::LosslessCast<MUInt16>::Cast(bestSelector) << (px * 2));
+ }
+
+ outError = totalError;
+ outSelectors = selectors;
+}
+
+void cvtt::Internal::ETCComputer::FindBestDifferentialCombination(int flip, int d, const ParallelMath::Int16CompFlag canIgnoreSector[2], ParallelMath::Int16CompFlag& bestIsThisMode, MFloat& bestTotalError, MUInt15& bestFlip, MUInt15& bestD, MUInt15 bestColors[2], MUInt16 bestSelectors[2], MUInt15 bestTables[2], DifferentialResolveStorage &drs)
+{
+ // We do this part scalar because most of the cost benefit of parallelization is in error evaluation,
+ // and this code has a LOT of early-outs and disjointed index lookups that vary heavily between blocks
+ // and save a lot of time.
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ bool canIgnore[2] = { ParallelMath::Extract(canIgnoreSector[0], block), ParallelMath::Extract(canIgnoreSector[1], block) };
+ bool canIgnoreEither = canIgnore[0] || canIgnore[1];
+ float blockBestTotalError = ParallelMath::Extract(bestTotalError, block);
+ float bestDiffErrors[2] = { FLT_MAX, FLT_MAX };
+ uint16_t bestDiffSelectors[2] = { 0, 0 };
+ uint16_t bestDiffColors[2] = { 0, 0 };
+ uint16_t bestDiffTables[2] = { 0, 0 };
+ for (int sector = 0; sector < 2; sector++)
+ {
+ unsigned int sectorNumAttempts = ParallelMath::Extract(drs.diffNumAttempts[sector], block);
+ for (unsigned int i = 0; i < sectorNumAttempts; i++)
+ {
+ float error = ParallelMath::Extract(drs.diffErrors[sector][i], block);
+ if (error < bestDiffErrors[sector])
+ {
+ bestDiffErrors[sector] = error;
+ bestDiffSelectors[sector] = ParallelMath::Extract(drs.diffSelectors[sector][i], block);
+ bestDiffColors[sector] = ParallelMath::Extract(drs.diffColors[sector][i], block);
+ bestDiffTables[sector] = ParallelMath::Extract(drs.diffTables[sector][i], block);
+ }
+ }
+ }
+
+ if (canIgnore[0])
+ bestDiffColors[0] = bestDiffColors[1];
+ else if (canIgnore[1])
+ bestDiffColors[1] = bestDiffColors[0];
+
+ // The best differential possibilities must be better than the best total error
+ if (bestDiffErrors[0] + bestDiffErrors[1] < blockBestTotalError)
+ {
+ // Fast path if the best possible case is legal
+ if (canIgnoreEither || ETCDifferentialIsLegalScalar(bestDiffColors[0], bestDiffColors[1]))
+ {
+ ParallelMath::PutBoolInt16(bestIsThisMode, block, true);
+ ParallelMath::PutFloat(bestTotalError, block, bestDiffErrors[0] + bestDiffErrors[1]);
+ ParallelMath::PutUInt15(bestFlip, block, flip);
+ ParallelMath::PutUInt15(bestD, block, d);
+ for (int sector = 0; sector < 2; sector++)
+ {
+ ParallelMath::PutUInt15(bestColors[sector], block, bestDiffColors[sector]);
+ ParallelMath::PutUInt16(bestSelectors[sector], block, bestDiffSelectors[sector]);
+ ParallelMath::PutUInt15(bestTables[sector], block, bestDiffTables[sector]);
+ }
+ }
+ else
+ {
+ // Slow path: Sort the possible cases by quality, and search valid combinations
+ // TODO: Pre-flatten the error lists so this is nicer to cache
+ unsigned int numSortIndexes[2] = { 0, 0 };
+ for (int sector = 0; sector < 2; sector++)
+ {
+ unsigned int sectorNumAttempts = ParallelMath::Extract(drs.diffNumAttempts[sector], block);
+
+ for (unsigned int i = 0; i < sectorNumAttempts; i++)
+ {
+ if (ParallelMath::Extract(drs.diffErrors[sector][i], block) < blockBestTotalError)
+ drs.attemptSortIndexes[sector][numSortIndexes[sector]++] = i;
+ }
+
+ struct SortPredicate
+ {
+ const MFloat *diffErrors;
+ int block;
+
+ bool operator()(uint16_t a, uint16_t b) const
+ {
+ float errorA = ParallelMath::Extract(diffErrors[a], block);
+ float errorB = ParallelMath::Extract(diffErrors[b], block);
+
+ if (errorA < errorB)
+ return true;
+ if (errorA > errorB)
+ return false;
+
+ return a < b;
+ }
+ };
+
+ SortPredicate sp;
+ sp.diffErrors = drs.diffErrors[sector];
+ sp.block = block;
+
+ std::sort<uint16_t*, const SortPredicate&>(drs.attemptSortIndexes[sector], drs.attemptSortIndexes[sector] + numSortIndexes[sector], sp);
+ }
+
+ int scannedElements = 0;
+ for (unsigned int i = 0; i < numSortIndexes[0]; i++)
+ {
+ unsigned int attemptIndex0 = drs.attemptSortIndexes[0][i];
+ float error0 = ParallelMath::Extract(drs.diffErrors[0][attemptIndex0], block);
+
+ scannedElements++;
+
+ if (error0 >= blockBestTotalError)
+ break;
+
+ float maxError1 = ParallelMath::Extract(bestTotalError, block) - error0;
+ uint16_t diffColor0 = ParallelMath::Extract(drs.diffColors[0][attemptIndex0], block);
+
+ if (maxError1 < bestDiffErrors[1])
+ break;
+
+ for (unsigned int j = 0; j < numSortIndexes[1]; j++)
+ {
+ unsigned int attemptIndex1 = drs.attemptSortIndexes[1][j];
+ float error1 = ParallelMath::Extract(drs.diffErrors[1][attemptIndex1], block);
+
+ scannedElements++;
+
+ if (error1 >= maxError1)
+ break;
+
+ uint16_t diffColor1 = ParallelMath::Extract(drs.diffColors[1][attemptIndex1], block);
+
+ if (ETCDifferentialIsLegalScalar(diffColor0, diffColor1))
+ {
+ blockBestTotalError = error0 + error1;
+
+ ParallelMath::PutBoolInt16(bestIsThisMode, block, true);
+ ParallelMath::PutFloat(bestTotalError, block, blockBestTotalError);
+ ParallelMath::PutUInt15(bestFlip, block, flip);
+ ParallelMath::PutUInt15(bestD, block, d);
+ ParallelMath::PutUInt15(bestColors[0], block, diffColor0);
+ ParallelMath::PutUInt15(bestColors[1], block, diffColor1);
+ ParallelMath::PutUInt16(bestSelectors[0], block, ParallelMath::Extract(drs.diffSelectors[0][attemptIndex0], block));
+ ParallelMath::PutUInt16(bestSelectors[1], block, ParallelMath::Extract(drs.diffSelectors[1][attemptIndex1], block));
+ ParallelMath::PutUInt15(bestTables[0], block, ParallelMath::Extract(drs.diffTables[0][attemptIndex0], block));
+ ParallelMath::PutUInt15(bestTables[1], block, ParallelMath::Extract(drs.diffTables[1][attemptIndex1], block));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+cvtt::ParallelMath::Int16CompFlag cvtt::Internal::ETCComputer::ETCDifferentialIsLegalForChannel(const MUInt15 &a, const MUInt15 &b)
+{
+ MSInt16 diff = ParallelMath::LosslessCast<MSInt16>::Cast(b) - ParallelMath::LosslessCast<MSInt16>::Cast(a);
+
+ return ParallelMath::Less(ParallelMath::MakeSInt16(-5), diff) & ParallelMath::Less(diff, ParallelMath::MakeSInt16(4));
+}
+
+cvtt::ParallelMath::Int16CompFlag cvtt::Internal::ETCComputer::ETCDifferentialIsLegal(const MUInt15 &a, const MUInt15 &b)
+{
+ MUInt15 mask = ParallelMath::MakeUInt15(31);
+
+ return ETCDifferentialIsLegalForChannel(ParallelMath::RightShift(a, 10), ParallelMath::RightShift(b, 10))
+ & ETCDifferentialIsLegalForChannel(ParallelMath::RightShift(a, 5) & mask, ParallelMath::RightShift(b, 5) & mask)
+ & ETCDifferentialIsLegalForChannel(a & mask, b & mask);
+}
+
+bool cvtt::Internal::ETCComputer::ETCDifferentialIsLegalForChannelScalar(const uint16_t &a, const uint16_t &b)
+{
+ int16_t diff = static_cast<int16_t>(b) - static_cast<int16_t>(a);
+
+ return (-4 <= diff) && (diff <= 3);
+}
+
+bool cvtt::Internal::ETCComputer::ETCDifferentialIsLegalScalar(const uint16_t &a, const uint16_t &b)
+{
+ MUInt15 mask = ParallelMath::MakeUInt15(31);
+
+ return ETCDifferentialIsLegalForChannelScalar((a >> 10), (b >> 10))
+ & ETCDifferentialIsLegalForChannelScalar((a >> 5) & 31, (b >> 5) & 31)
+ & ETCDifferentialIsLegalForChannelScalar(a & 31, b & 31);
+}
+
+void cvtt::Internal::ETCComputer::EncodeTMode(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag isIsolated[16], const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const Options &options)
+{
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ ParallelMath::Int16CompFlag bestIsThisMode = ParallelMath::MakeBoolInt16(false);
+
+ MUInt15 isolatedTotal[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
+ MUInt15 lineTotal[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
+
+ MUInt15 numPixelsIsolated = ParallelMath::MakeUInt15(0);
+
+ // To speed this up, we compute line total as the sum, then subtract out isolated
+ for (unsigned int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ isolatedTotal[ch] = isolatedTotal[ch] + ParallelMath::SelectOrZero(isIsolated[px], pixels[px][ch]);
+ lineTotal[ch] = lineTotal[ch] + pixels[px][ch];
+ }
+ numPixelsIsolated = numPixelsIsolated + ParallelMath::SelectOrZero(isIsolated[px], ParallelMath::MakeUInt15(1));
+ }
+
+ for (int ch = 0; ch < 3; ch++)
+ lineTotal[ch] = lineTotal[ch] - isolatedTotal[ch];
+
+ MUInt15 numPixelsLine = ParallelMath::MakeUInt15(16) - numPixelsIsolated;
+
+ MUInt15 isolatedAverageQuantized[3];
+ MUInt15 isolatedAverageTargets[3];
+ {
+ int divisors[ParallelMath::ParallelSize];
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ divisors[block] = ParallelMath::Extract(numPixelsIsolated, block) * 34;
+
+ MUInt15 addend = (numPixelsIsolated << 4) | numPixelsIsolated;
+ for (int ch = 0; ch < 3; ch++)
+ {
+ // isolatedAverageQuantized[ch] = (isolatedTotal[ch] * 2 + numPixelsIsolated * 17) / (numPixelsIsolated * 34);
+
+ MUInt15 numerator = isolatedTotal[ch] + isolatedTotal[ch];
+ if (!isFakeBT709)
+ numerator = numerator + addend;
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = divisors[block];
+ if (divisor == 0)
+ ParallelMath::PutUInt15(isolatedAverageQuantized[ch], block, 0);
+ else
+ ParallelMath::PutUInt15(isolatedAverageQuantized[ch], block, ParallelMath::Extract(numerator, block) / divisor);
+ }
+
+ isolatedAverageTargets[ch] = numerator;
+ }
+ }
+
+ if (isFakeBT709)
+ ResolveTHFakeBT709Rounding(isolatedAverageQuantized, isolatedAverageTargets, numPixelsIsolated);
+
+ MUInt15 isolatedColor[3];
+ for (int ch = 0; ch < 3; ch++)
+ isolatedColor[ch] = (isolatedAverageQuantized[ch]) | (isolatedAverageQuantized[ch] << 4);
+
+ MFloat isolatedError[16];
+ for (int px = 0; px < 16; px++)
+ {
+ if (isFakeBT709)
+ isolatedError[px] = ComputeErrorFakeBT709(isolatedColor, preWeightedPixels[px]);
+ else if (isUniform)
+ isolatedError[px] = ComputeErrorUniform(pixels[px], isolatedColor);
+ else
+ isolatedError[px] = ComputeErrorWeighted(isolatedColor, preWeightedPixels[px], options);
+ }
+
+ MSInt32 bestSelectors = ParallelMath::MakeSInt32(0);
+ MUInt15 bestTable = ParallelMath::MakeUInt15(0);
+ MUInt15 bestLineColor = ParallelMath::MakeUInt15(0);
+
+ MSInt16 maxLine = ParallelMath::LosslessCast<MSInt16>::Cast(numPixelsLine);
+ MSInt16 minLine = ParallelMath::MakeSInt16(0) - maxLine;
+
+ int16_t clusterMaxLine = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int16_t blockMaxLine = ParallelMath::Extract(maxLine, block);
+ if (blockMaxLine > clusterMaxLine)
+ clusterMaxLine = blockMaxLine;
+ }
+
+ int16_t clusterMinLine = -clusterMaxLine;
+
+ int lineDivisors[ParallelMath::ParallelSize];
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ lineDivisors[block] = ParallelMath::Extract(numPixelsLine, block) * 34;
+
+ MUInt15 lineAddend = (numPixelsLine << 4) | numPixelsLine;
+
+ for (int table = 0; table < 8; table++)
+ {
+ int numUniqueColors[ParallelMath::ParallelSize];
+ MUInt15 uniqueQuantizedColors[31];
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ numUniqueColors[block] = 0;
+
+ MUInt15 modifier = ParallelMath::MakeUInt15(cvtt::Tables::ETC2::g_thModifierTable[table]);
+ MUInt15 modifierOffset = (modifier + modifier);
+
+ for (int16_t offsetPremultiplier = clusterMinLine; offsetPremultiplier <= clusterMaxLine; offsetPremultiplier++)
+ {
+ MSInt16 clampedOffsetPremultiplier = ParallelMath::Max(minLine, ParallelMath::Min(maxLine, ParallelMath::MakeSInt16(offsetPremultiplier)));
+ MSInt16 modifierAddend = ParallelMath::CompactMultiply(clampedOffsetPremultiplier, modifierOffset);
+
+ MUInt15 quantized[3];
+ if (isFakeBT709)
+ {
+ MUInt15 targets[3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ //quantized[ch] = std::min<int16_t>(15, std::max(0, (lineTotal[ch] * 2 + modifierOffset * offsetPremultiplier)) / (numDAIILine * 34));
+ MUInt15 numerator = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(lineTotal[ch] + lineTotal[ch]) + modifierAddend));
+ MUInt15 divided = ParallelMath::MakeUInt15(0);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = lineDivisors[block];
+ if (divisor == 0)
+ ParallelMath::PutUInt15(divided, block, 0);
+ else
+ ParallelMath::PutUInt15(divided, block, ParallelMath::Extract(numerator, block) / divisor);
+ }
+ quantized[ch] = ParallelMath::Min(ParallelMath::MakeUInt15(15), divided);
+ targets[ch] = numerator;
+ }
+
+ ResolveTHFakeBT709Rounding(quantized, targets, numPixelsLine);
+ }
+ else
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ //quantized[ch] = std::min<int16_t>(15, std::max(0, (lineTotal[ch] * 2 + numDAIILine * 17 + modifierOffset * offsetPremultiplier)) / (numDAIILine * 34));
+ MUInt15 numerator = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(lineTotal[ch] + lineTotal[ch] + lineAddend) + modifierAddend));
+ MUInt15 divided = ParallelMath::MakeUInt15(0);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = lineDivisors[block];
+ if (divisor == 0)
+ ParallelMath::PutUInt15(divided, block, 0);
+ else
+ ParallelMath::PutUInt15(divided, block, ParallelMath::Extract(numerator, block) / divisor);
+ }
+ quantized[ch] = ParallelMath::Min(ParallelMath::MakeUInt15(15), divided);
+ }
+ }
+
+ MUInt15 packedColor = quantized[0] | (quantized[1] << 5) | (quantized[2] << 10);
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t blockPackedColor = ParallelMath::Extract(packedColor, block);
+ if (numUniqueColors[block] == 0 || blockPackedColor != ParallelMath::Extract(uniqueQuantizedColors[numUniqueColors[block] - 1], block))
+ ParallelMath::PutUInt15(uniqueQuantizedColors[numUniqueColors[block]++], block, blockPackedColor);
+ }
+ }
+
+ // Stripe unfilled unique colors
+ int maxUniqueColors = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (numUniqueColors[block] > maxUniqueColors)
+ maxUniqueColors = numUniqueColors[block];
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t fillColor = ParallelMath::Extract(uniqueQuantizedColors[0], block);
+
+ int numUnique = numUniqueColors[block];
+ for (int fill = numUnique + 1; fill < maxUniqueColors; fill++)
+ ParallelMath::PutUInt15(uniqueQuantizedColors[fill], block, fillColor);
+ }
+
+ for (int ci = 0; ci < maxUniqueColors; ci++)
+ {
+ MUInt15 lineColors[3][3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 quantizedChannel = (ParallelMath::RightShift(uniqueQuantizedColors[ci], (ch * 5)) & ParallelMath::MakeUInt15(15));
+
+ MUInt15 unquantizedColor = (quantizedChannel << 4) | quantizedChannel;
+ lineColors[0][ch] = ParallelMath::Min(ParallelMath::MakeUInt15(255), unquantizedColor + modifier);
+ lineColors[1][ch] = unquantizedColor;
+ lineColors[2][ch] = ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(unquantizedColor) - ParallelMath::LosslessCast<MSInt16>::Cast(modifier)));
+ }
+
+ MSInt32 selectors = ParallelMath::MakeSInt32(0);
+ MFloat error = ParallelMath::MakeFloatZero();
+ for (int px = 0; px < 16; px++)
+ {
+ MFloat pixelError = isolatedError[px];
+
+ MUInt15 pixelBestSelector = ParallelMath::MakeUInt15(0);
+ for (int i = 0; i < 3; i++)
+ {
+ MFloat error = isUniform ? ComputeErrorUniform(lineColors[i], pixels[px]) : ComputeErrorWeighted(lineColors[i], preWeightedPixels[px], options);
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, pixelError);
+ pixelError = ParallelMath::Min(error, pixelError);
+ pixelBestSelector = ParallelMath::Select(ParallelMath::FloatFlagToInt16(errorBetter), ParallelMath::MakeUInt15(i + 1), pixelBestSelector);
+ }
+
+ error = error + pixelError;
+ selectors = selectors | (ParallelMath::ToInt32(pixelBestSelector) << (px * 2));
+ }
+
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, bestError));
+ bestError = ParallelMath::Min(error, bestError);
+
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ ParallelMath::ConditionalSet(bestLineColor, errorBetter, uniqueQuantizedColors[ci]);
+ ParallelMath::ConditionalSet(bestSelectors, errorBetter, selectors);
+ ParallelMath::ConditionalSet(bestTable, errorBetter, ParallelMath::MakeUInt15(table));
+ bestIsThisMode = bestIsThisMode | errorBetter;
+ }
+ }
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (ParallelMath::Extract(bestIsThisMode, block))
+ {
+ uint32_t lowBits = 0;
+ uint32_t highBits = 0;
+
+ uint16_t blockBestLineColor = ParallelMath::Extract(bestLineColor, block);
+ ParallelMath::ScalarUInt16 blockIsolatedAverageQuantized[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ blockIsolatedAverageQuantized[ch] = ParallelMath::Extract(isolatedAverageQuantized[ch], block);
+
+ uint16_t blockBestTable = ParallelMath::Extract(bestTable, block);
+ int32_t blockBestSelectors = ParallelMath::Extract(bestSelectors, block);
+
+ ParallelMath::ScalarUInt16 lineColor[3];
+ for (int ch = 0; ch < 3; ch++)
+ lineColor[ch] = (blockBestLineColor >> (ch * 5)) & 15;
+
+ EmitTModeBlock(outputBuffer + block * 8, lineColor, blockIsolatedAverageQuantized, blockBestSelectors, blockBestTable, true);
+ }
+ }
+}
+
+void cvtt::Internal::ETCComputer::EncodeHMode(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag groupings[16], const MUInt15 pixels[16][3], HModeEval &he, const MFloat preWeightedPixels[16][3], const Options &options)
+{
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ MUInt15 zero15 = ParallelMath::MakeUInt15(0);
+
+ MUInt15 counts[2] = { zero15, zero15 };
+
+ ParallelMath::Int16CompFlag bestIsThisMode = ParallelMath::MakeBoolInt16(false);
+
+ MUInt15 totals[2][3] =
+ {
+ { zero15, zero15, zero15 },
+ { zero15, zero15, zero15 }
+ };
+
+ for (unsigned int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ totals[0][ch] = totals[0][ch] + pixels[px][ch];
+ totals[1][ch] = totals[1][ch] + ParallelMath::SelectOrZero(groupings[px], pixels[px][ch]);
+ }
+ counts[1] = counts[1] + ParallelMath::SelectOrZero(groupings[px], ParallelMath::MakeUInt15(1));
+ }
+
+ for (int ch = 0; ch < 3; ch++)
+ totals[0][ch] = totals[0][ch] - totals[1][ch];
+ counts[0] = ParallelMath::MakeUInt15(16) - counts[1];
+
+ MUInt16 bestSectorBits = ParallelMath::MakeUInt16(0);
+ MUInt16 bestSignBits = ParallelMath::MakeUInt16(0);
+ MUInt15 bestColors[2] = { zero15, zero15 };
+ MUInt15 bestTable = ParallelMath::MakeUInt15(0);
+
+ for (int table = 0; table < 8; table++)
+ {
+ MUInt15 numUniqueColors = zero15;
+
+ int modifier = cvtt::Tables::ETC1::g_thModifierTable[table];
+
+ for (int sector = 0; sector < 2; sector++)
+ {
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int blockNumUniqueColors = 0;
+ uint16_t blockUniqueQuantizedColors[31];
+
+ int maxOffsetMultiplier = ParallelMath::Extract(counts[sector], block);
+ int minOffsetMultiplier = -maxOffsetMultiplier;
+
+ int modifierOffset = modifier * 2;
+
+ int blockSectorCounts = ParallelMath::Extract(counts[sector], block);
+ int blockSectorTotals[3];
+ for (int ch = 0; ch < 3; ch++)
+ blockSectorTotals[ch] = ParallelMath::Extract(totals[sector][ch], block);
+
+ for (int offsetPremultiplier = minOffsetMultiplier; offsetPremultiplier <= maxOffsetMultiplier; offsetPremultiplier++)
+ {
+ // TODO: This isn't ideal for FakeBT709
+ int16_t quantized[3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ if (blockSectorCounts == 0)
+ quantized[ch] = 0;
+ else
+ quantized[ch] = std::min<int16_t>(15, std::max<int16_t>(0, (blockSectorTotals[ch] * 2 + blockSectorCounts * 17 + modifierOffset * offsetPremultiplier)) / (blockSectorCounts * 34));
+ }
+
+ uint16_t packedColor = (quantized[0] << 10) | (quantized[1] << 5) | quantized[2];
+ if (blockNumUniqueColors == 0 || packedColor != blockUniqueQuantizedColors[blockNumUniqueColors - 1])
+ {
+ assert(blockNumUniqueColors < 32);
+ blockUniqueQuantizedColors[blockNumUniqueColors++] = packedColor;
+ }
+ }
+
+ ParallelMath::PutUInt15(he.numUniqueColors[sector], block, blockNumUniqueColors);
+
+ int baseIndex = 0;
+ if (sector == 1)
+ baseIndex = ParallelMath::Extract(he.numUniqueColors[0], block);
+
+ for (int i = 0; i < blockNumUniqueColors; i++)
+ ParallelMath::PutUInt15(he.uniqueQuantizedColors[baseIndex + i], block, blockUniqueQuantizedColors[i]);
+ }
+ }
+
+ MUInt15 totalColors = he.numUniqueColors[0] + he.numUniqueColors[1];
+ int maxErrorColors = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ maxErrorColors = std::max<int>(maxErrorColors, ParallelMath::Extract(totalColors, block));
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int lastColor = ParallelMath::Extract(totalColors, block);
+ uint16_t stripeColor = ParallelMath::Extract(he.uniqueQuantizedColors[0], block);
+ for (int i = lastColor; i < maxErrorColors; i++)
+ ParallelMath::PutUInt15(he.uniqueQuantizedColors[i], block, stripeColor);
+ }
+
+ for (int ci = 0; ci < maxErrorColors; ci++)
+ {
+ MUInt15 fifteen = ParallelMath::MakeUInt15(15);
+ MUInt15 twoFiftyFive = ParallelMath::MakeUInt15(255);
+ MSInt16 zeroS16 = ParallelMath::MakeSInt16(0);
+
+ MUInt15 colors[2][3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 quantizedChannel = ParallelMath::RightShift(he.uniqueQuantizedColors[ci], ((2 - ch) * 5)) & fifteen;
+
+ MUInt15 unquantizedColor = (quantizedChannel << 4) | quantizedChannel;
+ colors[0][ch] = ParallelMath::Min(twoFiftyFive, unquantizedColor + modifier);
+ colors[1][ch] = ParallelMath::ToUInt15(ParallelMath::Max(zeroS16, ParallelMath::LosslessCast<MSInt16>::Cast(unquantizedColor) - ParallelMath::MakeSInt16(modifier)));
+ }
+
+ MUInt16 signBits = ParallelMath::MakeUInt16(0);
+ for (int px = 0; px < 16; px++)
+ {
+ MFloat errors[2];
+ for (int i = 0; i < 2; i++)
+ {
+ if (isFakeBT709)
+ errors[i] = ComputeErrorFakeBT709(colors[i], preWeightedPixels[px]);
+ else if (isUniform)
+ errors[i] = ComputeErrorUniform(colors[i], pixels[px]);
+ else
+ errors[i] = ComputeErrorWeighted(colors[i], preWeightedPixels[px], options);
+ }
+
+ ParallelMath::Int16CompFlag errorOneLess = ParallelMath::FloatFlagToInt16(ParallelMath::Less(errors[1], errors[0]));
+ he.errors[ci][px] = ParallelMath::Min(errors[0], errors[1]);
+ signBits = signBits | ParallelMath::SelectOrZero(errorOneLess, ParallelMath::MakeUInt16(1 << px));
+ }
+ he.signBits[ci] = signBits;
+ }
+
+ int maxUniqueColorCombos = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int numUniqueColorCombos = ParallelMath::Extract(he.numUniqueColors[0], block) * ParallelMath::Extract(he.numUniqueColors[1], block);
+ if (numUniqueColorCombos > maxUniqueColorCombos)
+ maxUniqueColorCombos = numUniqueColorCombos;
+ }
+
+ MUInt15 indexes[2] = { zero15, zero15 };
+ MUInt15 maxIndex[2] = { he.numUniqueColors[0] - ParallelMath::MakeUInt15(1), he.numUniqueColors[1] - ParallelMath::MakeUInt15(1) };
+
+ int block1Starts[ParallelMath::ParallelSize];
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ block1Starts[block] = ParallelMath::Extract(he.numUniqueColors[0], block);
+
+ for (int combo = 0; combo < maxUniqueColorCombos; combo++)
+ {
+ MUInt15 index0 = indexes[0] + ParallelMath::MakeUInt15(1);
+ ParallelMath::Int16CompFlag index0Overflow = ParallelMath::Less(maxIndex[0], index0);
+ ParallelMath::ConditionalSet(index0, index0Overflow, ParallelMath::MakeUInt15(0));
+
+ MUInt15 index1 = ParallelMath::Min(maxIndex[1], indexes[1] + ParallelMath::SelectOrZero(index0Overflow, ParallelMath::MakeUInt15(1)));
+ indexes[0] = index0;
+ indexes[1] = index1;
+
+ int ci0[ParallelMath::ParallelSize];
+ int ci1[ParallelMath::ParallelSize];
+ MUInt15 color0;
+ MUInt15 color1;
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ ci0[block] = ParallelMath::Extract(index0, block);
+ ci1[block] = ParallelMath::Extract(index1, block) + block1Starts[block];
+ ParallelMath::PutUInt15(color0, block, ParallelMath::Extract(he.uniqueQuantizedColors[ci0[block]], block));
+ ParallelMath::PutUInt15(color1, block, ParallelMath::Extract(he.uniqueQuantizedColors[ci1[block]], block));
+ }
+
+ MFloat totalError = ParallelMath::MakeFloatZero();
+ MUInt16 sectorBits = ParallelMath::MakeUInt16(0);
+ MUInt16 signBits = ParallelMath::MakeUInt16(0);
+ for (int px = 0; px < 16; px++)
+ {
+ MFloat errorCI0;
+ MFloat errorCI1;
+ MUInt16 signBits0;
+ MUInt16 signBits1;
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ ParallelMath::PutFloat(errorCI0, block, ParallelMath::Extract(he.errors[ci0[block]][px], block));
+ ParallelMath::PutFloat(errorCI1, block, ParallelMath::Extract(he.errors[ci1[block]][px], block));
+ ParallelMath::PutUInt16(signBits0, block, ParallelMath::Extract(he.signBits[ci0[block]], block));
+ ParallelMath::PutUInt16(signBits1, block, ParallelMath::Extract(he.signBits[ci1[block]], block));
+ }
+
+ totalError = totalError + ParallelMath::Min(errorCI0, errorCI1);
+
+ MUInt16 bitPosition = ParallelMath::MakeUInt16(1 << px);
+
+ ParallelMath::Int16CompFlag error1Better = ParallelMath::FloatFlagToInt16(ParallelMath::Less(errorCI1, errorCI0));
+
+ sectorBits = sectorBits | ParallelMath::SelectOrZero(error1Better, bitPosition);
+ signBits = signBits | (bitPosition & ParallelMath::Select(error1Better, signBits1, signBits0));
+ }
+
+ ParallelMath::FloatCompFlag totalErrorBetter = ParallelMath::Less(totalError, bestError);
+ ParallelMath::Int16CompFlag totalErrorBetter16 = ParallelMath::FloatFlagToInt16(totalErrorBetter);
+ if (ParallelMath::AnySet(totalErrorBetter16))
+ {
+ bestIsThisMode = bestIsThisMode | totalErrorBetter16;
+ ParallelMath::ConditionalSet(bestTable, totalErrorBetter16, ParallelMath::MakeUInt15(table));
+ ParallelMath::ConditionalSet(bestColors[0], totalErrorBetter16, color0);
+ ParallelMath::ConditionalSet(bestColors[1], totalErrorBetter16, color1);
+ ParallelMath::ConditionalSet(bestSectorBits, totalErrorBetter16, sectorBits);
+ ParallelMath::ConditionalSet(bestSignBits, totalErrorBetter16, signBits);
+ bestError = ParallelMath::Min(totalError, bestError);
+ }
+ }
+ }
+
+ if (ParallelMath::AnySet(bestIsThisMode))
+ {
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (!ParallelMath::Extract(bestIsThisMode, block))
+ continue;
+
+ ParallelMath::ScalarUInt16 blockBestColors[2] = { ParallelMath::Extract(bestColors[0], block), ParallelMath::Extract(bestColors[1], block) };
+ ParallelMath::ScalarUInt16 blockBestSectorBits = ParallelMath::Extract(bestSectorBits, block);
+ ParallelMath::ScalarUInt16 blockBestSignBits = ParallelMath::Extract(bestSignBits, block);
+ ParallelMath::ScalarUInt16 blockBestTable = ParallelMath::Extract(bestTable, block);
+
+ EmitHModeBlock(outputBuffer + block * 8, blockBestColors, blockBestSectorBits, blockBestSignBits, blockBestTable, true);
+ }
+ }
+}
+
+void cvtt::Internal::ETCComputer::EncodeVirtualTModePunchthrough(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag isIsolatedBase[16], const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const ParallelMath::Int16CompFlag isTransparent[16], const ParallelMath::Int16CompFlag& anyTransparent, const ParallelMath::Int16CompFlag& allTransparent, const Options &options)
+{
+ // We treat T and H mode as the same mode ("Virtual T mode") with punchthrough, because of how the colors work:
+ //
+ // T mode: C1, C2+M, Transparent, C2-M
+ // H mode: C1+M, C1-M, Transparent, C2-M
+ //
+ // So in either case, we have 2 colors +/- a modifier, and a third unique color, which is basically T mode except without the middle color.
+ // The only thing that matters is whether it's better to store the isolated color as T mode color 1, or store it offset in H mode color 2.
+ //
+ // Sometimes it won't even be possible to store it in H mode color 2 because the table low bit derives from a numeric comparison of the colors,
+ // but unlike opaque blocks, we can't flip them.
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ ParallelMath::FloatCompFlag isTransparentF[16];
+ for (int px = 0; px < 16; px++)
+ isTransparentF[px] = ParallelMath::Int16FlagToFloat(isTransparent[px]);
+
+ ParallelMath::Int16CompFlag bestIsThisMode = ParallelMath::MakeBoolInt16(false);
+ ParallelMath::Int16CompFlag bestIsHMode = ParallelMath::MakeBoolInt16(false);
+
+ MUInt15 isolatedTotal[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
+ MUInt15 lineTotal[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
+
+ MUInt15 numPixelsIsolated = ParallelMath::MakeUInt15(0);
+ MUInt15 numPixelsLine = ParallelMath::MakeUInt15(0);
+
+ ParallelMath::Int16CompFlag isIsolated[16];
+ ParallelMath::Int16CompFlag isLine[16];
+
+ for (unsigned int px = 0; px < 16; px++)
+ {
+ ParallelMath::Int16CompFlag isOpaque = ParallelMath::Not(isTransparent[px]);
+ isIsolated[px] = isIsolatedBase[px] & isOpaque;
+ isLine[px] = ParallelMath::Not(isIsolatedBase[px]) & isOpaque;
+ }
+
+ for (unsigned int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ isolatedTotal[ch] = isolatedTotal[ch] + ParallelMath::SelectOrZero(isIsolated[px], pixels[px][ch]);
+ lineTotal[ch] = lineTotal[ch] + ParallelMath::SelectOrZero(isLine[px], pixels[px][ch]);
+ }
+ numPixelsIsolated = numPixelsIsolated + ParallelMath::SelectOrZero(isIsolated[px], ParallelMath::MakeUInt15(1));
+ numPixelsLine = numPixelsLine + ParallelMath::SelectOrZero(isLine[px], ParallelMath::MakeUInt15(1));
+ }
+
+ MUInt15 isolatedAverageQuantized[3];
+ MUInt15 hModeIsolatedQuantized[8][3];
+ MUInt15 isolatedAverageTargets[3];
+ {
+ int divisors[ParallelMath::ParallelSize];
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ divisors[block] = ParallelMath::Extract(numPixelsIsolated, block) * 34;
+
+ MUInt15 addend = (numPixelsIsolated << 4) | numPixelsIsolated;
+ for (int ch = 0; ch < 3; ch++)
+ {
+ // isolatedAverageQuantized[ch] = (isolatedTotal[ch] * 2 + numPixelsIsolated * 17) / (numPixelsIsolated * 34);
+
+ MUInt15 numerator = isolatedTotal[ch] + isolatedTotal[ch];
+ if (!isFakeBT709)
+ numerator = numerator + addend;
+
+ MUInt15 hModeIsolatedNumerators[8];
+ for (int table = 0; table < 8; table++)
+ {
+ // FIXME: Handle fake BT.709 correctly
+ MUInt15 offsetTotal = isolatedTotal[ch] + ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(ParallelMath::MakeUInt15(cvtt::Tables::ETC2::g_thModifierTable[table]), numPixelsIsolated));
+
+ hModeIsolatedNumerators[table] = (offsetTotal + offsetTotal) + addend;
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = divisors[block];
+ if (divisor == 0)
+ {
+ ParallelMath::PutUInt15(isolatedAverageQuantized[ch], block, 0);
+ for (int table = 0; table < 8; table++)
+ ParallelMath::PutUInt15(hModeIsolatedQuantized[table][ch], block, 0);
+ }
+ else
+ {
+ ParallelMath::PutUInt15(isolatedAverageQuantized[ch], block, ParallelMath::Extract(numerator, block) / divisor);
+ for (int table = 0; table < 8; table++)
+ ParallelMath::PutUInt15(hModeIsolatedQuantized[table][ch], block, ParallelMath::Extract(hModeIsolatedNumerators[table], block) / divisor);
+ }
+ }
+
+ isolatedAverageTargets[ch] = numerator;
+ }
+ }
+
+ if (isFakeBT709)
+ ResolveTHFakeBT709Rounding(isolatedAverageQuantized, isolatedAverageTargets, numPixelsIsolated);
+
+ for (int table = 0; table < 8; table++)
+ for (int ch = 0; ch < 3; ch++)
+ hModeIsolatedQuantized[table][ch] = ParallelMath::Min(ParallelMath::MakeUInt15(15), hModeIsolatedQuantized[table][ch]);
+
+ MUInt15 isolatedColor[3];
+ for (int ch = 0; ch < 3; ch++)
+ isolatedColor[ch] = (isolatedAverageQuantized[ch]) | (isolatedAverageQuantized[ch] << 4);
+
+ MFloat isolatedError[16];
+ for (int px = 0; px < 16; px++)
+ {
+ if (isFakeBT709)
+ isolatedError[px] = ComputeErrorFakeBT709(isolatedColor, preWeightedPixels[px]);
+ else if (isUniform)
+ isolatedError[px] = ComputeErrorUniform(pixels[px], isolatedColor);
+ else
+ isolatedError[px] = ComputeErrorWeighted(isolatedColor, preWeightedPixels[px], options);
+
+ ParallelMath::ConditionalSet(isolatedError[px], isTransparentF[px], ParallelMath::MakeFloatZero());
+ }
+
+ MSInt32 bestSelectors = ParallelMath::MakeSInt32(0);
+ MUInt15 bestTable = ParallelMath::MakeUInt15(0);
+ MUInt15 bestLineColor = ParallelMath::MakeUInt15(0);
+ MUInt15 bestIsolatedColor = ParallelMath::MakeUInt15(0);
+ MUInt15 bestHModeColor2 = ParallelMath::MakeUInt15(0);
+ ParallelMath::Int16CompFlag bestUseHMode = ParallelMath::MakeBoolInt16(false);
+
+ MSInt16 maxLine = ParallelMath::LosslessCast<MSInt16>::Cast(numPixelsLine);
+ MSInt16 minLine = ParallelMath::MakeSInt16(0) - maxLine;
+
+ int16_t clusterMaxLine = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int16_t blockMaxLine = ParallelMath::Extract(maxLine, block);
+ if (blockMaxLine > clusterMaxLine)
+ clusterMaxLine = blockMaxLine;
+ }
+
+ int16_t clusterMinLine = -clusterMaxLine;
+
+ int lineDivisors[ParallelMath::ParallelSize];
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ lineDivisors[block] = ParallelMath::Extract(numPixelsLine, block) * 34;
+
+ MUInt15 lineAddend = (numPixelsLine << 4) | numPixelsLine;
+
+ for (int table = 0; table < 8; table++)
+ {
+ int numUniqueColors[ParallelMath::ParallelSize];
+ MUInt15 uniqueQuantizedColors[31];
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ numUniqueColors[block] = 0;
+
+ MUInt15 modifier = ParallelMath::MakeUInt15(cvtt::Tables::ETC2::g_thModifierTable[table]);
+ MUInt15 modifierOffset = (modifier + modifier);
+
+ for (int16_t offsetPremultiplier = clusterMinLine; offsetPremultiplier <= clusterMaxLine; offsetPremultiplier += 2)
+ {
+ MSInt16 clampedOffsetPremultiplier = ParallelMath::Max(minLine, ParallelMath::Min(maxLine, ParallelMath::MakeSInt16(offsetPremultiplier)));
+ MSInt16 modifierAddend = ParallelMath::CompactMultiply(clampedOffsetPremultiplier, modifierOffset);
+
+ MUInt15 quantized[3];
+ if (isFakeBT709)
+ {
+ MUInt15 targets[3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ //quantized[ch] = std::min<int16_t>(15, std::max(0, (lineTotal[ch] * 2 + modifierOffset * offsetPremultiplier)) / (numDAIILine * 34));
+ MUInt15 numerator = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(lineTotal[ch] + lineTotal[ch]) + modifierAddend));
+ MUInt15 divided = ParallelMath::MakeUInt15(0);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = lineDivisors[block];
+ if (divisor == 0)
+ ParallelMath::PutUInt15(divided, block, 0);
+ else
+ ParallelMath::PutUInt15(divided, block, ParallelMath::Extract(numerator, block) / divisor);
+ }
+ quantized[ch] = ParallelMath::Min(ParallelMath::MakeUInt15(15), divided);
+ targets[ch] = numerator;
+ }
+
+ ResolveTHFakeBT709Rounding(quantized, targets, numPixelsLine);
+ }
+ else
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ //quantized[ch] = std::min<int16_t>(15, std::max(0, (lineTotal[ch] * 2 + numDAIILine * 17 + modifierOffset * offsetPremultiplier)) / (numDAIILine * 34));
+ MUInt15 numerator = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(lineTotal[ch] + lineTotal[ch] + lineAddend) + modifierAddend));
+ MUInt15 divided = ParallelMath::MakeUInt15(0);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int divisor = lineDivisors[block];
+ if (divisor == 0)
+ ParallelMath::PutUInt15(divided, block, 0);
+ else
+ ParallelMath::PutUInt15(divided, block, ParallelMath::Extract(numerator, block) / divisor);
+ }
+ quantized[ch] = ParallelMath::Min(ParallelMath::MakeUInt15(15), divided);
+ }
+ }
+
+ MUInt15 packedColor = (quantized[0] << 10) | (quantized[1] << 5) | quantized[2];
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t blockPackedColor = ParallelMath::Extract(packedColor, block);
+ if (numUniqueColors[block] == 0 || blockPackedColor != ParallelMath::Extract(uniqueQuantizedColors[numUniqueColors[block] - 1], block))
+ ParallelMath::PutUInt15(uniqueQuantizedColors[numUniqueColors[block]++], block, blockPackedColor);
+ }
+ }
+
+ // Stripe unfilled unique colors
+ int maxUniqueColors = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (numUniqueColors[block] > maxUniqueColors)
+ maxUniqueColors = numUniqueColors[block];
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t fillColor = ParallelMath::Extract(uniqueQuantizedColors[0], block);
+
+ int numUnique = numUniqueColors[block];
+ for (int fill = numUnique + 1; fill < maxUniqueColors; fill++)
+ ParallelMath::PutUInt15(uniqueQuantizedColors[fill], block, fillColor);
+ }
+
+ MFloat hModeErrors[16];
+ MUInt15 hModeUnquantizedColor[3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 quantizedChannel = hModeIsolatedQuantized[table][ch];
+
+ MUInt15 unquantizedCh = (quantizedChannel << 4) | quantizedChannel;
+ hModeUnquantizedColor[ch] = ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(unquantizedCh) - ParallelMath::LosslessCast<MSInt16>::Cast(modifier)));
+ }
+
+ for (int px = 0; px < 16; px++)
+ {
+ hModeErrors[px] = isUniform ? ComputeErrorUniform(hModeUnquantizedColor, pixels[px]) : ComputeErrorWeighted(hModeUnquantizedColor, preWeightedPixels[px], options);
+ ParallelMath::ConditionalSet(hModeErrors[px], isTransparentF[px], ParallelMath::MakeFloatZero());
+ }
+
+ MUInt15 packedHModeColor2 = (hModeIsolatedQuantized[table][0] << 10) | (hModeIsolatedQuantized[table][1] << 5) | hModeIsolatedQuantized[table][2];
+ ParallelMath::Int16CompFlag tableLowBitIsZero = ((table & 1) == 0) ? ParallelMath::MakeBoolInt16(true) : ParallelMath::MakeBoolInt16(false);
+
+ for (int ci = 0; ci < maxUniqueColors; ci++)
+ {
+ MUInt15 lineColors[2][3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 quantizedChannel = (ParallelMath::RightShift(uniqueQuantizedColors[ci], 10 - (ch * 5)) & ParallelMath::MakeUInt15(15));
+
+ MUInt15 unquantizedColor = (quantizedChannel << 4) | quantizedChannel;
+ lineColors[0][ch] = ParallelMath::Min(ParallelMath::MakeUInt15(255), unquantizedColor + modifier);
+ lineColors[1][ch] = ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::MakeSInt16(0), ParallelMath::LosslessCast<MSInt16>::Cast(unquantizedColor) - ParallelMath::LosslessCast<MSInt16>::Cast(modifier)));
+ }
+
+ MUInt15 bestLineSelector[16];
+ MFloat bestLineError[16];
+ for (int px = 0; px < 16; px++)
+ {
+ MFloat lineErrors[2];
+ for (int i = 0; i < 2; i++)
+ lineErrors[i] = isUniform ? ComputeErrorUniform(lineColors[i], pixels[px]) : ComputeErrorWeighted(lineColors[i], preWeightedPixels[px], options);
+
+ ParallelMath::Int16CompFlag firstIsBetter = ParallelMath::FloatFlagToInt16(ParallelMath::LessOrEqual(lineErrors[0], lineErrors[1]));
+ bestLineSelector[px] = ParallelMath::Select(firstIsBetter, ParallelMath::MakeUInt15(1), ParallelMath::MakeUInt15(3));
+ bestLineError[px] = ParallelMath::Min(lineErrors[0], lineErrors[1]);
+
+ ParallelMath::ConditionalSet(bestLineError[px], isTransparentF[px], ParallelMath::MakeFloatZero());
+ }
+
+ // One case considered here was if it was possible to force H mode to be valid when the line color is unused.
+ // That case isn't actually useful because it's equivalent to the isolated color being unused at maximum offset,
+ // which is always checked after a swap.
+ MFloat tModeError = ParallelMath::MakeFloatZero();
+ MFloat hModeError = ParallelMath::MakeFloatZero();
+ for (int px = 0; px < 16; px++)
+ {
+ tModeError = tModeError + ParallelMath::Min(bestLineError[px], isolatedError[px]);
+ hModeError = hModeError + ParallelMath::Min(bestLineError[px], hModeErrors[px]);
+ }
+
+ ParallelMath::FloatCompFlag hLessError = ParallelMath::Less(hModeError, tModeError);
+
+ MUInt15 packedHModeColor1 = uniqueQuantizedColors[ci];
+
+ ParallelMath::Int16CompFlag hModeTableLowBitMustBeZero = ParallelMath::Less(packedHModeColor1, packedHModeColor2);
+
+ ParallelMath::Int16CompFlag hModeIsLegal = ParallelMath::Equal(hModeTableLowBitMustBeZero, tableLowBitIsZero);
+ ParallelMath::Int16CompFlag useHMode = ParallelMath::FloatFlagToInt16(hLessError) & hModeIsLegal;
+
+ MFloat roundBestError = tModeError;
+ ParallelMath::ConditionalSet(roundBestError, ParallelMath::Int16FlagToFloat(useHMode), hModeError);
+
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(roundBestError, bestError));
+ ParallelMath::FloatCompFlag useHModeF = ParallelMath::Int16FlagToFloat(useHMode);
+
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ MSInt32 selectors = ParallelMath::MakeSInt32(0);
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 selector = bestLineSelector[px];
+
+ MFloat isolatedPixelError = ParallelMath::Select(useHModeF, hModeErrors[px], isolatedError[px]);
+ ParallelMath::Int16CompFlag isolatedBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(isolatedPixelError, bestLineError[px]));
+
+ ParallelMath::ConditionalSet(selector, isolatedBetter, ParallelMath::MakeUInt15(0));
+ ParallelMath::ConditionalSet(selector, isTransparent[px], ParallelMath::MakeUInt15(2));
+ selectors = selectors | (ParallelMath::ToInt32(selector) << (px * 2));
+ }
+
+ bestError = ParallelMath::Min(bestError, roundBestError);
+ ParallelMath::ConditionalSet(bestLineColor, errorBetter, uniqueQuantizedColors[ci]);
+ ParallelMath::ConditionalSet(bestSelectors, errorBetter, selectors);
+ ParallelMath::ConditionalSet(bestTable, errorBetter, ParallelMath::MakeUInt15(table));
+ ParallelMath::ConditionalSet(bestIsHMode, errorBetter, useHMode);
+ ParallelMath::ConditionalSet(bestHModeColor2, errorBetter, packedHModeColor2);
+
+ bestIsThisMode = bestIsThisMode | errorBetter;
+ }
+ }
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (ParallelMath::Extract(bestIsThisMode, block))
+ {
+ uint32_t lowBits = 0;
+ uint32_t highBits = 0;
+
+ uint16_t blockBestLineColor = ParallelMath::Extract(bestLineColor, block);
+ ParallelMath::ScalarUInt16 blockIsolatedAverageQuantized[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ blockIsolatedAverageQuantized[ch] = ParallelMath::Extract(isolatedAverageQuantized[ch], block);
+
+ uint16_t blockBestTable = ParallelMath::Extract(bestTable, block);
+ int32_t blockBestSelectors = ParallelMath::Extract(bestSelectors, block);
+
+ ParallelMath::ScalarUInt16 lineColor[3];
+ for (int ch = 0; ch < 3; ch++)
+ lineColor[ch] = (blockBestLineColor >> (10 - (ch * 5))) & 15;
+
+ if (ParallelMath::Extract(bestIsHMode, block))
+ {
+ // T mode: C1, C2+M, Transparent, C2-M
+ // H mode: C1+M, C1-M, Transparent, C2-M
+ static const ParallelMath::ScalarUInt16 selectorRemapSector[4] = { 1, 0, 1, 0 };
+ static const ParallelMath::ScalarUInt16 selectorRemapSign[4] = { 1, 0, 0, 1 };
+
+ // Remap selectors
+ ParallelMath::ScalarUInt16 signBits = 0;
+ ParallelMath::ScalarUInt16 sectorBits = 0;
+ int32_t blockBestSelectors = ParallelMath::Extract(bestSelectors, block);
+ for (int px = 0; px < 16; px++)
+ {
+ int32_t selector = (blockBestSelectors >> (px * 2)) & 3;
+ sectorBits |= (selectorRemapSector[selector] << px);
+ signBits |= (selectorRemapSign[selector] << px);
+ }
+
+ ParallelMath::ScalarUInt16 blockColors[2] = { blockBestLineColor, ParallelMath::Extract(bestHModeColor2, block) };
+
+ EmitHModeBlock(outputBuffer + block * 8, blockColors, sectorBits, signBits, blockBestTable, false);
+ }
+ else
+ EmitTModeBlock(outputBuffer + block * 8, lineColor, blockIsolatedAverageQuantized, blockBestSelectors, blockBestTable, false);
+ }
+ }
+}
+
+
+cvtt::ParallelMath::UInt15 cvtt::Internal::ETCComputer::DecodePlanarCoeff(const MUInt15 &coeff, int ch)
+{
+ if (ch == 1)
+ return (coeff << 1) | (ParallelMath::RightShift(coeff, 6));
+ else
+ return (coeff << 2) | (ParallelMath::RightShift(coeff, 4));
+}
+
+void cvtt::Internal::ETCComputer::EncodePlanar(uint8_t *outputBuffer, MFloat &bestError, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const Options &options)
+{
+ // NOTE: If it's desired to do this in another color space, the best way to do it would probably be
+ // to do everything in that color space and then transform it back to RGB.
+
+ // We compute H = (H-O)/4 and V= (V-O)/4 to simplify the math
+
+ // error = (x*H + y*V + O - C)^2
+ MFloat h[3] = { ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero() };
+ MFloat v[3] = { ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero() };
+ MFloat o[3] = { ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero() };
+
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+
+ MFloat totalError = ParallelMath::MakeFloatZero();
+ MUInt15 bestCoeffs[3][3]; // [Channel][Coeff]
+ for (int ch = 0; ch < 3; ch++)
+ {
+ float fhh = 0.f;
+ float fho = 0.f;
+ float fhv = 0.f;
+ float foo = 0.f;
+ float fov = 0.f;
+ float fvv = 0.f;
+ MFloat fc = ParallelMath::MakeFloatZero();
+ MFloat fh = ParallelMath::MakeFloatZero();
+ MFloat fv = ParallelMath::MakeFloatZero();
+ MFloat fo = ParallelMath::MakeFloatZero();
+
+ float &foh = fho;
+ float &fvh = fhv;
+ float &fvo = fov;
+
+ for (int px = 0; px < 16; px++)
+ {
+ float x = static_cast<float>(px % 4);
+ float y = static_cast<float>(px / 4);
+ MFloat c = isFakeBT709 ? preWeightedPixels[px][ch] : ParallelMath::ToFloat(pixels[px][ch]);
+
+ // (x*H + y*V + O - C)^2
+ fhh += x * x;
+ fhv += x * y;
+ fho += x;
+ fh = fh - c * x;
+
+ fvh += y * x;
+ fvv += y * y;
+ fvo += y;
+ fv = fv - c * y;
+
+ foh += x;
+ fov += y;
+ foo += 1;
+ fo = fo - c;
+
+ fh = fh - c * x;
+ fv = fv - c * y;
+ fo = fo - c;
+ fc = fc + c * c;
+ }
+
+ //float totalError = fhh * h * h + fho * h*o + fhv * h*v + foo * o * o + fov * o*v + fvv * v * v + fh * h + fv * v + fo * o + fc;
+
+ // error = fhh*h^2 + fho*h*o + fhv*h*v + foo*o^2 + fov*o*v + fvv*v^2 + fh*h + fv*v + fo*o + fc
+ // derror/dh = 2*fhh*h + fho*o + fhv*v + fh
+ // derror/dv = fhv*h + fov*o + 2*fvv*v + fv
+ // derror/do = fho*h + 2*foo*o + fov*v + fo
+
+ // Solve system of equations
+ // h o v 1 = 0
+ // -------
+ // d e f g R0
+ // i j k l R1
+ // m n p q R2
+
+ float d = 2.0f * fhh;
+ float e = fho;
+ float f = fhv;
+ MFloat gD = fh;
+
+ float i = fhv;
+ float j = fov;
+ float k = 2.0f * fvv;
+ MFloat lD = fv;
+
+ float m = fho;
+ float n = 2.0f * foo;
+ float p = fov;
+ MFloat qD = fo;
+
+ {
+ // Factor out first column from R1 and R2
+ float r0to1 = -i / d;
+ float r0to2 = -m / d;
+
+ // 0 j1 k1 l1D
+ float j1 = j + r0to1 * e;
+ float k1 = k + r0to1 * f;
+ MFloat l1D = lD + gD * r0to1;
+
+ // 0 n1 p1 q1D
+ float n1 = n + r0to2 * e;
+ float p1 = p + r0to2 * f;
+ MFloat q1D = qD + gD * r0to2;
+
+ // Factor out third column from R2
+ float r1to2 = -p1 / k1;
+
+ // 0 n2 0 q2D
+ float n2 = n1 + r1to2 * j1;
+ MFloat q2D = q1D + l1D * r1to2;
+
+ o[ch] = -q2D / n2;
+
+ // Factor out second column from R1
+ // 0 n2 0 q2D
+
+ float r2to1 = -j1 / n2;
+
+ // 0 0 k1 l2D
+ // 0 n2 0 q2D
+ MFloat l2D = l1D + q2D * r2to1;
+
+ float elim2 = -f / k1;
+ float elim1 = -e / n2;
+
+ // d 0 0 g2D
+ MFloat g2D = gD + l2D * elim2 + q2D * elim1;
+
+ // n2*o + q2 = 0
+ // o = -q2 / n2
+ h[ch] = -g2D / d;
+ v[ch] = -l2D / k1;
+ }
+
+ // Undo the local transformation
+ h[ch] = h[ch] * 4.0f + o[ch];
+ v[ch] = v[ch] * 4.0f + o[ch];
+ }
+
+ if (isFakeBT709)
+ {
+ MFloat oRGB[3];
+ MFloat hRGB[3];
+ MFloat vRGB[3];
+
+ ConvertFromFakeBT709(oRGB, o);
+ ConvertFromFakeBT709(hRGB, h);
+ ConvertFromFakeBT709(vRGB, v);
+
+ // Twiddling in fake BT.607 is a mess, just round off for now (the precision is pretty good anyway)
+ {
+ ParallelMath::RoundTowardNearestForScope rtn;
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MFloat fcoeffs[3] = { oRGB[ch], hRGB[ch], vRGB[ch] };
+
+ for (int c = 0; c < 3; c++)
+ {
+ MFloat coeff = ParallelMath::Max(ParallelMath::MakeFloatZero(), fcoeffs[c]);
+ if (ch == 1)
+ coeff = ParallelMath::Min(ParallelMath::MakeFloat(127.0f), coeff * (127.0f / 255.0f));
+ else
+ coeff = ParallelMath::Min(ParallelMath::MakeFloat(63.0f), coeff * (63.0f / 255.0f));
+ fcoeffs[c] = coeff;
+ }
+
+ for (int c = 0; c < 3; c++)
+ bestCoeffs[ch][c] = ParallelMath::RoundAndConvertToU15(fcoeffs[c], &rtn);
+ }
+ }
+
+ MUInt15 reconstructed[16][3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 dO = DecodePlanarCoeff(bestCoeffs[ch][0], ch);
+ MUInt15 dH = DecodePlanarCoeff(bestCoeffs[ch][1], ch);
+ MUInt15 dV = DecodePlanarCoeff(bestCoeffs[ch][2], ch);
+
+ MSInt16 hMinusO = ParallelMath::LosslessCast<MSInt16>::Cast(dH) - ParallelMath::LosslessCast<MSInt16>::Cast(dO);
+ MSInt16 vMinusO = ParallelMath::LosslessCast<MSInt16>::Cast(dV) - ParallelMath::LosslessCast<MSInt16>::Cast(dO);
+
+ MFloat error = ParallelMath::MakeFloatZero();
+
+ MSInt16 addend = ParallelMath::LosslessCast<MSInt16>::Cast(dO << 2) + 2;
+
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 pxv = ParallelMath::MakeUInt15(px);
+ MSInt16 x = ParallelMath::LosslessCast<MSInt16>::Cast(pxv & ParallelMath::MakeUInt15(3));
+ MSInt16 y = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RightShift(pxv, 2));
+
+ MSInt16 interpolated = ParallelMath::RightShift(ParallelMath::CompactMultiply(x, hMinusO) + ParallelMath::CompactMultiply(y, vMinusO) + addend, 2);
+ MUInt15 clampedLow = ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::MakeSInt16(0), interpolated));
+ reconstructed[px][ch] = ParallelMath::Min(ParallelMath::MakeUInt15(255), clampedLow);
+ }
+ }
+
+ totalError = ParallelMath::MakeFloatZero();
+ for (int px = 0; px < 16; px++)
+ totalError = totalError + ComputeErrorFakeBT709(reconstructed[px], preWeightedPixels[px]);
+ }
+ else
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MFloat fcoeffs[3] = { o[ch], h[ch], v[ch] };
+ MUInt15 coeffRanges[3][2];
+
+ for (int c = 0; c < 3; c++)
+ {
+ MFloat coeff = ParallelMath::Max(ParallelMath::MakeFloatZero(), fcoeffs[c]);
+ if (ch == 1)
+ coeff = ParallelMath::Min(ParallelMath::MakeFloat(127.0f), coeff * (127.0f / 255.0f));
+ else
+ coeff = ParallelMath::Min(ParallelMath::MakeFloat(63.0f), coeff * (63.0f / 255.0f));
+ fcoeffs[c] = coeff;
+ }
+
+ {
+ ParallelMath::RoundDownForScope rd;
+ for (int c = 0; c < 3; c++)
+ coeffRanges[c][0] = ParallelMath::RoundAndConvertToU15(fcoeffs[c], &rd);
+ }
+
+ {
+ ParallelMath::RoundUpForScope ru;
+ for (int c = 0; c < 3; c++)
+ coeffRanges[c][1] = ParallelMath::RoundAndConvertToU15(fcoeffs[c], &ru);
+ }
+
+ MFloat bestChannelError = ParallelMath::MakeFloat(FLT_MAX);
+ for (int io = 0; io < 2; io++)
+ {
+ MUInt15 dO = DecodePlanarCoeff(coeffRanges[0][io], ch);
+
+ for (int ih = 0; ih < 2; ih++)
+ {
+ MUInt15 dH = DecodePlanarCoeff(coeffRanges[1][ih], ch);
+ MSInt16 hMinusO = ParallelMath::LosslessCast<MSInt16>::Cast(dH) - ParallelMath::LosslessCast<MSInt16>::Cast(dO);
+
+ for (int iv = 0; iv < 2; iv++)
+ {
+ MUInt15 dV = DecodePlanarCoeff(coeffRanges[2][iv], ch);
+ MSInt16 vMinusO = ParallelMath::LosslessCast<MSInt16>::Cast(dV) - ParallelMath::LosslessCast<MSInt16>::Cast(dO);
+
+ MFloat error = ParallelMath::MakeFloatZero();
+
+ MSInt16 addend = ParallelMath::LosslessCast<MSInt16>::Cast(dO << 2) + 2;
+
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 pxv = ParallelMath::MakeUInt15(px);
+ MSInt16 x = ParallelMath::LosslessCast<MSInt16>::Cast(pxv & ParallelMath::MakeUInt15(3));
+ MSInt16 y = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RightShift(pxv, 2));
+
+ MSInt16 interpolated = ParallelMath::RightShift(ParallelMath::CompactMultiply(x, hMinusO) + ParallelMath::CompactMultiply(y, vMinusO) + addend, 2);
+ MUInt15 clampedLow = ParallelMath::ToUInt15(ParallelMath::Max(ParallelMath::MakeSInt16(0), interpolated));
+ MUInt15 dec = ParallelMath::Min(ParallelMath::MakeUInt15(255), clampedLow);
+
+ MSInt16 delta = ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][ch]) - ParallelMath::LosslessCast<MSInt16>::Cast(dec);
+
+ MFloat deltaF = ParallelMath::ToFloat(delta);
+ error = error + deltaF * deltaF;
+ }
+
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, bestChannelError));
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ bestChannelError = ParallelMath::Min(error, bestChannelError);
+ ParallelMath::ConditionalSet(bestCoeffs[ch][0], errorBetter, coeffRanges[0][io]);
+ ParallelMath::ConditionalSet(bestCoeffs[ch][1], errorBetter, coeffRanges[1][ih]);
+ ParallelMath::ConditionalSet(bestCoeffs[ch][2], errorBetter, coeffRanges[2][iv]);
+ }
+ }
+ }
+ }
+
+ if (!isUniform)
+ {
+ switch (ch)
+ {
+ case 0:
+ bestChannelError = bestChannelError * (options.redWeight * options.redWeight);
+ break;
+ case 1:
+ bestChannelError = bestChannelError * (options.greenWeight * options.greenWeight);
+ break;
+ case 2:
+ bestChannelError = bestChannelError * (options.blueWeight * options.blueWeight);
+ break;
+ default:
+ break;
+ }
+ }
+
+ totalError = totalError + bestChannelError;
+ }
+ }
+
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(totalError, bestError));
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ bestError = ParallelMath::Min(bestError, totalError);
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (!ParallelMath::Extract(errorBetter, block))
+ continue;
+
+ int ro = ParallelMath::Extract(bestCoeffs[0][0], block);
+ int rh = ParallelMath::Extract(bestCoeffs[0][1], block);
+ int rv = ParallelMath::Extract(bestCoeffs[0][2], block);
+
+ int go = ParallelMath::Extract(bestCoeffs[1][0], block);
+ int gh = ParallelMath::Extract(bestCoeffs[1][1], block);
+ int gv = ParallelMath::Extract(bestCoeffs[1][2], block);
+
+ int bo = ParallelMath::Extract(bestCoeffs[2][0], block);
+ int bh = ParallelMath::Extract(bestCoeffs[2][1], block);
+ int bv = ParallelMath::Extract(bestCoeffs[2][2], block);
+
+ int go1 = go >> 6;
+ int go2 = go & 63;
+
+ int bo1 = bo >> 5;
+ int bo2 = (bo >> 3) & 3;
+ int bo3 = bo & 7;
+
+ int rh1 = (rh >> 1);
+ int rh2 = rh & 1;
+
+ int fakeR = ro >> 2;
+ int fakeDR = go1 | ((ro & 3) << 1);
+
+ int fakeG = (go2 >> 2);
+ int fakeDG = ((go2 & 3) << 1) | bo1;
+
+ int fakeB = bo2;
+ int fakeDB = bo3 >> 1;
+
+ uint32_t highBits = 0;
+ uint32_t lowBits = 0;
+
+ // Avoid overflowing R
+ if ((fakeDR & 4) != 0 && fakeR + fakeDR < 8)
+ highBits |= 1 << (63 - 32);
+
+ // Avoid overflowing G
+ if ((fakeDG & 4) != 0 && fakeG + fakeDG < 8)
+ highBits |= 1 << (55 - 32);
+
+ // Overflow B
+ if (fakeB + fakeDB < 4)
+ {
+ // Overflow low
+ highBits |= 1 << (42 - 32);
+ }
+ else
+ {
+ // Overflow high
+ highBits |= 7 << (45 - 32);
+ }
+
+ highBits |= ro << (57 - 32);
+ highBits |= go1 << (56 - 32);
+ highBits |= go2 << (49 - 32);
+ highBits |= bo1 << (48 - 32);
+ highBits |= bo2 << (43 - 32);
+ highBits |= bo3 << (39 - 32);
+ highBits |= rh1 << (34 - 32);
+ highBits |= 1 << (33 - 32);
+ highBits |= rh2 << (32 - 32);
+
+ lowBits |= gh << 25;
+ lowBits |= bh << 19;
+ lowBits |= rv << 13;
+ lowBits |= gv << 6;
+ lowBits |= bv << 0;
+
+ for (int i = 0; i < 4; i++)
+ outputBuffer[block * 8 + i] = (highBits >> (24 - i * 8)) & 0xff;
+ for (int i = 0; i < 4; i++)
+ outputBuffer[block * 8 + i + 4] = (lowBits >> (24 - i * 8)) & 0xff;
+ }
+ }
+}
+
+void cvtt::Internal::ETCComputer::CompressETC2Block(uint8_t *outputBuffer, const PixelBlockU8 *pixelBlocks, ETC2CompressionData *compressionData, const Options &options, bool punchthroughAlpha)
+{
+ ParallelMath::Int16CompFlag pixelIsTransparent[16];
+ ParallelMath::Int16CompFlag anyTransparent = ParallelMath::MakeBoolInt16(false);
+ ParallelMath::Int16CompFlag allTransparent = ParallelMath::MakeBoolInt16(true);
+
+ if (punchthroughAlpha)
+ {
+ const float fThreshold = std::max<float>(std::min<float>(1.0f, options.threshold), 0.0f) * 255.0f;
+
+ // +1.0f is intentional, we want to take the next valid integer (even if it's 256) since everything else lower is transparent
+ MUInt15 threshold = ParallelMath::MakeUInt15(static_cast<uint16_t>(std::floor(fThreshold + 1.0f)));
+
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 alpha;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutUInt15(alpha, block, pixelBlocks[block].m_pixels[px][3]);
+
+ ParallelMath::Int16CompFlag isTransparent = ParallelMath::Less(alpha, threshold);
+ anyTransparent = (anyTransparent | isTransparent);
+ allTransparent = (allTransparent & isTransparent);
+ pixelIsTransparent[px] = isTransparent;
+ }
+ }
+ else
+ {
+ for (int px = 0; px < 16; px++)
+ pixelIsTransparent[px] = ParallelMath::MakeBoolInt16(false);
+
+ allTransparent = anyTransparent = ParallelMath::MakeBoolInt16(false);
+ }
+
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+
+ ETC2CompressionDataInternal* internalData = static_cast<ETC2CompressionDataInternal*>(compressionData);
+
+ MUInt15 pixels[16][3];
+ MFloat preWeightedPixels[16][3];
+ ExtractBlocks(pixels, preWeightedPixels, pixelBlocks, options);
+
+ if (ParallelMath::AnySet(anyTransparent))
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::Int16CompFlag flag = pixelIsTransparent[px];
+ ParallelMath::FloatCompFlag fflag = ParallelMath::Int16FlagToFloat(flag);
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ ParallelMath::ConditionalSet(pixels[px][ch], flag, ParallelMath::MakeUInt15(0));
+ ParallelMath::ConditionalSet(preWeightedPixels[px][ch], fflag, ParallelMath::MakeFloat(0.0f));
+ }
+ }
+ }
+
+ if (!ParallelMath::AllSet(allTransparent))
+ EncodePlanar(outputBuffer, bestError, pixels, preWeightedPixels, options);
+
+ MFloat chromaDelta[16][2];
+
+ MUInt15 numOpaque = ParallelMath::MakeUInt15(16);
+ for (int px = 0; px < 16; px++)
+ numOpaque = numOpaque - ParallelMath::SelectOrZero(pixelIsTransparent[px], ParallelMath::MakeUInt15(1));
+
+ if (options.flags & cvtt::Flags::Uniform)
+ {
+ MSInt16 chromaCoordinates3[16][2];
+ for (int px = 0; px < 16; px++)
+ {
+ chromaCoordinates3[px][0] = ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][0]) - ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][2]);
+ chromaCoordinates3[px][1] = ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][0]) - ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][1] << 1) + ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px][2]);
+ }
+
+ MSInt16 chromaCoordinateCentroid[2] = { ParallelMath::MakeSInt16(0), ParallelMath::MakeSInt16(0) };
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ chromaCoordinateCentroid[ch] = chromaCoordinateCentroid[ch] + chromaCoordinates3[px][ch];
+ }
+
+ if (punchthroughAlpha)
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ {
+ MUInt15 chromaCoordinateMultiplied = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(chromaCoordinates3[px][ch], numOpaque));
+ MSInt16 delta = ParallelMath::LosslessCast<MSInt16>::Cast(chromaCoordinateMultiplied) - chromaCoordinateCentroid[ch];
+ chromaDelta[px][ch] = ParallelMath::ToFloat(delta);
+ }
+ }
+ }
+ else
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ chromaDelta[px][ch] = ParallelMath::ToFloat((chromaCoordinates3[px][ch] << 4) - chromaCoordinateCentroid[ch]);
+ }
+ }
+
+ const MFloat rcpSqrt3 = ParallelMath::MakeFloat(0.57735026918962576450914878050196f);
+
+ for (int px = 0; px < 16; px++)
+ chromaDelta[px][1] = chromaDelta[px][1] * rcpSqrt3;
+ }
+ else
+ {
+ const float chromaAxis0[3] = { internalData->m_chromaSideAxis0[0], internalData->m_chromaSideAxis0[1], internalData->m_chromaSideAxis0[2] };
+ const float chromaAxis1[3] = { internalData->m_chromaSideAxis1[0], internalData->m_chromaSideAxis1[1], internalData->m_chromaSideAxis1[2] };
+
+ MFloat chromaCoordinates3[16][2];
+ for (int px = 0; px < 16; px++)
+ {
+ const MFloat &px0 = preWeightedPixels[px][0];
+ const MFloat &px1 = preWeightedPixels[px][1];
+ const MFloat &px2 = preWeightedPixels[px][2];
+
+ chromaCoordinates3[px][0] = px0 * chromaAxis0[0] + px1 * chromaAxis0[1] + px2 * chromaAxis0[2];
+ chromaCoordinates3[px][1] = px0 * chromaAxis1[0] + px1 * chromaAxis1[1] + px2 * chromaAxis1[2];
+ }
+
+ MFloat chromaCoordinateCentroid[2] = { ParallelMath::MakeFloatZero(), ParallelMath::MakeFloatZero() };
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ chromaCoordinateCentroid[ch] = chromaCoordinateCentroid[ch] + chromaCoordinates3[px][ch];
+ }
+
+ if (punchthroughAlpha)
+ {
+ const MFloat numOpaqueF = ParallelMath::ToFloat(numOpaque);
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ {
+ MFloat chromaCoordinateMultiplied = chromaCoordinates3[px][ch] * numOpaqueF;
+ MFloat delta = chromaCoordinateMultiplied - chromaCoordinateCentroid[ch];
+ chromaDelta[px][ch] = delta;
+ }
+ }
+ }
+ else
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 2; ch++)
+ chromaDelta[px][ch] = chromaCoordinates3[px][ch] * 16.0f - chromaCoordinateCentroid[ch];
+ }
+ }
+ }
+
+
+ MFloat covXX = ParallelMath::MakeFloatZero();
+ MFloat covYY = ParallelMath::MakeFloatZero();
+ MFloat covXY = ParallelMath::MakeFloatZero();
+
+ for (int px = 0; px < 16; px++)
+ {
+ MFloat nx = chromaDelta[px][0];
+ MFloat ny = chromaDelta[px][1];
+
+ covXX = covXX + nx * nx;
+ covYY = covYY + ny * ny;
+ covXY = covXY + nx * ny;
+ }
+
+ MFloat halfTrace = (covXX + covYY) * 0.5f;
+ MFloat det = covXX * covYY - covXY * covXY;
+
+ MFloat mm = ParallelMath::Sqrt(ParallelMath::Max(ParallelMath::MakeFloatZero(), halfTrace * halfTrace - det));
+
+ MFloat ev = halfTrace + mm;
+
+ MFloat dx = (covYY - ev + covXY);
+ MFloat dy = -(covXX - ev + covXY);
+
+ // If evenly distributed, pick an arbitrary plane
+ ParallelMath::FloatCompFlag allZero = ParallelMath::Equal(dx, ParallelMath::MakeFloatZero()) & ParallelMath::Equal(dy, ParallelMath::MakeFloatZero());
+ ParallelMath::ConditionalSet(dx, allZero, ParallelMath::MakeFloat(1.f));
+
+ ParallelMath::Int16CompFlag sectorAssignments[16];
+ for (int px = 0; px < 16; px++)
+ sectorAssignments[px] = ParallelMath::FloatFlagToInt16(ParallelMath::Less(chromaDelta[px][0] * dx + chromaDelta[px][1] * dy, ParallelMath::MakeFloatZero()));
+
+ if (!ParallelMath::AllSet(allTransparent))
+ {
+ EncodeTMode(outputBuffer, bestError, sectorAssignments, pixels, preWeightedPixels, options);
+
+ // Flip sector assignments
+ for (int px = 0; px < 16; px++)
+ sectorAssignments[px] = ParallelMath::Not(sectorAssignments[px]);
+
+ EncodeTMode(outputBuffer, bestError, sectorAssignments, pixels, preWeightedPixels, options);
+
+ EncodeHMode(outputBuffer, bestError, sectorAssignments, pixels, internalData->m_h, preWeightedPixels, options);
+
+ CompressETC1BlockInternal(bestError, outputBuffer, pixels, preWeightedPixels, internalData->m_drs, options, true);
+ }
+
+ if (ParallelMath::AnySet(anyTransparent))
+ {
+ if (!ParallelMath::AllSet(allTransparent))
+ {
+ // Flip sector assignments
+ for (int px = 0; px < 16; px++)
+ sectorAssignments[px] = ParallelMath::Not(sectorAssignments[px]);
+ }
+
+ // Reset the error of any transparent blocks to max and retry with punchthrough modes
+ ParallelMath::ConditionalSet(bestError, ParallelMath::Int16FlagToFloat(anyTransparent), ParallelMath::MakeFloat(FLT_MAX));
+
+ EncodeVirtualTModePunchthrough(outputBuffer, bestError, sectorAssignments, pixels, preWeightedPixels, pixelIsTransparent, anyTransparent, allTransparent, options);
+
+ // Flip sector assignments
+ for (int px = 0; px < 16; px++)
+ sectorAssignments[px] = ParallelMath::Not(sectorAssignments[px]);
+
+ EncodeVirtualTModePunchthrough(outputBuffer, bestError, sectorAssignments, pixels, preWeightedPixels, pixelIsTransparent, anyTransparent, allTransparent, options);
+
+ CompressETC1PunchthroughBlockInternal(bestError, outputBuffer, pixels, preWeightedPixels, pixelIsTransparent, static_cast<ETC2CompressionDataInternal*>(compressionData)->m_drs, options);
+ }
+}
+
+void cvtt::Internal::ETCComputer::CompressETC2AlphaBlock(uint8_t *outputBuffer, const PixelBlockU8 *pixelBlocks, const Options &options)
+{
+ MUInt15 pixels[16];
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutUInt15(pixels[px], block, pixelBlocks[block].m_pixels[px][3]);
+ }
+
+ CompressETC2AlphaBlockInternal(outputBuffer, pixels, false, false, options);
+}
+
+void cvtt::Internal::ETCComputer::CompressETC2AlphaBlockInternal(uint8_t *outputBuffer, const MUInt15 pixels[16], bool is11Bit, bool isSigned, const Options &options)
+{
+ MUInt15 minAlpha = ParallelMath::MakeUInt15(is11Bit ? 2047 : 255);
+ MUInt15 maxAlpha = ParallelMath::MakeUInt15(0);
+
+ for (int px = 0; px < 16; px++)
+ {
+ minAlpha = ParallelMath::Min(minAlpha, pixels[px]);
+ maxAlpha = ParallelMath::Max(maxAlpha, pixels[px]);
+ }
+
+ MUInt15 alphaSpan = maxAlpha - minAlpha;
+ MUInt15 alphaSpanMidpointTimes2 = maxAlpha + minAlpha;
+
+ MUInt31 bestTotalError = ParallelMath::MakeUInt31(0x7fffffff);
+ MUInt15 bestTableIndex = ParallelMath::MakeUInt15(0);
+ MUInt15 bestBaseCodeword = ParallelMath::MakeUInt15(0);
+ MUInt15 bestMultiplier = ParallelMath::MakeUInt15(0);
+ MUInt15 bestIndexes[16];
+
+ for (int px = 0; px < 16; px++)
+ bestIndexes[px] = ParallelMath::MakeUInt15(0);
+
+ const int numAlphaRanges = 10;
+ for (uint16_t tableIndex = 0; tableIndex < 16; tableIndex++)
+ {
+ for (int r = 0; r < numAlphaRanges; r++)
+ {
+ int subrange = r % 3;
+ int mainRange = r / 3;
+
+ int16_t maxOffset = Tables::ETC2::g_alphaModifierTablePositive[tableIndex][3 - mainRange - (subrange & 1)];
+ int16_t minOffset = -Tables::ETC2::g_alphaModifierTablePositive[tableIndex][3 - mainRange - ((subrange >> 1) & 1)] - 1;
+ uint16_t offsetSpan = static_cast<uint16_t>(maxOffset - minOffset);
+
+ MSInt16 vminOffset = ParallelMath::MakeSInt16(minOffset);
+ MUInt15 vmaxOffset = ParallelMath::MakeUInt15(maxOffset);
+ MUInt15 voffsetSpan = ParallelMath::MakeUInt15(offsetSpan);
+
+ MUInt15 minMultiplier = ParallelMath::MakeUInt15(0);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t singleAlphaSpan = ParallelMath::Extract(alphaSpan, block);
+
+ uint16_t lowMultiplier = singleAlphaSpan / offsetSpan;
+ ParallelMath::PutUInt15(minMultiplier, block, lowMultiplier);
+ }
+
+ if (is11Bit)
+ {
+ // Clamps this to valid multipliers under 15 and rounds down to nearest multiple of 8
+ minMultiplier = ParallelMath::Min(minMultiplier, ParallelMath::MakeUInt15(112)) & ParallelMath::MakeUInt15(120);
+ }
+ else
+ {
+ // We cap at 1 and 14 so both multipliers are valid and dividable
+ // Cases where offset span is 0 should be caught by multiplier 1 of table 13
+ minMultiplier = ParallelMath::Max(ParallelMath::Min(minMultiplier, ParallelMath::MakeUInt15(14)), ParallelMath::MakeUInt15(1));
+ }
+
+ for (uint16_t multiplierOffset = 0; multiplierOffset < 2; multiplierOffset++)
+ {
+ MUInt15 multiplier = minMultiplier;
+
+ if (is11Bit)
+ {
+ if (multiplierOffset == 1)
+ multiplier = multiplier + ParallelMath::MakeUInt15(8);
+ else
+ multiplier = ParallelMath::Max(multiplier, ParallelMath::MakeUInt15(1));
+ }
+ else
+ {
+ if (multiplierOffset == 1)
+ multiplier = multiplier + ParallelMath::MakeUInt15(1);
+ }
+
+ MSInt16 multipliedMinOffset = ParallelMath::CompactMultiply(ParallelMath::LosslessCast<MSInt16>::Cast(multiplier), vminOffset);
+ MUInt15 multipliedMaxOffset = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(multiplier, vmaxOffset));
+
+ // codeword = (maxOffset + minOffset + minAlpha + maxAlpha) / 2
+ MSInt16 unclampedBaseAlphaTimes2 = ParallelMath::LosslessCast<MSInt16>::Cast(alphaSpanMidpointTimes2) - ParallelMath::LosslessCast<MSInt16>::Cast(multipliedMaxOffset) - multipliedMinOffset;
+
+ MUInt15 baseAlpha;
+ if (is11Bit)
+ {
+ // In unsigned, 4 is added to the unquantized alpha, so compensating for that cancels the 4 we have to add to do rounding.
+ if (isSigned)
+ unclampedBaseAlphaTimes2 = unclampedBaseAlphaTimes2 + ParallelMath::MakeSInt16(8);
+
+ // -128 is illegal for some reason
+ MSInt16 minBaseAlphaTimes2 = isSigned ? ParallelMath::MakeSInt16(16) : ParallelMath::MakeSInt16(0);
+
+ MUInt15 clampedBaseAlphaTimes2 = ParallelMath::Min(ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(unclampedBaseAlphaTimes2, minBaseAlphaTimes2)), ParallelMath::MakeUInt15(4095));
+ baseAlpha = ParallelMath::RightShift(clampedBaseAlphaTimes2, 1) & ParallelMath::MakeUInt15(2040);
+
+ if (!isSigned)
+ baseAlpha = baseAlpha + ParallelMath::MakeUInt15(4);
+ }
+ else
+ {
+ MUInt15 clampedBaseAlphaTimes2 = ParallelMath::Min(ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(unclampedBaseAlphaTimes2, ParallelMath::MakeSInt16(0))), ParallelMath::MakeUInt15(510));
+ baseAlpha = ParallelMath::RightShift(clampedBaseAlphaTimes2 + ParallelMath::MakeUInt15(1), 1);
+ }
+
+ MUInt15 indexes[16];
+ MUInt31 totalError = ParallelMath::MakeUInt31(0);
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 quantizedValues;
+ QuantizeETC2Alpha(tableIndex, pixels[px], baseAlpha, multiplier, is11Bit, isSigned, indexes[px], quantizedValues);
+
+ if (is11Bit)
+ {
+ MSInt16 delta = ParallelMath::LosslessCast<MSInt16>::Cast(quantizedValues) - ParallelMath::LosslessCast<MSInt16>::Cast(pixels[px]);
+ MSInt32 deltaSq = ParallelMath::XMultiply(delta, delta);
+ totalError = totalError + ParallelMath::LosslessCast<MUInt31>::Cast(deltaSq);
+ }
+ else
+ totalError = totalError + ParallelMath::ToUInt31(ParallelMath::SqDiffUInt8(quantizedValues, pixels[px]));
+ }
+
+ ParallelMath::Int16CompFlag isBetter = ParallelMath::Int32FlagToInt16(ParallelMath::Less(totalError, bestTotalError));
+ if (ParallelMath::AnySet(isBetter))
+ {
+ ParallelMath::ConditionalSet(bestTotalError, isBetter, totalError);
+ ParallelMath::ConditionalSet(bestTableIndex, isBetter, ParallelMath::MakeUInt15(tableIndex));
+ ParallelMath::ConditionalSet(bestBaseCodeword, isBetter, baseAlpha);
+ ParallelMath::ConditionalSet(bestMultiplier, isBetter, multiplier);
+
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestIndexes[px], isBetter, indexes[px]);
+ }
+
+ // TODO: Do one refine pass
+ }
+ }
+ }
+
+ if (is11Bit)
+ {
+ bestMultiplier = ParallelMath::RightShift(bestMultiplier, 3);
+
+ if (isSigned)
+ bestBaseCodeword = bestBaseCodeword ^ ParallelMath::MakeUInt15(0x80);
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint8_t *output = outputBuffer + block * 8;
+
+ output[0] = static_cast<uint8_t>(ParallelMath::Extract(bestBaseCodeword, block));
+
+ ParallelMath::ScalarUInt16 multiplier = ParallelMath::Extract(bestMultiplier, block);
+ ParallelMath::ScalarUInt16 tableIndex = ParallelMath::Extract(bestTableIndex, block);
+
+ output[1] = static_cast<uint8_t>((multiplier << 4) | tableIndex);
+
+ static const int pixelSelectorOrder[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+ ParallelMath::ScalarUInt16 indexes[16];
+ for (int px = 0; px < 16; px++)
+ indexes[pixelSelectorOrder[px]] = ParallelMath::Extract(bestIndexes[px], block);
+
+ int outputOffset = 2;
+ int outputBits = 0;
+ int numOutputBits = 0;
+ for (int s = 0; s < 16; s++)
+ {
+ outputBits = (outputBits << 3) | indexes[s];
+ numOutputBits += 3;
+
+ if (numOutputBits >= 8)
+ {
+ output[outputOffset++] = static_cast<uint8_t>(outputBits >> (numOutputBits - 8));
+ numOutputBits -= 8;
+
+ outputBits &= ((1 << numOutputBits) - 1);
+ }
+ }
+
+ assert(outputOffset == 8 && numOutputBits == 0);
+ }
+}
+
+void cvtt::Internal::ETCComputer::CompressEACBlock(uint8_t *outputBuffer, const PixelBlockScalarS16 *inputBlocks, bool isSigned, const Options &options)
+{
+ MUInt15 pixels[16];
+ for (int px = 0; px < 16; px++)
+ {
+ MSInt16 adjustedPixel;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutSInt16(adjustedPixel, block, inputBlocks[block].m_pixels[px]);
+
+ // We use a slightly shifted range here so we can keep the unquantized base color in a UInt15
+ // That is, signed range is 1..2047, and unsigned range is 0..2047
+ if (isSigned)
+ {
+ adjustedPixel = ParallelMath::Min(adjustedPixel, ParallelMath::MakeSInt16(1023)) + ParallelMath::MakeSInt16(1024);
+ adjustedPixel = ParallelMath::Max(ParallelMath::MakeSInt16(1), adjustedPixel);
+ }
+ else
+ {
+ adjustedPixel = ParallelMath::Min(adjustedPixel, ParallelMath::MakeSInt16(2047));
+ adjustedPixel = ParallelMath::Max(ParallelMath::MakeSInt16(0), adjustedPixel);
+ }
+
+
+ pixels[px] = ParallelMath::LosslessCast<MUInt15>::Cast(adjustedPixel);
+ }
+
+ CompressETC2AlphaBlockInternal(outputBuffer, pixels, true, isSigned, options);
+}
+
+void cvtt::Internal::ETCComputer::CompressETC1Block(uint8_t *outputBuffer, const PixelBlockU8 *inputBlocks, ETC1CompressionData *compressionData, const Options &options)
+{
+ DifferentialResolveStorage &drs = static_cast<ETC1CompressionDataInternal*>(compressionData)->m_drs;
+ MFloat bestTotalError = ParallelMath::MakeFloat(FLT_MAX);
+
+ MUInt15 pixels[16][3];
+ MFloat preWeightedPixels[16][3];
+ ExtractBlocks(pixels, preWeightedPixels, inputBlocks, options);
+
+ CompressETC1BlockInternal(bestTotalError, outputBuffer, pixels, preWeightedPixels, drs, options, false);
+}
+
+void cvtt::Internal::ETCComputer::ExtractBlocks(MUInt15 pixels[16][3], MFloat preWeightedPixels[16][3], const PixelBlockU8 *inputBlocks, const Options &options)
+{
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+ bool isUniform = ((options.flags & cvtt::Flags::Uniform) != 0);
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutUInt15(pixels[px][ch], block, inputBlocks[block].m_pixels[px][ch]);
+ }
+
+ if (isFakeBT709)
+ ConvertToFakeBT709(preWeightedPixels[px], pixels[px]);
+ else if (isUniform)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ preWeightedPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]);
+ }
+ else
+ {
+ preWeightedPixels[px][0] = ParallelMath::ToFloat(pixels[px][0]) * options.redWeight;
+ preWeightedPixels[px][1] = ParallelMath::ToFloat(pixels[px][1]) * options.greenWeight;
+ preWeightedPixels[px][2] = ParallelMath::ToFloat(pixels[px][2]) * options.blueWeight;
+ }
+ }
+}
+
+void cvtt::Internal::ETCComputer::ResolveHalfBlockFakeBT709RoundingAccurate(MUInt15 quantized[3], const MUInt15 sectorCumulative[3], bool isDifferential)
+{
+ for (int ch = 0; ch < 3; ch++)
+ {
+ const MUInt15& cu15 = sectorCumulative[ch];
+
+ if (isDifferential)
+ {
+ //quantized[ch] = (cu * 31 + (cu >> 3)) >> 11;
+ quantized[ch] = ParallelMath::ToUInt15(
+ ParallelMath::RightShift(
+ (ParallelMath::LosslessCast<MUInt16>::Cast(cu15) << 5) - ParallelMath::LosslessCast<MUInt16>::Cast(cu15) + ParallelMath::LosslessCast<MUInt16>::Cast(ParallelMath::RightShift(cu15, 3))
+ , 11)
+ );
+ }
+ else
+ {
+ //quantized[ch] = (cu * 30 + (cu >> 3)) >> 12;
+ quantized[ch] = ParallelMath::ToUInt15(
+ ParallelMath::RightShift(
+ (ParallelMath::LosslessCast<MUInt16>::Cast(cu15) << 5) - ParallelMath::LosslessCast<MUInt16>::Cast(cu15 << 1) + ParallelMath::LosslessCast<MUInt16>::Cast(ParallelMath::RightShift(cu15, 3))
+ , 12)
+ );
+ }
+ }
+
+ MFloat lowOctantRGBFloat[3];
+ MFloat highOctantRGBFloat[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 unquantized;
+ MUInt15 unquantizedNext;
+ if (isDifferential)
+ {
+ unquantized = (quantized[ch] << 3) | ParallelMath::RightShift(quantized[ch], 2);
+ MUInt15 quantizedNext = ParallelMath::Min(ParallelMath::MakeUInt15(31), quantized[ch] + ParallelMath::MakeUInt15(1));
+ unquantizedNext = (quantizedNext << 3) | ParallelMath::RightShift(quantizedNext, 2);
+ }
+ else
+ {
+ unquantized = (quantized[ch] << 4) | quantized[ch];
+ unquantizedNext = ParallelMath::Min(ParallelMath::MakeUInt15(255), unquantized + ParallelMath::MakeUInt15(17));
+ }
+ lowOctantRGBFloat[ch] = ParallelMath::ToFloat(unquantized << 3);
+ highOctantRGBFloat[ch] = ParallelMath::ToFloat(unquantizedNext << 3);
+ }
+
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt15 bestOctant = ParallelMath::MakeUInt15(0);
+
+ MFloat cumulativeYUV[3];
+ ConvertToFakeBT709(cumulativeYUV, sectorCumulative);
+
+ for (uint16_t octant = 0; octant < 8; octant++)
+ {
+ const MFloat &r = (octant & 1) ? highOctantRGBFloat[0] : lowOctantRGBFloat[0];
+ const MFloat &g = (octant & 2) ? highOctantRGBFloat[1] : lowOctantRGBFloat[1];
+ const MFloat &b = (octant & 4) ? highOctantRGBFloat[2] : lowOctantRGBFloat[2];
+
+ MFloat octantYUV[3];
+ ConvertToFakeBT709(octantYUV, r, g, b);
+
+ MFloat delta[3];
+ for (int ch = 0; ch < 3; ch++)
+ delta[ch] = octantYUV[ch] - cumulativeYUV[ch];
+
+ MFloat error = delta[0] * delta[0] + delta[1] + delta[1] + delta[2] * delta[2];
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, bestError));
+ ParallelMath::ConditionalSet(bestOctant, errorBetter, ParallelMath::MakeUInt15(octant));
+ bestError = ParallelMath::Min(error, bestError);
+ }
+
+ for (int ch = 0; ch < 3; ch++)
+ quantized[ch] = quantized[ch] + (ParallelMath::RightShift(bestOctant, ch) & ParallelMath::MakeUInt15(1));
+}
+
+void cvtt::Internal::ETCComputer::ResolveHalfBlockFakeBT709RoundingFast(MUInt15 quantized[3], const MUInt15 sectorCumulative[3], bool isDifferential)
+{
+ // sectorCumulative range is 0..2040 (11 bits)
+ MUInt15 roundingOffset = ParallelMath::MakeUInt15(0);
+
+ MUInt15 rOffset;
+ MUInt15 gOffset;
+ MUInt15 bOffset;
+ MUInt15 quantizedBase[3];
+ MUInt15 upperBound;
+
+ MUInt15 sectorCumulativeFillIn[3];
+ for (int ch = 0; ch < 3; ch++)
+ sectorCumulativeFillIn[ch] = sectorCumulative[ch] + ParallelMath::RightShift(sectorCumulative[ch], 8);
+
+ if (isDifferential)
+ {
+ rOffset = (sectorCumulativeFillIn[0] << 6) & ParallelMath::MakeUInt15(0xf00);
+ gOffset = (sectorCumulativeFillIn[1] << 4) & ParallelMath::MakeUInt15(0x0f0);
+ bOffset = ParallelMath::RightShift(sectorCumulativeFillIn[2], 2) & ParallelMath::MakeUInt15(0x00f);
+
+ for (int ch = 0; ch < 3; ch++)
+ quantizedBase[ch] = ParallelMath::RightShift(sectorCumulativeFillIn[ch], 6);
+
+ upperBound = ParallelMath::MakeUInt15(31);
+ }
+ else
+ {
+ rOffset = (sectorCumulativeFillIn[0] << 5) & ParallelMath::MakeUInt15(0xf00);
+ gOffset = (sectorCumulativeFillIn[1] << 1) & ParallelMath::MakeUInt15(0x0f0);
+ bOffset = ParallelMath::RightShift(sectorCumulativeFillIn[2], 3) & ParallelMath::MakeUInt15(0x00f);
+
+ for (int ch = 0; ch < 3; ch++)
+ quantizedBase[ch] = ParallelMath::RightShift(sectorCumulativeFillIn[ch], 7);
+
+ upperBound = ParallelMath::MakeUInt15(15);
+ }
+
+ MUInt15 lookupIndex = (rOffset | gOffset | bOffset);
+
+ MUInt15 octant;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutUInt15(octant, block, Tables::FakeBT709::g_rounding16[ParallelMath::Extract(lookupIndex, block)]);
+
+ quantizedBase[0] = quantizedBase[0] + (octant & ParallelMath::MakeUInt15(1));
+ quantizedBase[1] = quantizedBase[1] + (ParallelMath::RightShift(octant, 1) & ParallelMath::MakeUInt15(1));
+ quantizedBase[2] = quantizedBase[2] + (ParallelMath::RightShift(octant, 2) & ParallelMath::MakeUInt15(1));
+
+ for (int ch = 0; ch < 3; ch++)
+ quantized[ch] = ParallelMath::Min(quantizedBase[ch], upperBound);
+}
+
+void cvtt::Internal::ETCComputer::ResolveTHFakeBT709Rounding(MUInt15 quantized[3], const MUInt15 targets[3], const MUInt15 &granularity)
+{
+ MFloat lowOctantRGBFloat[3];
+ MFloat highOctantRGBFloat[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 unquantized = (quantized[ch] << 4) | quantized[ch];
+ MUInt15 unquantizedNext = ParallelMath::Min(ParallelMath::MakeUInt15(255), unquantized + ParallelMath::MakeUInt15(17));
+
+ lowOctantRGBFloat[ch] = ParallelMath::ToFloat(ParallelMath::CompactMultiply(unquantized, granularity) << 1);
+ highOctantRGBFloat[ch] = ParallelMath::ToFloat(ParallelMath::CompactMultiply(unquantizedNext, granularity) << 1);
+ }
+
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt15 bestOctant = ParallelMath::MakeUInt15(0);
+
+ MFloat cumulativeYUV[3];
+ ConvertToFakeBT709(cumulativeYUV, ParallelMath::ToFloat(targets[0]), ParallelMath::ToFloat(targets[1]), ParallelMath::ToFloat(targets[2]));
+
+ for (uint16_t octant = 0; octant < 8; octant++)
+ {
+ const MFloat &r = (octant & 1) ? highOctantRGBFloat[0] : lowOctantRGBFloat[0];
+ const MFloat &g = (octant & 2) ? highOctantRGBFloat[1] : lowOctantRGBFloat[1];
+ const MFloat &b = (octant & 4) ? highOctantRGBFloat[2] : lowOctantRGBFloat[2];
+
+ MFloat octantYUV[3];
+ ConvertToFakeBT709(octantYUV, r, g, b);
+
+ MFloat delta[3];
+ for (int ch = 0; ch < 3; ch++)
+ delta[ch] = octantYUV[ch] - cumulativeYUV[ch];
+
+ MFloat error = delta[0] * delta[0] + delta[1] + delta[1] + delta[2] * delta[2];
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, bestError));
+ ParallelMath::ConditionalSet(bestOctant, errorBetter, ParallelMath::MakeUInt15(octant));
+ bestError = ParallelMath::Min(error, bestError);
+ }
+
+ for (int ch = 0; ch < 3; ch++)
+ quantized[ch] = quantized[ch] + (ParallelMath::RightShift(bestOctant, ch) & ParallelMath::MakeUInt15(1));
+}
+
+void cvtt::Internal::ETCComputer::ConvertToFakeBT709(MFloat yuv[3], const MUInt15 color[3])
+{
+ MFloat floatRGB[3];
+ for (int ch = 0; ch < 3; ch++)
+ floatRGB[ch] = ParallelMath::ToFloat(color[ch]);
+
+ ConvertToFakeBT709(yuv, floatRGB);
+}
+
+void cvtt::Internal::ETCComputer::ConvertToFakeBT709(MFloat yuv[3], const MFloat color[3])
+{
+ ConvertToFakeBT709(yuv, color[0], color[1], color[2]);
+}
+
+void cvtt::Internal::ETCComputer::ConvertToFakeBT709(MFloat yuv[3], const MFloat &pr, const MFloat &pg, const MFloat &pb)
+{
+ MFloat r = pr;
+ MFloat g = pg;
+ MFloat b = pb;
+
+ yuv[0] = r * 0.368233989135369f + g * 1.23876274963149f + b * 0.125054068802017f;
+ yuv[1] = r * 0.5f - g * 0.4541529f - b * 0.04584709f;
+ yuv[2] = r * -0.081014709086133f - g * 0.272538676238785f + b * 0.353553390593274f;
+}
+
+void cvtt::Internal::ETCComputer::ConvertFromFakeBT709(MFloat rgb[3], const MFloat yuv[3])
+{
+ MFloat yy = yuv[0] * 0.57735026466774571071f;
+ MFloat u = yuv[1];
+ MFloat v = yuv[2];
+
+ rgb[0] = yy + u * 1.5748000207960953486f;
+ rgb[1] = yy - u * 0.46812425854364753669f - v * 0.26491652528157560861f;
+ rgb[2] = yy + v * 2.6242146882856944069f;
+}
+
+
+void cvtt::Internal::ETCComputer::QuantizeETC2Alpha(int tableIndex, const MUInt15& value, const MUInt15& baseValue, const MUInt15& multiplier, bool is11Bit, bool isSigned, MUInt15& outIndexes, MUInt15& outQuantizedValues)
+{
+ MSInt16 offset = ParallelMath::LosslessCast<MSInt16>::Cast(value) - ParallelMath::LosslessCast<MSInt16>::Cast(baseValue);
+ MSInt16 offsetTimes2 = offset + offset;
+
+ // ETC2's offset tables all have a reflect about 0.5*multiplier
+ MSInt16 offsetAboutReflectorTimes2 = offsetTimes2 + ParallelMath::LosslessCast<MSInt16>::Cast(multiplier);
+
+ MUInt15 absOffsetAboutReflectorTimes2 = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Abs(offsetAboutReflectorTimes2));
+ MUInt15 lookupIndex = ParallelMath::RightShift(absOffsetAboutReflectorTimes2, 1);
+
+ MUInt15 positiveIndex;
+ MUInt15 positiveOffsetUnmultiplied;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t blockLookupIndex = ParallelMath::Extract(lookupIndex, block) / ParallelMath::Extract(multiplier, block);
+ if (blockLookupIndex >= Tables::ETC2::g_alphaRoundingTableWidth)
+ blockLookupIndex = Tables::ETC2::g_alphaRoundingTableWidth - 1;
+ uint16_t index = Tables::ETC2::g_alphaRoundingTables[tableIndex][blockLookupIndex];
+ ParallelMath::PutUInt15(positiveIndex, block, index);
+ ParallelMath::PutUInt15(positiveOffsetUnmultiplied, block, Tables::ETC2::g_alphaModifierTablePositive[tableIndex][index]);
+
+ // TODO: This is suboptimal when the offset is capped. We should detect 0 and 255 values and always map them to the maximum offsets.
+ // Doing that will also affect refinement though.
+ }
+
+ MSInt16 signBits = ParallelMath::RightShift(offsetAboutReflectorTimes2, 15);
+ MSInt16 offsetUnmultiplied = ParallelMath::LosslessCast<MSInt16>::Cast(positiveOffsetUnmultiplied) ^ signBits;
+ MSInt16 quantizedOffset = ParallelMath::CompactMultiply(offsetUnmultiplied, multiplier);
+
+ MSInt16 offsetValue = ParallelMath::LosslessCast<MSInt16>::Cast(baseValue) + quantizedOffset;
+
+ if (is11Bit)
+ {
+ if (isSigned)
+ outQuantizedValues = ParallelMath::Min(ParallelMath::MakeUInt15(2047), ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(1), offsetValue)));
+ else
+ outQuantizedValues = ParallelMath::Min(ParallelMath::MakeUInt15(2047), ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), offsetValue)));
+ }
+ else
+ outQuantizedValues = ParallelMath::Min(ParallelMath::MakeUInt15(255), ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::Max(ParallelMath::MakeSInt16(0), offsetValue)));
+
+ MUInt15 indexSub = ParallelMath::LosslessCast<MUInt15>::Cast(signBits) & ParallelMath::MakeUInt15(4);
+
+ outIndexes = positiveIndex + ParallelMath::MakeUInt15(4) - indexSub;
+}
+
+
+void cvtt::Internal::ETCComputer::EmitTModeBlock(uint8_t *outputBuffer, const ParallelMath::ScalarUInt16 lineColor[3], const ParallelMath::ScalarUInt16 isolatedColor[3], int32_t packedSelectors, ParallelMath::ScalarUInt16 table, bool opaque)
+{
+ static const int selectorOrder[] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+ uint32_t lowBits = 0;
+ uint32_t highBits = 0;
+
+ int rh = ((isolatedColor[0] >> 2) & 3);
+ int rl = (isolatedColor[0] & 3);
+
+ if (rh + rl < 4)
+ {
+ // Overflow low
+ highBits |= 1 << (58 - 32);
+ }
+ else
+ {
+ // Overflow high
+ highBits |= 7 << (61 - 32);
+ }
+
+ highBits |= rh << (59 - 32);
+ highBits |= rl << (56 - 32);
+ highBits |= isolatedColor[1] << (52 - 32);
+ highBits |= isolatedColor[2] << (48 - 32);
+ highBits |= lineColor[0] << (44 - 32);
+ highBits |= lineColor[1] << (40 - 32);
+ highBits |= lineColor[2] << (36 - 32);
+ highBits |= ((table >> 1) & 3) << (34 - 32);
+ if (opaque)
+ highBits |= 1 << (33 - 32);
+ highBits |= (table & 1) << (32 - 32);
+
+ for (int px = 0; px < 16; px++)
+ {
+ int sel = (packedSelectors >> (2 * selectorOrder[px])) & 3;
+ if ((sel & 0x1) != 0)
+ lowBits |= (1 << px);
+ if ((sel & 0x2) != 0)
+ lowBits |= (1 << (16 + px));
+ }
+
+ for (int i = 0; i < 4; i++)
+ outputBuffer[i] = (highBits >> (24 - i * 8)) & 0xff;
+ for (int i = 0; i < 4; i++)
+ outputBuffer[i + 4] = (lowBits >> (24 - i * 8)) & 0xff;
+}
+
+void cvtt::Internal::ETCComputer::EmitHModeBlock(uint8_t *outputBuffer, const ParallelMath::ScalarUInt16 blockColors[2], ParallelMath::ScalarUInt16 sectorBits, ParallelMath::ScalarUInt16 signBits, ParallelMath::ScalarUInt16 table, bool opaque)
+{
+ if (blockColors[0] == blockColors[1])
+ {
+ // Base colors are the same.
+ // If the table low bit isn't 1, then we can't encode this, because swapping the block colors will have no effect
+ // on their order.
+ // Instead, we encode this as T mode where all of the indexes are on the line.
+
+ ParallelMath::ScalarUInt16 lineColor[3];
+ ParallelMath::ScalarUInt16 isolatedColor[3];
+
+ lineColor[0] = isolatedColor[0] = (blockColors[0] >> 10) & 0x1f;
+ lineColor[1] = isolatedColor[1] = (blockColors[0] >> 5) & 0x1f;
+ lineColor[2] = isolatedColor[2] = (blockColors[0] >> 0) & 0x1f;
+
+ int32_t packedSelectors = 0x55555555;
+ for (int px = 0; px < 16; px++)
+ packedSelectors |= ((signBits >> px) & 1) << ((px * 2) + 1);
+
+ EmitTModeBlock(outputBuffer, lineColor, isolatedColor, packedSelectors, table, opaque);
+ return;
+ }
+
+ static const int selectorOrder[] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+ int16_t colors[2][3];
+ for (int sector = 0; sector < 2; sector++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ colors[sector][ch] = (blockColors[sector] >> ((2 - ch) * 5)) & 15;
+ }
+
+ uint32_t lowBits = 0;
+ uint32_t highBits = 0;
+
+ if (((table & 1) == 1) != (blockColors[0] > blockColors[1]))
+ {
+ for (int ch = 0; ch < 3; ch++)
+ std::swap(colors[0][ch], colors[1][ch]);
+ sectorBits ^= 0xffff;
+ }
+
+ int r1 = colors[0][0];
+ int g1a = colors[0][1] >> 1;
+ int g1b = (colors[0][1] & 1);
+ int b1a = colors[0][2] >> 3;
+ int b1b = colors[0][2] & 7;
+ int r2 = colors[1][0];
+ int g2 = colors[1][1];
+ int b2 = colors[1][2];
+
+ // Avoid overflowing R
+ if ((g1a & 4) != 0 && r1 + g1a < 8)
+ highBits |= 1 << (63 - 32);
+
+ int fakeDG = b1b >> 1;
+ int fakeG = b1a | (g1b << 1);
+
+ if (fakeG + fakeDG < 4)
+ {
+ // Overflow low
+ highBits |= 1 << (50 - 32);
+ }
+ else
+ {
+ // Overflow high
+ highBits |= 7 << (53 - 32);
+ }
+
+ int da = (table >> 2) & 1;
+ int db = (table >> 1) & 1;
+
+ highBits |= r1 << (59 - 32);
+ highBits |= g1a << (56 - 32);
+ highBits |= g1b << (52 - 32);
+ highBits |= b1a << (51 - 32);
+ highBits |= b1b << (47 - 32);
+ highBits |= r2 << (43 - 32);
+ highBits |= g2 << (39 - 32);
+ highBits |= b2 << (35 - 32);
+ highBits |= da << (34 - 32);
+ if (opaque)
+ highBits |= 1 << (33 - 32);
+ highBits |= db << (32 - 32);
+
+ for (int px = 0; px < 16; px++)
+ {
+ int sectorBit = (sectorBits >> selectorOrder[px]) & 1;
+ int signBit = (signBits >> selectorOrder[px]) & 1;
+
+ lowBits |= (signBit << px);
+ lowBits |= (sectorBit << (16 + px));
+ }
+
+ uint8_t *output = outputBuffer;
+
+ for (int i = 0; i < 4; i++)
+ output[i] = (highBits >> (24 - i * 8)) & 0xff;
+ for (int i = 0; i < 4; i++)
+ output[i + 4] = (lowBits >> (24 - i * 8)) & 0xff;
+}
+
+void cvtt::Internal::ETCComputer::EmitETC1Block(uint8_t *outputBuffer, int blockBestFlip, int blockBestD, const int blockBestColors[2][3], const int blockBestTables[2], const ParallelMath::ScalarUInt16 blockBestSelectors[2], bool transparent)
+{
+ uint32_t highBits = 0;
+ uint32_t lowBits = 0;
+
+ if (blockBestD == 0)
+ {
+ highBits |= blockBestColors[0][0] << 28;
+ highBits |= blockBestColors[1][0] << 24;
+ highBits |= blockBestColors[0][1] << 20;
+ highBits |= blockBestColors[1][1] << 16;
+ highBits |= blockBestColors[0][2] << 12;
+ highBits |= blockBestColors[1][2] << 8;
+ }
+ else
+ {
+ highBits |= blockBestColors[0][0] << 27;
+ highBits |= ((blockBestColors[1][0] - blockBestColors[0][0]) & 7) << 24;
+ highBits |= blockBestColors[0][1] << 19;
+ highBits |= ((blockBestColors[1][1] - blockBestColors[0][1]) & 7) << 16;
+ highBits |= blockBestColors[0][2] << 11;
+ highBits |= ((blockBestColors[1][2] - blockBestColors[0][2]) & 7) << 8;
+ }
+
+ highBits |= (blockBestTables[0] << 5);
+ highBits |= (blockBestTables[1] << 2);
+ if (!transparent)
+ highBits |= (blockBestD << 1);
+ highBits |= blockBestFlip;
+
+ const uint8_t modifierCodes[4] = { 3, 2, 0, 1 };
+
+ uint8_t unpackedSelectors[16];
+ uint8_t unpackedSelectorCodes[16];
+ for (int sector = 0; sector < 2; sector++)
+ {
+ int blockSectorBestSelectors = blockBestSelectors[sector];
+
+ for (int px = 0; px < 8; px++)
+ {
+ int selector = (blockSectorBestSelectors >> (2 * px)) & 3;
+ unpackedSelectorCodes[g_flipTables[blockBestFlip][sector][px]] = modifierCodes[selector];
+ unpackedSelectors[g_flipTables[blockBestFlip][sector][px]] = selector;
+ }
+ }
+
+ const int pixelSelectorOrder[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+ int lowBitOffset = 0;
+ for (int sb = 0; sb < 2; sb++)
+ for (int px = 0; px < 16; px++)
+ lowBits |= ((unpackedSelectorCodes[pixelSelectorOrder[px]] >> sb) & 1) << (px + sb * 16);
+
+ for (int i = 0; i < 4; i++)
+ outputBuffer[i] = (highBits >> (24 - i * 8)) & 0xff;
+ for (int i = 0; i < 4; i++)
+ outputBuffer[i + 4] = (lowBits >> (24 - i * 8)) & 0xff;
+}
+
+void cvtt::Internal::ETCComputer::CompressETC1BlockInternal(MFloat &bestTotalError, uint8_t *outputBuffer, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], DifferentialResolveStorage &drs, const Options &options, bool punchthrough)
+{
+ int numTries = 0;
+
+ MUInt15 zeroU15 = ParallelMath::MakeUInt15(0);
+ MUInt16 zeroU16 = ParallelMath::MakeUInt16(0);
+
+ MUInt15 bestColors[2] = { zeroU15, zeroU15 };
+ MUInt16 bestSelectors[2] = { zeroU16, zeroU16 };
+ MUInt15 bestTables[2] = { zeroU15, zeroU15 };
+ MUInt15 bestFlip = zeroU15;
+ MUInt15 bestD = zeroU15;
+
+ MUInt15 sectorPixels[2][2][8][3];
+ MFloat sectorPreWeightedPixels[2][2][8][3];
+ MUInt15 sectorCumulative[2][2][3];
+
+ ParallelMath::Int16CompFlag bestIsThisMode = ParallelMath::MakeBoolInt16(false);
+
+ for (int flip = 0; flip < 2; flip++)
+ {
+ for (int sector = 0; sector < 2; sector++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ sectorCumulative[flip][sector][ch] = zeroU15;
+
+ for (int px = 0; px < 8; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 pixelChannelValue = pixels[g_flipTables[flip][sector][px]][ch];
+ sectorPixels[flip][sector][px][ch] = pixelChannelValue;
+ sectorPreWeightedPixels[flip][sector][px][ch] = preWeightedPixels[g_flipTables[flip][sector][px]][ch];
+ sectorCumulative[flip][sector][ch] = sectorCumulative[flip][sector][ch] + pixelChannelValue;
+ }
+ }
+ }
+ }
+
+ static const MSInt16 modifierTables[8][4] =
+ {
+ { ParallelMath::MakeSInt16(-8), ParallelMath::MakeSInt16(-2), ParallelMath::MakeSInt16(2), ParallelMath::MakeSInt16(8) },
+ { ParallelMath::MakeSInt16(-17), ParallelMath::MakeSInt16(-5), ParallelMath::MakeSInt16(5), ParallelMath::MakeSInt16(17) },
+ { ParallelMath::MakeSInt16(-29), ParallelMath::MakeSInt16(-9), ParallelMath::MakeSInt16(9), ParallelMath::MakeSInt16(29) },
+ { ParallelMath::MakeSInt16(-42), ParallelMath::MakeSInt16(-13), ParallelMath::MakeSInt16(13), ParallelMath::MakeSInt16(42) },
+ { ParallelMath::MakeSInt16(-60), ParallelMath::MakeSInt16(-18), ParallelMath::MakeSInt16(18), ParallelMath::MakeSInt16(60) },
+ { ParallelMath::MakeSInt16(-80), ParallelMath::MakeSInt16(-24), ParallelMath::MakeSInt16(24), ParallelMath::MakeSInt16(80) },
+ { ParallelMath::MakeSInt16(-106), ParallelMath::MakeSInt16(-33), ParallelMath::MakeSInt16(33), ParallelMath::MakeSInt16(106) },
+ { ParallelMath::MakeSInt16(-183), ParallelMath::MakeSInt16(-47), ParallelMath::MakeSInt16(47), ParallelMath::MakeSInt16(183) },
+ };
+
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ int minD = punchthrough ? 1 : 0;
+
+ for (int flip = 0; flip < 2; flip++)
+ {
+ drs.diffNumAttempts[0] = drs.diffNumAttempts[1] = zeroU15;
+
+ MFloat bestIndError[2] = { ParallelMath::MakeFloat(FLT_MAX), ParallelMath::MakeFloat(FLT_MAX) };
+ MUInt16 bestIndSelectors[2] = { ParallelMath::MakeUInt16(0), ParallelMath::MakeUInt16(0) };
+ MUInt15 bestIndColors[2] = { zeroU15, zeroU15 };
+ MUInt15 bestIndTable[2] = { zeroU15, zeroU15 };
+
+ for (int d = minD; d < 2; d++)
+ {
+ for (int sector = 0; sector < 2; sector++)
+ {
+ const int16_t *potentialOffsets = cvtt::Tables::ETC1::g_potentialOffsets4;
+
+ for (int table = 0; table < 8; table++)
+ {
+ int16_t numOffsets = *potentialOffsets++;
+
+ MUInt15 possibleColors[cvtt::Tables::ETC1::g_maxPotentialOffsets];
+
+ MUInt15 quantized[3];
+ for (int oi = 0; oi < numOffsets; oi++)
+ {
+ if (!isFakeBT709)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ // cu is in range 0..2040
+ MUInt15 cu15 = ParallelMath::Min(
+ ParallelMath::MakeUInt15(2040),
+ ParallelMath::ToUInt15(
+ ParallelMath::Max(
+ ParallelMath::MakeSInt16(0),
+ ParallelMath::LosslessCast<MSInt16>::Cast(sectorCumulative[flip][sector][ch]) + ParallelMath::MakeSInt16(potentialOffsets[oi])
+ )
+ )
+ );
+
+ if (d == 1)
+ {
+ //quantized[ch] = (cu * 31 + (cu >> 3) + 1024) >> 11;
+ quantized[ch] = ParallelMath::ToUInt15(
+ ParallelMath::RightShift(
+ (ParallelMath::LosslessCast<MUInt16>::Cast(cu15) << 5) - ParallelMath::LosslessCast<MUInt16>::Cast(cu15) + ParallelMath::LosslessCast<MUInt16>::Cast(ParallelMath::RightShift(cu15, 3)) + ParallelMath::MakeUInt16(1024)
+ , 11)
+ );
+ }
+ else
+ {
+ //quantized[ch] = (cu * 30 + (cu >> 3) + 2048) >> 12;
+ quantized[ch] = ParallelMath::ToUInt15(
+ ParallelMath::RightShift(
+ (ParallelMath::LosslessCast<MUInt16>::Cast(cu15) << 5) - ParallelMath::LosslessCast<MUInt16>::Cast(cu15 << 1) + ParallelMath::LosslessCast<MUInt16>::Cast(ParallelMath::RightShift(cu15, 3)) + ParallelMath::MakeUInt16(2048)
+ , 12)
+ );
+ }
+ }
+ }
+ else
+ {
+ MUInt15 offsetCumulative[3];
+ for (int ch = 0; ch < 3; ch++)
+ {
+ // cu is in range 0..2040
+ MUInt15 cu15 = ParallelMath::Min(
+ ParallelMath::MakeUInt15(2040),
+ ParallelMath::ToUInt15(
+ ParallelMath::Max(
+ ParallelMath::MakeSInt16(0),
+ ParallelMath::LosslessCast<MSInt16>::Cast(sectorCumulative[flip][sector][ch]) + ParallelMath::MakeSInt16(potentialOffsets[oi])
+ )
+ )
+ );
+
+ offsetCumulative[ch] = cu15;
+ }
+
+ if ((options.flags & cvtt::Flags::ETC_FakeBT709Accurate) != 0)
+ ResolveHalfBlockFakeBT709RoundingAccurate(quantized, offsetCumulative, d == 1);
+ else
+ ResolveHalfBlockFakeBT709RoundingFast(quantized, offsetCumulative, d == 1);
+ }
+
+ possibleColors[oi] = quantized[0] | (quantized[1] << 5) | (quantized[2] << 10);
+ }
+
+ potentialOffsets += numOffsets;
+
+ ParallelMath::UInt15 numUniqueColors;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t blockNumUniqueColors = 1;
+ for (int i = 1; i < numOffsets; i++)
+ {
+ uint16_t color = ParallelMath::Extract(possibleColors[i], block);
+ if (color != ParallelMath::Extract(possibleColors[blockNumUniqueColors - 1], block))
+ ParallelMath::PutUInt15(possibleColors[blockNumUniqueColors++], block, color);
+ }
+
+ ParallelMath::PutUInt15(numUniqueColors, block, blockNumUniqueColors);
+ }
+
+ int maxUniqueColors = ParallelMath::Extract(numUniqueColors, 0);
+ for (int block = 1; block < ParallelMath::ParallelSize; block++)
+ maxUniqueColors = std::max<int>(maxUniqueColors, ParallelMath::Extract(numUniqueColors, block));
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t fillColor = ParallelMath::Extract(possibleColors[0], block);
+ for (int i = ParallelMath::Extract(numUniqueColors, block); i < maxUniqueColors; i++)
+ ParallelMath::PutUInt15(possibleColors[i], block, fillColor);
+ }
+
+ for (int i = 0; i < maxUniqueColors; i++)
+ {
+ MFloat error = ParallelMath::MakeFloatZero();
+ MUInt16 selectors = ParallelMath::MakeUInt16(0);
+ MUInt15 quantized = possibleColors[i];
+ TestHalfBlock(error, selectors, quantized, sectorPixels[flip][sector], sectorPreWeightedPixels[flip][sector], modifierTables[table], d == 1, options);
+
+ if (d == 0)
+ {
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(error, bestIndError[sector]));
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ bestIndError[sector] = ParallelMath::Min(error, bestIndError[sector]);
+ ParallelMath::ConditionalSet(bestIndSelectors[sector], errorBetter, selectors);
+ ParallelMath::ConditionalSet(bestIndColors[sector], errorBetter, quantized);
+ ParallelMath::ConditionalSet(bestIndTable[sector], errorBetter, ParallelMath::MakeUInt15(table));
+ }
+ }
+ else
+ {
+ ParallelMath::Int16CompFlag isInBounds = ParallelMath::Less(ParallelMath::MakeUInt15(i), numUniqueColors);
+
+ MUInt15 storageIndexes = drs.diffNumAttempts[sector];
+ drs.diffNumAttempts[sector] = drs.diffNumAttempts[sector] + ParallelMath::SelectOrZero(isInBounds, ParallelMath::MakeUInt15(1));
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int storageIndex = ParallelMath::Extract(storageIndexes, block);
+
+ ParallelMath::PutFloat(drs.diffErrors[sector][storageIndex], block, ParallelMath::Extract(error, block));
+ ParallelMath::PutUInt16(drs.diffSelectors[sector][storageIndex], block, ParallelMath::Extract(selectors, block));
+ ParallelMath::PutUInt15(drs.diffColors[sector][storageIndex], block, ParallelMath::Extract(quantized, block));
+ ParallelMath::PutUInt15(drs.diffTables[sector][storageIndex], block, table);
+ }
+ }
+ }
+ }
+ }
+
+ if (d == 0)
+ {
+ MFloat bestIndErrorTotal = bestIndError[0] + bestIndError[1];
+ ParallelMath::Int16CompFlag errorBetter = ParallelMath::FloatFlagToInt16(ParallelMath::Less(bestIndErrorTotal, bestTotalError));
+ if (ParallelMath::AnySet(errorBetter))
+ {
+ bestIsThisMode = bestIsThisMode | errorBetter;
+
+ bestTotalError = ParallelMath::Min(bestTotalError, bestIndErrorTotal);
+ ParallelMath::ConditionalSet(bestFlip, errorBetter, ParallelMath::MakeUInt15(flip));
+ ParallelMath::ConditionalSet(bestD, errorBetter, ParallelMath::MakeUInt15(d));
+ for (int sector = 0; sector < 2; sector++)
+ {
+ ParallelMath::ConditionalSet(bestColors[sector], errorBetter, bestIndColors[sector]);
+ ParallelMath::ConditionalSet(bestSelectors[sector], errorBetter, bestIndSelectors[sector]);
+ ParallelMath::ConditionalSet(bestTables[sector], errorBetter, bestIndTable[sector]);
+ }
+ }
+ }
+ else
+ {
+ ParallelMath::Int16CompFlag canIgnoreSector[2] = { ParallelMath::MakeBoolInt16(false), ParallelMath::MakeBoolInt16(false) };
+ FindBestDifferentialCombination(flip, d, canIgnoreSector, bestIsThisMode, bestTotalError, bestFlip, bestD, bestColors, bestSelectors, bestTables, drs);
+ }
+ }
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (!ParallelMath::Extract(bestIsThisMode, block))
+ continue;
+
+ uint32_t highBits = 0;
+ uint32_t lowBits = 0;
+
+ int blockBestFlip = ParallelMath::Extract(bestFlip, block);
+ int blockBestD = ParallelMath::Extract(bestD, block);
+ int blockBestTables[2] = { ParallelMath::Extract(bestTables[0], block), ParallelMath::Extract(bestTables[1], block) };
+ ParallelMath::ScalarUInt16 blockBestSelectors[2] = { ParallelMath::Extract(bestSelectors[0], block), ParallelMath::Extract(bestSelectors[1], block) };
+
+ int colors[2][3];
+ for (int sector = 0; sector < 2; sector++)
+ {
+ int sectorColor = ParallelMath::Extract(bestColors[sector], block);
+ for (int ch = 0; ch < 3; ch++)
+ colors[sector][ch] = (sectorColor >> (ch * 5)) & 31;
+ }
+
+ EmitETC1Block(outputBuffer + block * 8, blockBestFlip, blockBestD, colors, blockBestTables, blockBestSelectors, false);
+ }
+}
+
+
+void cvtt::Internal::ETCComputer::CompressETC1PunchthroughBlockInternal(MFloat &bestTotalError, uint8_t *outputBuffer, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const ParallelMath::Int16CompFlag isTransparent[16], DifferentialResolveStorage &drs, const Options &options)
+{
+ int numTries = 0;
+
+ MUInt15 zeroU15 = ParallelMath::MakeUInt15(0);
+ MUInt16 zeroU16 = ParallelMath::MakeUInt16(0);
+
+ MUInt15 bestColors[2] = { zeroU15, zeroU15 };
+ MUInt16 bestSelectors[2] = { zeroU16, zeroU16 };
+ MUInt15 bestTables[2] = { zeroU15, zeroU15 };
+ MUInt15 bestFlip = zeroU15;
+
+ MUInt15 sectorPixels[2][2][8][3];
+ ParallelMath::Int16CompFlag sectorTransparent[2][2][8];
+ MFloat sectorPreWeightedPixels[2][2][8][3];
+ MUInt15 sectorCumulative[2][2][3];
+
+ ParallelMath::Int16CompFlag bestIsThisMode = ParallelMath::MakeBoolInt16(false);
+
+ for (int flip = 0; flip < 2; flip++)
+ {
+ for (int sector = 0; sector < 2; sector++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ sectorCumulative[flip][sector][ch] = zeroU15;
+
+ for (int px = 0; px < 8; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ MUInt15 pixelChannelValue = pixels[g_flipTables[flip][sector][px]][ch];
+ sectorPixels[flip][sector][px][ch] = pixelChannelValue;
+ sectorPreWeightedPixels[flip][sector][px][ch] = preWeightedPixels[g_flipTables[flip][sector][px]][ch];
+ sectorCumulative[flip][sector][ch] = sectorCumulative[flip][sector][ch] + pixelChannelValue;
+ }
+
+ sectorTransparent[flip][sector][px] = isTransparent[g_flipTables[flip][sector][px]];
+ }
+ }
+ }
+
+ static const MUInt15 modifiers[8] =
+ {
+ ParallelMath::MakeUInt15(8),
+ ParallelMath::MakeUInt15(17),
+ ParallelMath::MakeUInt15(29),
+ ParallelMath::MakeUInt15(42),
+ ParallelMath::MakeUInt15(60),
+ ParallelMath::MakeUInt15(80),
+ ParallelMath::MakeUInt15(106),
+ ParallelMath::MakeUInt15(183),
+ };
+
+ bool isFakeBT709 = ((options.flags & cvtt::Flags::ETC_UseFakeBT709) != 0);
+
+ const int maxSectorCumulativeOffsets = 17;
+
+ for (int flip = 0; flip < 2; flip++)
+ {
+ ParallelMath::Int16CompFlag canIgnoreSector[2] = { ParallelMath::MakeBoolInt16(true), ParallelMath::MakeBoolInt16(false) };
+
+ for (int sector = 0; sector < 2; sector++)
+ for (int px = 0; px < 8; px++)
+ canIgnoreSector[sector] = canIgnoreSector[sector] & sectorTransparent[flip][sector][px];
+
+ drs.diffNumAttempts[0] = drs.diffNumAttempts[1] = zeroU15;
+
+ for (int sector = 0; sector < 2; sector++)
+ {
+ MUInt15 sectorNumOpaque = ParallelMath::MakeUInt15(0);
+ for (int px = 0; px < 8; px++)
+ sectorNumOpaque = sectorNumOpaque + ParallelMath::SelectOrZero(sectorTransparent[flip][sector][px], ParallelMath::MakeUInt15(1));
+
+ int sectorMaxOpaque = 0;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ sectorMaxOpaque = std::max<int>(sectorMaxOpaque, ParallelMath::Extract(sectorNumOpaque, block));
+
+ int sectorNumOpaqueMultipliers = sectorMaxOpaque * 2 + 1;
+
+ MUInt15 sectorNumOpaqueDenominator = ParallelMath::Max(ParallelMath::MakeUInt15(1), sectorNumOpaque) << 8;
+ MUInt15 sectorNumOpaqueAddend = sectorNumOpaque << 7;
+
+ MSInt16 sectorNumOpaqueSigned = ParallelMath::LosslessCast<MSInt16>::Cast(sectorNumOpaque);
+ MSInt16 negSectorNumOpaqueSigned = ParallelMath::MakeSInt16(0) - sectorNumOpaqueSigned;
+
+ MUInt15 sectorCumulativeMax = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(ParallelMath::MakeUInt15(255), sectorNumOpaque));
+
+ for (int table = 0; table < 8; table++)
+ {
+ MUInt15 possibleColors[maxSectorCumulativeOffsets];
+
+ MUInt15 quantized[3];
+ for (int om = -sectorMaxOpaque; om <= sectorMaxOpaque; om++)
+ {
+ MSInt16 clampedOffsetMult = ParallelMath::Max(ParallelMath::Min(ParallelMath::MakeSInt16(om), sectorNumOpaqueSigned), negSectorNumOpaqueSigned);
+ MSInt16 offset = ParallelMath::CompactMultiply(clampedOffsetMult, modifiers[table]);
+
+ for (int ch = 0; ch < 3; ch++)
+ {
+ // cu is in range 0..255*numOpaque (at most 0..2040)
+ MUInt15 cu15 = ParallelMath::Min(
+ sectorCumulativeMax,
+ ParallelMath::ToUInt15(
+ ParallelMath::Max(
+ ParallelMath::MakeSInt16(0),
+ ParallelMath::LosslessCast<MSInt16>::Cast(sectorCumulative[flip][sector][ch]) + offset
+ )
+ )
+ );
+
+ //quantized[ch] = (cu * 31 + (cu >> 3) + (numOpaque * 128)) / (numOpaque * 256)
+ MUInt16 cuTimes31 = (ParallelMath::LosslessCast<MUInt16>::Cast(cu15) << 5) - ParallelMath::LosslessCast<MUInt16>::Cast(cu15);
+ MUInt15 cuDiv8 = ParallelMath::RightShift(cu15, 3);
+ MUInt16 numerator = cuTimes31 + ParallelMath::LosslessCast<MUInt16>::Cast(cuDiv8 + sectorNumOpaqueAddend);
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ ParallelMath::PutUInt15(quantized[ch], block, ParallelMath::Extract(numerator, block) / ParallelMath::Extract(sectorNumOpaqueDenominator, block));
+ }
+
+ possibleColors[om + sectorMaxOpaque] = quantized[0] | (quantized[1] << 5) | (quantized[2] << 10);
+ }
+
+ ParallelMath::UInt15 numUniqueColors;
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t blockNumUniqueColors = 1;
+ for (int i = 1; i < sectorNumOpaqueMultipliers; i++)
+ {
+ uint16_t color = ParallelMath::Extract(possibleColors[i], block);
+ if (color != ParallelMath::Extract(possibleColors[blockNumUniqueColors - 1], block))
+ ParallelMath::PutUInt15(possibleColors[blockNumUniqueColors++], block, color);
+ }
+
+ ParallelMath::PutUInt15(numUniqueColors, block, blockNumUniqueColors);
+ }
+
+ int maxUniqueColors = ParallelMath::Extract(numUniqueColors, 0);
+ for (int block = 1; block < ParallelMath::ParallelSize; block++)
+ maxUniqueColors = std::max<int>(maxUniqueColors, ParallelMath::Extract(numUniqueColors, block));
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ uint16_t fillColor = ParallelMath::Extract(possibleColors[0], block);
+ for (int i = ParallelMath::Extract(numUniqueColors, block); i < maxUniqueColors; i++)
+ ParallelMath::PutUInt15(possibleColors[i], block, fillColor);
+ }
+
+ for (int i = 0; i < maxUniqueColors; i++)
+ {
+ MFloat error = ParallelMath::MakeFloatZero();
+ MUInt16 selectors = ParallelMath::MakeUInt16(0);
+ MUInt15 quantized = possibleColors[i];
+ TestHalfBlockPunchthrough(error, selectors, quantized, sectorPixels[flip][sector], sectorPreWeightedPixels[flip][sector], sectorTransparent[flip][sector], modifiers[table], options);
+
+ ParallelMath::Int16CompFlag isInBounds = ParallelMath::Less(ParallelMath::MakeUInt15(i), numUniqueColors);
+
+ MUInt15 storageIndexes = drs.diffNumAttempts[sector];
+ drs.diffNumAttempts[sector] = drs.diffNumAttempts[sector] + ParallelMath::SelectOrZero(isInBounds, ParallelMath::MakeUInt15(1));
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int storageIndex = ParallelMath::Extract(storageIndexes, block);
+
+ ParallelMath::PutFloat(drs.diffErrors[sector][storageIndex], block, ParallelMath::Extract(error, block));
+ ParallelMath::PutUInt16(drs.diffSelectors[sector][storageIndex], block, ParallelMath::Extract(selectors, block));
+ ParallelMath::PutUInt15(drs.diffColors[sector][storageIndex], block, ParallelMath::Extract(quantized, block));
+ ParallelMath::PutUInt15(drs.diffTables[sector][storageIndex], block, table);
+ }
+ }
+ }
+ }
+
+ MUInt15 bestDDummy = ParallelMath::MakeUInt15(0);
+ FindBestDifferentialCombination(flip, 1, canIgnoreSector, bestIsThisMode, bestTotalError, bestFlip, bestDDummy, bestColors, bestSelectors, bestTables, drs);
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ if (!ParallelMath::Extract(bestIsThisMode, block))
+ continue;
+
+ int blockBestColors[2][3];
+ int blockBestTables[2];
+ ParallelMath::ScalarUInt16 blockBestSelectors[2];
+ for (int sector = 0; sector < 2; sector++)
+ {
+ int sectorColor = ParallelMath::Extract(bestColors[sector], block);
+ for (int ch = 0; ch < 3; ch++)
+ blockBestColors[sector][ch] = (sectorColor >> (ch * 5)) & 31;
+
+ blockBestTables[sector] = ParallelMath::Extract(bestTables[sector], block);
+ blockBestSelectors[sector] = ParallelMath::Extract(bestSelectors[sector], block);
+ }
+
+ EmitETC1Block(outputBuffer + block * 8, ParallelMath::Extract(bestFlip, block), 1, blockBestColors, blockBestTables, blockBestSelectors, true);
+ }
+}
+
+
+cvtt::ETC1CompressionData *cvtt::Internal::ETCComputer::AllocETC1Data(cvtt::Kernels::allocFunc_t allocFunc, void *context)
+{
+ void *buffer = allocFunc(context, sizeof(cvtt::Internal::ETCComputer::ETC1CompressionDataInternal));
+ if (!buffer)
+ return NULL;
+ new (buffer) cvtt::Internal::ETCComputer::ETC1CompressionDataInternal(context);
+ return static_cast<ETC1CompressionData*>(buffer);
+}
+
+void cvtt::Internal::ETCComputer::ReleaseETC1Data(ETC1CompressionData *compressionData, cvtt::Kernels::freeFunc_t freeFunc)
+{
+ cvtt::Internal::ETCComputer::ETC1CompressionDataInternal* internalData = static_cast<cvtt::Internal::ETCComputer::ETC1CompressionDataInternal*>(compressionData);
+ void *context = internalData->m_context;
+ internalData->~ETC1CompressionDataInternal();
+ freeFunc(context, compressionData, sizeof(cvtt::Internal::ETCComputer::ETC1CompressionDataInternal));
+}
+
+cvtt::ETC2CompressionData *cvtt::Internal::ETCComputer::AllocETC2Data(cvtt::Kernels::allocFunc_t allocFunc, void *context, const cvtt::Options &options)
+{
+ void *buffer = allocFunc(context, sizeof(cvtt::Internal::ETCComputer::ETC2CompressionDataInternal));
+ if (!buffer)
+ return NULL;
+ new (buffer) cvtt::Internal::ETCComputer::ETC2CompressionDataInternal(context, options);
+ return static_cast<ETC2CompressionData*>(buffer);
+}
+
+void cvtt::Internal::ETCComputer::ReleaseETC2Data(ETC2CompressionData *compressionData, cvtt::Kernels::freeFunc_t freeFunc)
+{
+ cvtt::Internal::ETCComputer::ETC2CompressionDataInternal* internalData = static_cast<cvtt::Internal::ETCComputer::ETC2CompressionDataInternal*>(compressionData);
+ void *context = internalData->m_context;
+ internalData->~ETC2CompressionDataInternal();
+ freeFunc(context, compressionData, sizeof(cvtt::Internal::ETCComputer::ETC2CompressionDataInternal));
+}
+
+cvtt::Internal::ETCComputer::ETC2CompressionDataInternal::ETC2CompressionDataInternal(void *context, const cvtt::Options &options)
+ : m_context(context)
+{
+ const float cd[3] = { options.redWeight, options.greenWeight, options.blueWeight };
+ const float rotCD[3] = { cd[1], cd[2], cd[0] };
+
+ const float offs = -(rotCD[0] * cd[0] + rotCD[1] * cd[1] + rotCD[2] * cd[2]) / (cd[0] * cd[0] + cd[1] * cd[1] + cd[2] * cd[2]);
+
+ const float chromaAxis0[3] = { rotCD[0] + cd[0] * offs, rotCD[1] + cd[1] * offs, rotCD[2] + cd[2] * offs };
+
+ const float chromaAxis1Unnormalized[3] =
+ {
+ chromaAxis0[1] * cd[2] - chromaAxis0[2] * cd[1],
+ chromaAxis0[2] * cd[0] - chromaAxis0[0] * cd[2],
+ chromaAxis0[0] * cd[1] - chromaAxis0[1] * cd[0]
+ };
+
+ const float ca0LengthSq = (chromaAxis0[0] * chromaAxis0[0] + chromaAxis0[1] * chromaAxis0[1] + chromaAxis0[2] * chromaAxis0[2]);
+ const float ca1UNLengthSq = (chromaAxis1Unnormalized[0] * chromaAxis1Unnormalized[0] + chromaAxis1Unnormalized[1] * chromaAxis1Unnormalized[1] + chromaAxis1Unnormalized[2] * chromaAxis1Unnormalized[2]);
+ const float lengthRatio = static_cast<float>(std::sqrt(ca0LengthSq / ca1UNLengthSq));
+
+ const float chromaAxis1[3] = { chromaAxis1Unnormalized[0] * lengthRatio, chromaAxis1Unnormalized[1] * lengthRatio, chromaAxis1Unnormalized[2] * lengthRatio };
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_chromaSideAxis0[i] = chromaAxis0[i];
+ m_chromaSideAxis1[i] = chromaAxis1[i];
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_ETC.h b/thirdparty/cvtt/ConvectionKernels_ETC.h
new file mode 100644
index 0000000000..5e3c4d74fd
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ETC.h
@@ -0,0 +1,126 @@
+#pragma once
+#ifndef __CVTT_CONVECTIONKERNELS_ETC_H__
+#define __CVTT_CONVECTIONKERNELS_ETC_H__
+
+#include "ConvectionKernels.h"
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ struct Options;
+
+ namespace Internal
+ {
+ class ETCComputer
+ {
+ public:
+ static void CompressETC1Block(uint8_t *outputBuffer, const PixelBlockU8 *inputBlocks, ETC1CompressionData *compressionData, const Options &options);
+ static void CompressETC2Block(uint8_t *outputBuffer, const PixelBlockU8 *inputBlocks, ETC2CompressionData *compressionData, const Options &options, bool punchthroughAlpha);
+ static void CompressETC2AlphaBlock(uint8_t *outputBuffer, const PixelBlockU8 *inputBlocks, const Options &options);
+ static void CompressEACBlock(uint8_t *outputBuffer, const PixelBlockScalarS16 *inputBlocks, bool isSigned, const Options &options);
+
+ static ETC2CompressionData *AllocETC2Data(cvtt::Kernels::allocFunc_t allocFunc, void *context, const cvtt::Options &options);
+ static void ReleaseETC2Data(ETC2CompressionData *compressionData, cvtt::Kernels::freeFunc_t freeFunc);
+
+ static ETC1CompressionData *AllocETC1Data(cvtt::Kernels::allocFunc_t allocFunc, void *context);
+ static void ReleaseETC1Data(ETC1CompressionData *compressionData, cvtt::Kernels::freeFunc_t freeFunc);
+
+ private:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+ typedef ParallelMath::UInt31 MUInt31;
+
+ struct DifferentialResolveStorage
+ {
+ static const unsigned int MaxAttemptsPerSector = 57 + 81 + 81 + 81 + 81 + 81 + 81 + 81;
+
+ MUInt15 diffNumAttempts[2];
+ MFloat diffErrors[2][MaxAttemptsPerSector];
+ MUInt16 diffSelectors[2][MaxAttemptsPerSector];
+ MUInt15 diffColors[2][MaxAttemptsPerSector];
+ MUInt15 diffTables[2][MaxAttemptsPerSector];
+
+ uint16_t attemptSortIndexes[2][MaxAttemptsPerSector];
+ };
+
+ struct HModeEval
+ {
+ MFloat errors[62][16];
+ MUInt16 signBits[62];
+ MUInt15 uniqueQuantizedColors[62];
+ MUInt15 numUniqueColors[2];
+ };
+
+ struct ETC1CompressionDataInternal : public cvtt::ETC1CompressionData
+ {
+ explicit ETC1CompressionDataInternal(void *context)
+ : m_context(context)
+ {
+ }
+
+ DifferentialResolveStorage m_drs;
+ void *m_context;
+ };
+
+ struct ETC2CompressionDataInternal : public cvtt::ETC2CompressionData
+ {
+ explicit ETC2CompressionDataInternal(void *context, const cvtt::Options &options);
+
+ HModeEval m_h;
+ DifferentialResolveStorage m_drs;
+
+ void *m_context;
+ float m_chromaSideAxis0[3];
+ float m_chromaSideAxis1[3];
+ };
+
+ static MFloat ComputeErrorUniform(const MUInt15 pixelA[3], const MUInt15 pixelB[3]);
+ static MFloat ComputeErrorWeighted(const MUInt15 reconstructed[3], const MFloat pixelB[3], const Options options);
+ static MFloat ComputeErrorFakeBT709(const MUInt15 reconstructed[3], const MFloat pixelB[3]);
+
+ static void TestHalfBlock(MFloat &outError, MUInt16 &outSelectors, MUInt15 quantizedPackedColor, const MUInt15 pixels[8][3], const MFloat preWeightedPixels[8][3], const MSInt16 modifiers[4], bool isDifferential, const Options &options);
+ static void TestHalfBlockPunchthrough(MFloat &outError, MUInt16 &outSelectors, MUInt15 quantizedPackedColor, const MUInt15 pixels[8][3], const MFloat preWeightedPixels[8][3], const ParallelMath::Int16CompFlag isTransparent[8], const MUInt15 modifier, const Options &options);
+ static void FindBestDifferentialCombination(int flip, int d, const ParallelMath::Int16CompFlag canIgnoreSector[2], ParallelMath::Int16CompFlag& bestIsThisMode, MFloat& bestTotalError, MUInt15& bestFlip, MUInt15& bestD, MUInt15 bestColors[2], MUInt16 bestSelectors[2], MUInt15 bestTables[2], DifferentialResolveStorage &drs);
+
+ static ParallelMath::Int16CompFlag ETCDifferentialIsLegalForChannel(const MUInt15 &a, const MUInt15 &b);
+ static ParallelMath::Int16CompFlag ETCDifferentialIsLegal(const MUInt15 &a, const MUInt15 &b);
+ static bool ETCDifferentialIsLegalForChannelScalar(const uint16_t &a, const uint16_t &b);
+ static bool ETCDifferentialIsLegalScalar(const uint16_t &a, const uint16_t &b);
+
+ static void EncodeTMode(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag isIsolated[16], const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const Options &options);
+ static void EncodeHMode(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag groupings[16], const MUInt15 pixels[16][3], HModeEval &he, const MFloat preWeightedPixels[16][3], const Options &options);
+
+ static void EncodeVirtualTModePunchthrough(uint8_t *outputBuffer, MFloat &bestError, const ParallelMath::Int16CompFlag isIsolated[16], const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const ParallelMath::Int16CompFlag isTransparent[16], const ParallelMath::Int16CompFlag& anyTransparent, const ParallelMath::Int16CompFlag& allTransparent, const Options &options);
+
+ static MUInt15 DecodePlanarCoeff(const MUInt15 &coeff, int ch);
+ static void EncodePlanar(uint8_t *outputBuffer, MFloat &bestError, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const Options &options);
+
+ static void CompressETC1BlockInternal(MFloat &bestTotalError, uint8_t *outputBuffer, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], DifferentialResolveStorage& compressionData, const Options &options, bool punchthrough);
+ static void CompressETC1PunchthroughBlockInternal(MFloat &bestTotalError, uint8_t *outputBuffer, const MUInt15 pixels[16][3], const MFloat preWeightedPixels[16][3], const ParallelMath::Int16CompFlag isTransparent[16], DifferentialResolveStorage& compressionData, const Options &options);
+ static void CompressETC2AlphaBlockInternal(uint8_t *outputBuffer, const MUInt15 pixels[16], bool is11Bit, bool isSigned, const Options &options);
+
+ static void ExtractBlocks(MUInt15 pixels[16][3], MFloat preWeightedPixels[16][3], const PixelBlockU8 *inputBlocks, const Options &options);
+
+ static void ResolveHalfBlockFakeBT709RoundingAccurate(MUInt15 quantized[3], const MUInt15 sectorCumulative[3], bool isDifferential);
+ static void ResolveHalfBlockFakeBT709RoundingFast(MUInt15 quantized[3], const MUInt15 sectorCumulative[3], bool isDifferential);
+ static void ResolveTHFakeBT709Rounding(MUInt15 quantized[3], const MUInt15 target[3], const MUInt15 &granularity);
+ static void ConvertToFakeBT709(MFloat yuv[3], const MUInt15 color[3]);
+ static void ConvertToFakeBT709(MFloat yuv[3], const MFloat color[3]);
+ static void ConvertToFakeBT709(MFloat yuv[3], const MFloat &r, const MFloat &g, const MFloat &b);
+ static void ConvertFromFakeBT709(MFloat rgb[3], const MFloat yuv[3]);
+
+ static void QuantizeETC2Alpha(int tableIndex, const MUInt15& value, const MUInt15& baseValue, const MUInt15& multiplier, bool is11Bit, bool isSigned, MUInt15& outIndexes, MUInt15& outQuantizedValues);
+
+ static void EmitTModeBlock(uint8_t *outputBuffer, const ParallelMath::ScalarUInt16 lineColor[3], const ParallelMath::ScalarUInt16 isolatedColor[3], int32_t packedSelectors, ParallelMath::ScalarUInt16 table, bool opaque);
+ static void EmitHModeBlock(uint8_t *outputBuffer, const ParallelMath::ScalarUInt16 blockColors[2], ParallelMath::ScalarUInt16 sectorBits, ParallelMath::ScalarUInt16 signBits, ParallelMath::ScalarUInt16 table, bool opaque);
+ static void EmitETC1Block(uint8_t *outputBuffer, int blockBestFlip, int blockBestD, const int blockBestColors[2][3], const int blockBestTables[2], const ParallelMath::ScalarUInt16 blockBestSelectors[2], bool transparent);
+
+ static const int g_flipTables[2][2][8];
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_ETC1.h b/thirdparty/cvtt/ConvectionKernels_ETC1.h
new file mode 100644
index 0000000000..775e41669f
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ETC1.h
@@ -0,0 +1,29 @@
+#include <stdint.h>
+
+namespace cvtt
+{
+ namespace Tables
+ {
+ namespace ETC1
+ {
+ const int16_t g_potentialOffsets4[] =
+ {
+ 57, -64, -58, -54, -52, -48, -46, -44, -42, -40, -38, -36, -34, -32, -30, -28, -26, -24, -22, -20, -18, -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 52, 54, 58, 64,
+ 81, -136, -124, -114, -112, -102, -100, -92, -90, -88, -80, -78, -76, -70, -68, -66, -64, -58, -56, -54, -52, -48, -46, -44, -42, -40, -36, -34, -32, -30, -26, -24, -22, -20, -18, -14, -12, -10, -8, -4, -2, 0, 2, 4, 8, 10, 12, 14, 18, 20, 22, 24, 26, 30, 32, 34, 36, 40, 42, 44, 46, 48, 52, 54, 56, 58, 64, 66, 68, 70, 76, 78, 80, 88, 90, 92, 100, 102, 112, 114, 124, 136,
+ 81, -232, -212, -194, -192, -174, -172, -156, -154, -152, -136, -134, -132, -118, -116, -114, -112, -98, -96, -94, -92, -80, -78, -76, -74, -72, -60, -58, -56, -54, -42, -40, -38, -36, -34, -22, -20, -18, -16, -4, -2, 0, 2, 4, 16, 18, 20, 22, 34, 36, 38, 40, 42, 54, 56, 58, 60, 72, 74, 76, 78, 80, 92, 94, 96, 98, 112, 114, 116, 118, 132, 134, 136, 152, 154, 156, 172, 174, 192, 194, 212, 232,
+ 81, -336, -307, -281, -278, -252, -249, -226, -223, -220, -197, -194, -191, -171, -168, -165, -162, -142, -139, -136, -133, -116, -113, -110, -107, -104, -87, -84, -81, -78, -61, -58, -55, -52, -49, -32, -29, -26, -23, -6, -3, 0, 3, 6, 23, 26, 29, 32, 49, 52, 55, 58, 61, 78, 81, 84, 87, 104, 107, 110, 113, 116, 133, 136, 139, 142, 162, 165, 168, 171, 191, 194, 197, 220, 223, 226, 249, 252, 278, 281, 307, 336,
+ 81, -480, -438, -402, -396, -360, -354, -324, -318, -312, -282, -276, -270, -246, -240, -234, -228, -204, -198, -192, -186, -168, -162, -156, -150, -144, -126, -120, -114, -108, -90, -84, -78, -72, -66, -48, -42, -36, -30, -12, -6, 0, 6, 12, 30, 36, 42, 48, 66, 72, 78, 84, 90, 108, 114, 120, 126, 144, 150, 156, 162, 168, 186, 192, 198, 204, 228, 234, 240, 246, 270, 276, 282, 312, 318, 324, 354, 360, 396, 402, 438, 480,
+ 81, -640, -584, -536, -528, -480, -472, -432, -424, -416, -376, -368, -360, -328, -320, -312, -304, -272, -264, -256, -248, -224, -216, -208, -200, -192, -168, -160, -152, -144, -120, -112, -104, -96, -88, -64, -56, -48, -40, -16, -8, 0, 8, 16, 40, 48, 56, 64, 88, 96, 104, 112, 120, 144, 152, 160, 168, 192, 200, 208, 216, 224, 248, 256, 264, 272, 304, 312, 320, 328, 360, 368, 376, 416, 424, 432, 472, 480, 528, 536, 584, 640,
+ 81, -848, -775, -709, -702, -636, -629, -570, -563, -556, -497, -490, -483, -431, -424, -417, -410, -358, -351, -344, -337, -292, -285, -278, -271, -264, -219, -212, -205, -198, -153, -146, -139, -132, -125, -80, -73, -66, -59, -14, -7, 0, 7, 14, 59, 66, 73, 80, 125, 132, 139, 146, 153, 198, 205, 212, 219, 264, 271, 278, 285, 292, 337, 344, 351, 358, 410, 417, 424, 431, 483, 490, 497, 556, 563, 570, 629, 636, 702, 709, 775, 848,
+ 81, -1464, -1328, -1234, -1192, -1098, -1056, -1004, -962, -920, -868, -826, -784, -774, -732, -690, -648, -638, -596, -554, -544, -512, -502, -460, -418, -408, -376, -366, -324, -314, -282, -272, -230, -188, -178, -146, -136, -94, -84, -52, -42, 0, 42, 52, 84, 94, 136, 146, 178, 188, 230, 272, 282, 314, 324, 366, 376, 408, 418, 460, 502, 512, 544, 554, 596, 638, 648, 690, 732, 774, 784, 826, 868, 920, 962, 1004, 1056, 1098, 1192, 1234, 1328, 1464
+ };
+
+ const unsigned int g_maxPotentialOffsets = 81;
+
+ const int16_t g_thModifierTable[8] =
+ {
+ 3, 6, 11, 16, 23, 32, 41, 64
+ };
+ }
+ }
+}
diff --git a/thirdparty/cvtt/ConvectionKernels_ETC2.h b/thirdparty/cvtt/ConvectionKernels_ETC2.h
new file mode 100644
index 0000000000..4befc8e8c2
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ETC2.h
@@ -0,0 +1,35 @@
+#include <stdint.h>
+
+namespace cvtt
+{
+ namespace Tables
+ {
+ namespace ETC2
+ {
+ const int16_t g_thModifierTable[8] =
+ {
+ 3, 6, 11, 16, 23, 32, 41, 64
+ };
+
+ const int16_t g_alphaModifierTablePositive[16][4] =
+ {
+ { 2, 5, 8, 14, },
+ { 2, 6, 9, 12, },
+ { 1, 4, 7, 12, },
+ { 1, 3, 5, 12, },
+ { 2, 5, 7, 11, },
+ { 2, 6, 8, 10, },
+ { 3, 6, 7, 10, },
+ { 2, 4, 7, 10, },
+ { 1, 5, 7, 9, },
+ { 1, 4, 7, 9, },
+ { 1, 3, 7, 9, },
+ { 1, 4, 6, 9, },
+ { 2, 3, 6, 9, },
+ { 0, 1, 2, 9, },
+ { 3, 5, 7, 8, },
+ { 2, 4, 6, 8, },
+ };
+ }
+ }
+}
diff --git a/thirdparty/cvtt/ConvectionKernels_ETC2_Rounding.h b/thirdparty/cvtt/ConvectionKernels_ETC2_Rounding.h
new file mode 100644
index 0000000000..a4f5a3ddfa
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ETC2_Rounding.h
@@ -0,0 +1,27 @@
+#pragma once
+#include <stdint.h>
+
+// This file is generated by the MakeTables app. Do not edit this file manually.
+
+namespace cvtt { namespace Tables { namespace ETC2 {
+ const int g_alphaRoundingTableWidth = 13;
+ const uint8_t g_alphaRoundingTables[16][13] =
+ {
+ { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3 },
+ { 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3 },
+ { 0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 },
+ { 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3 },
+ { 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3 },
+ { 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3 },
+ { 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3 },
+ { 0, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3 },
+ { 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 3, 3, 3 },
+ { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3 },
+ };
+}}}
diff --git a/thirdparty/cvtt/ConvectionKernels_EndpointRefiner.h b/thirdparty/cvtt/ConvectionKernels_EndpointRefiner.h
new file mode 100644
index 0000000000..c1276553b2
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_EndpointRefiner.h
@@ -0,0 +1,181 @@
+#pragma once
+#ifndef __CVTT_ENDPOINTREFINER_H__
+#define __CVTT_ENDPOINTREFINER_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ // Solve for a, b where v = a*t + b
+ // This allows endpoints to be mapped to where T=0 and T=1
+ // Least squares from totals:
+ // a = (tv - t*v/w)/(tt - t*t/w)
+ // b = (v - a*t)/w
+ template<int TVectorSize>
+ class EndpointRefiner
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::AInt16 MAInt16;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+
+ MFloat m_tv[TVectorSize];
+ MFloat m_v[TVectorSize];
+ MFloat m_tt;
+ MFloat m_t;
+ MFloat m_w;
+ int m_wu;
+
+ float m_rcpMaxIndex;
+ float m_channelWeights[TVectorSize];
+ float m_rcpChannelWeights[TVectorSize];
+
+ void Init(int indexRange, const float channelWeights[TVectorSize])
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ m_tv[ch] = ParallelMath::MakeFloatZero();
+ m_v[ch] = ParallelMath::MakeFloatZero();
+ }
+ m_tt = ParallelMath::MakeFloatZero();
+ m_t = ParallelMath::MakeFloatZero();
+ m_w = ParallelMath::MakeFloatZero();
+
+ m_rcpMaxIndex = 1.0f / static_cast<float>(indexRange - 1);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ m_channelWeights[ch] = channelWeights[ch];
+ m_rcpChannelWeights[ch] = 1.0f;
+ if (m_channelWeights[ch] != 0.0f)
+ m_rcpChannelWeights[ch] = 1.0f / channelWeights[ch];
+ }
+
+ m_wu = 0;
+ }
+
+ void ContributePW(const MFloat *pwFloatPixel, const MUInt15 &index, const MFloat &weight)
+ {
+ MFloat t = ParallelMath::ToFloat(index) * m_rcpMaxIndex;
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MFloat v = pwFloatPixel[ch] * weight;
+
+ m_tv[ch] = m_tv[ch] + t * v;
+ m_v[ch] = m_v[ch] + v;
+ }
+ m_tt = m_tt + weight * t * t;
+ m_t = m_t + weight * t;
+ m_w = m_w + weight;
+ }
+
+ void ContributeUnweightedPW(const MFloat *pwFloatPixel, const MUInt15 &index, int numRealChannels)
+ {
+ MFloat t = ParallelMath::ToFloat(index) * m_rcpMaxIndex;
+
+ for (int ch = 0; ch < numRealChannels; ch++)
+ {
+ MFloat v = pwFloatPixel[ch];
+
+ m_tv[ch] = m_tv[ch] + t * v;
+ m_v[ch] = m_v[ch] + v;
+ }
+ m_tt = m_tt + t * t;
+ m_t = m_t + t;
+ m_wu++;
+ }
+
+ void ContributeUnweightedPW(const MFloat *floatPixel, const MUInt15 &index)
+ {
+ ContributeUnweightedPW(floatPixel, index, TVectorSize);
+ }
+
+ void GetRefinedEndpoints(MFloat endPoint[2][TVectorSize])
+ {
+ // a = (tv - t*v/w)/(tt - t*t/w)
+ // b = (v - a*t)/w
+ MFloat w = m_w + ParallelMath::MakeFloat(static_cast<float>(m_wu));
+
+ ParallelMath::MakeSafeDenominator(w);
+ MFloat wRcp = ParallelMath::Reciprocal(w);
+
+ MFloat adenom = (m_tt * w - m_t * m_t) * wRcp;
+
+ ParallelMath::FloatCompFlag adenomZero = ParallelMath::Equal(adenom, ParallelMath::MakeFloatZero());
+ ParallelMath::ConditionalSet(adenom, adenomZero, ParallelMath::MakeFloat(1.0f));
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ /*
+ if (adenom == 0.0)
+ p1 = p2 = er.v / er.w;
+ else
+ {
+ float4 a = (er.tv - er.t*er.v / er.w) / adenom;
+ float4 b = (er.v - a * er.t) / er.w;
+ p1 = b;
+ p2 = a + b;
+ }
+ */
+
+ MFloat a = (m_tv[ch] - m_t * m_v[ch] * wRcp) / adenom;
+ MFloat b = (m_v[ch] - a * m_t) * wRcp;
+
+ MFloat p1 = b;
+ MFloat p2 = a + b;
+
+ ParallelMath::ConditionalSet(p1, adenomZero, (m_v[ch] * wRcp));
+ ParallelMath::ConditionalSet(p2, adenomZero, p1);
+
+ // Unweight
+ float inverseWeight = m_rcpChannelWeights[ch];
+
+ endPoint[0][ch] = p1 * inverseWeight;
+ endPoint[1][ch] = p2 * inverseWeight;
+ }
+ }
+
+ void GetRefinedEndpointsLDR(MUInt15 endPoint[2][TVectorSize], int numRealChannels, const ParallelMath::RoundTowardNearestForScope *roundingMode)
+ {
+ MFloat floatEndPoint[2][TVectorSize];
+ GetRefinedEndpoints(floatEndPoint);
+
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < TVectorSize; ch++)
+ endPoint[epi][ch] = ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(floatEndPoint[epi][ch], 0.0f, 255.0f), roundingMode);
+ }
+
+ void GetRefinedEndpointsLDR(MUInt15 endPoint[2][TVectorSize], const ParallelMath::RoundTowardNearestForScope *roundingMode)
+ {
+ GetRefinedEndpointsLDR(endPoint, TVectorSize, roundingMode);
+ }
+
+ void GetRefinedEndpointsHDR(MSInt16 endPoint[2][TVectorSize], bool isSigned, const ParallelMath::RoundTowardNearestForScope *roundingMode)
+ {
+ MFloat floatEndPoint[2][TVectorSize];
+ GetRefinedEndpoints(floatEndPoint);
+
+ for (int epi = 0; epi < 2; epi++)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MFloat f = floatEndPoint[epi][ch];
+ if (isSigned)
+ endPoint[epi][ch] = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RoundAndConvertToS16(ParallelMath::Clamp(f, -31743.0f, 31743.0f), roundingMode));
+ else
+ endPoint[epi][ch] = ParallelMath::LosslessCast<MSInt16>::Cast(ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(f, 0.0f, 31743.0f), roundingMode));
+ }
+ }
+ }
+ };
+ }
+}
+
+#endif
+
diff --git a/thirdparty/cvtt/ConvectionKernels_EndpointSelector.h b/thirdparty/cvtt/ConvectionKernels_EndpointSelector.h
new file mode 100644
index 0000000000..e09dfd248c
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_EndpointSelector.h
@@ -0,0 +1,153 @@
+#pragma once
+#ifndef __CVTT_ENDPOINTSELECTOR_H__
+#define __CVTT_ENDPOINTSELECTOR_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+#include "ConvectionKernels_UnfinishedEndpoints.h"
+#include "ConvectionKernels_PackedCovarianceMatrix.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ static const int NumEndpointSelectorPasses = 3;
+
+ template<int TVectorSize, int TIterationCount>
+ class EndpointSelector
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+
+ EndpointSelector()
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ m_centroid[ch] = ParallelMath::MakeFloatZero();
+ m_direction[ch] = ParallelMath::MakeFloatZero();
+ }
+ m_weightTotal = ParallelMath::MakeFloatZero();
+ m_minDist = ParallelMath::MakeFloat(FLT_MAX);
+ m_maxDist = ParallelMath::MakeFloat(-FLT_MAX);
+ }
+
+ void ContributePass(const MFloat *value, int pass, const MFloat &weight)
+ {
+ if (pass == 0)
+ ContributeCentroid(value, weight);
+ else if (pass == 1)
+ ContributeDirection(value, weight);
+ else if (pass == 2)
+ ContributeMinMax(value);
+ }
+
+ void FinishPass(int pass)
+ {
+ if (pass == 0)
+ FinishCentroid();
+ else if (pass == 1)
+ FinishDirection();
+ }
+
+ UnfinishedEndpoints<TVectorSize> GetEndpoints(const float channelWeights[TVectorSize]) const
+ {
+ MFloat unweightedBase[TVectorSize];
+ MFloat unweightedOffset[TVectorSize];
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MFloat min = m_centroid[ch] + m_direction[ch] * m_minDist;
+ MFloat max = m_centroid[ch] + m_direction[ch] * m_maxDist;
+
+ float safeWeight = channelWeights[ch];
+ if (safeWeight == 0.f)
+ safeWeight = 1.0f;
+
+ unweightedBase[ch] = min / channelWeights[ch];
+ unweightedOffset[ch] = (max - min) / channelWeights[ch];
+ }
+
+ return UnfinishedEndpoints<TVectorSize>(unweightedBase, unweightedOffset);
+ }
+
+ private:
+ void ContributeCentroid(const MFloat *value, const MFloat &weight)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_centroid[ch] = m_centroid[ch] + value[ch] * weight;
+ m_weightTotal = m_weightTotal + weight;
+ }
+
+ void FinishCentroid()
+ {
+ MFloat denom = m_weightTotal;
+ ParallelMath::MakeSafeDenominator(denom);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_centroid[ch] = m_centroid[ch] / denom;
+ }
+
+ void ContributeDirection(const MFloat *value, const MFloat &weight)
+ {
+ MFloat diff[TVectorSize];
+ for (int ch = 0; ch < TVectorSize; ch++)
+ diff[ch] = value[ch] - m_centroid[ch];
+
+ m_covarianceMatrix.Add(diff, weight);
+ }
+
+ void FinishDirection()
+ {
+ MFloat approx[TVectorSize];
+ for (int ch = 0; ch < TVectorSize; ch++)
+ approx[ch] = ParallelMath::MakeFloat(1.0f);
+
+ for (int i = 0; i < TIterationCount; i++)
+ {
+ MFloat product[TVectorSize];
+ m_covarianceMatrix.Product(product, approx);
+
+ MFloat largestComponent = product[0];
+ for (int ch = 1; ch < TVectorSize; ch++)
+ largestComponent = ParallelMath::Max(largestComponent, product[ch]);
+
+ // product = largestComponent*newApprox
+ ParallelMath::MakeSafeDenominator(largestComponent);
+ for (int ch = 0; ch < TVectorSize; ch++)
+ approx[ch] = product[ch] / largestComponent;
+ }
+
+ // Normalize
+ MFloat approxLen = ParallelMath::MakeFloatZero();
+ for (int ch = 0; ch < TVectorSize; ch++)
+ approxLen = approxLen + approx[ch] * approx[ch];
+
+ approxLen = ParallelMath::Sqrt(approxLen);
+
+ ParallelMath::MakeSafeDenominator(approxLen);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_direction[ch] = approx[ch] / approxLen;
+ }
+
+ void ContributeMinMax(const MFloat *value)
+ {
+ MFloat dist = ParallelMath::MakeFloatZero();
+ for (int ch = 0; ch < TVectorSize; ch++)
+ dist = dist + m_direction[ch] * (value[ch] - m_centroid[ch]);
+
+ m_minDist = ParallelMath::Min(m_minDist, dist);
+ m_maxDist = ParallelMath::Max(m_maxDist, dist);
+ }
+
+ ParallelMath::Float m_centroid[TVectorSize];
+ ParallelMath::Float m_direction[TVectorSize];
+ PackedCovarianceMatrix<TVectorSize> m_covarianceMatrix;
+ ParallelMath::Float m_weightTotal;
+
+ ParallelMath::Float m_minDist;
+ ParallelMath::Float m_maxDist;
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_FakeBT709_Rounding.h b/thirdparty/cvtt/ConvectionKernels_FakeBT709_Rounding.h
new file mode 100644
index 0000000000..1eb924befe
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_FakeBT709_Rounding.h
@@ -0,0 +1,282 @@
+#pragma once
+#include <stdint.h>
+
+// This file is generated by the MakeTables app. Do not edit this file manually.
+
+namespace cvtt { namespace Tables { namespace FakeBT709 {
+ const uint8_t g_rounding16[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 4, 4, 4, 4, 4,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6,
+ 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7,
+
+ };
+}}}
diff --git a/thirdparty/cvtt/ConvectionKernels_IndexSelector.cpp b/thirdparty/cvtt/ConvectionKernels_IndexSelector.cpp
new file mode 100644
index 0000000000..b3d1b5497e
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_IndexSelector.cpp
@@ -0,0 +1,66 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_IndexSelector.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ const ParallelMath::UInt16 g_weightReciprocals[17] =
+ {
+ ParallelMath::MakeUInt16(0), // -1
+ ParallelMath::MakeUInt16(0), // 0
+ ParallelMath::MakeUInt16(32768), // 1
+ ParallelMath::MakeUInt16(16384), // 2
+ ParallelMath::MakeUInt16(10923), // 3
+ ParallelMath::MakeUInt16(8192), // 4
+ ParallelMath::MakeUInt16(6554), // 5
+ ParallelMath::MakeUInt16(5461), // 6
+ ParallelMath::MakeUInt16(4681), // 7
+ ParallelMath::MakeUInt16(4096), // 8
+ ParallelMath::MakeUInt16(3641), // 9
+ ParallelMath::MakeUInt16(3277), // 10
+ ParallelMath::MakeUInt16(2979), // 11
+ ParallelMath::MakeUInt16(2731), // 12
+ ParallelMath::MakeUInt16(2521), // 13
+ ParallelMath::MakeUInt16(2341), // 14
+ ParallelMath::MakeUInt16(2185), // 15
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_IndexSelector.h b/thirdparty/cvtt/ConvectionKernels_IndexSelector.h
new file mode 100644
index 0000000000..0f9d209183
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_IndexSelector.h
@@ -0,0 +1,147 @@
+#pragma once
+#ifndef __CVTT_INDEXSELECTOR_H__
+#define __CVTT_INDEXSELECTOR_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ extern const ParallelMath::UInt16 g_weightReciprocals[17];
+
+ template<int TVectorSize>
+ class IndexSelector
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::AInt16 MAInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+ typedef ParallelMath::UInt31 MUInt31;
+
+
+ template<class TInterpolationEPType, class TColorEPType>
+ void Init(const float *channelWeights, const TInterpolationEPType interpolationEndPoints[2][TVectorSize], const TColorEPType colorSpaceEndpoints[2][TVectorSize], int range)
+ {
+ // In BC6H, the interpolation endpoints are higher-precision than the endpoints in color space.
+ // We need to select indexes using the color-space endpoints.
+
+ m_isUniform = true;
+ for (int ch = 1; ch < TVectorSize; ch++)
+ {
+ if (channelWeights[ch] != channelWeights[0])
+ m_isUniform = false;
+ }
+
+ // To work with channel weights, we need something where:
+ // pxDiff = px - ep[0]
+ // epDiff = ep[1] - ep[0]
+ //
+ // weightedEPDiff = epDiff * channelWeights
+ // normalizedWeightedAxis = weightedEPDiff / len(weightedEPDiff)
+ // normalizedIndex = dot(pxDiff * channelWeights, normalizedWeightedAxis) / len(weightedEPDiff)
+ // index = normalizedIndex * maxValue
+ //
+ // Equivalent to:
+ // axis = channelWeights * maxValue * epDiff * channelWeights / lenSquared(epDiff * channelWeights)
+ // index = dot(axis, pxDiff)
+
+ for (int ep = 0; ep < 2; ep++)
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_endPoint[ep][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(interpolationEndPoints[ep][ch]);
+
+ m_range = range;
+ m_maxValue = static_cast<float>(range - 1);
+
+ MFloat epDiffWeighted[TVectorSize];
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ m_origin[ch] = ParallelMath::ToFloat(colorSpaceEndpoints[0][ch]);
+ MFloat opposingOriginCh = ParallelMath::ToFloat(colorSpaceEndpoints[1][ch]);
+ epDiffWeighted[ch] = (opposingOriginCh - m_origin[ch]) * channelWeights[ch];
+ }
+
+ MFloat lenSquared = epDiffWeighted[0] * epDiffWeighted[0];
+ for (int ch = 1; ch < TVectorSize; ch++)
+ lenSquared = lenSquared + epDiffWeighted[ch] * epDiffWeighted[ch];
+
+ ParallelMath::MakeSafeDenominator(lenSquared);
+
+ MFloat maxValueDividedByLengthSquared = ParallelMath::MakeFloat(m_maxValue) / lenSquared;
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_axis[ch] = epDiffWeighted[ch] * channelWeights[ch] * maxValueDividedByLengthSquared;
+ }
+
+ template<bool TSigned>
+ void Init(const float channelWeights[TVectorSize], const MUInt15 endPoints[2][TVectorSize], int range)
+ {
+ MAInt16 converted[2][TVectorSize];
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < TVectorSize; ch++)
+ converted[epi][ch] = ParallelMath::LosslessCast<MAInt16>::Cast(endPoints[epi][ch]);
+
+ Init<MUInt15, MUInt15>(channelWeights, endPoints, endPoints, range);
+ }
+
+ void ReconstructLDR_BC7(const MUInt15 &index, MUInt15* pixel, int numRealChannels)
+ {
+ MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
+
+ for (int ch = 0; ch < numRealChannels; ch++)
+ {
+ MUInt15 ep0f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply((ParallelMath::MakeUInt15(64) - weight), ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[0][ch])));
+ MUInt15 ep1f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(weight, ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[1][ch])));
+ pixel[ch] = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ep0f + ep1f + ParallelMath::MakeUInt15(32), 6));
+ }
+ }
+
+ void ReconstructLDRPrecise(const MUInt15 &index, MUInt15* pixel, int numRealChannels)
+ {
+ MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 64, 7));
+
+ for (int ch = 0; ch < numRealChannels; ch++)
+ {
+ MUInt15 ep0f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply((ParallelMath::MakeUInt15(256) - weight), ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[0][ch])));
+ MUInt15 ep1f = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::CompactMultiply(weight, ParallelMath::LosslessCast<MUInt15>::Cast(m_endPoint[1][ch])));
+ pixel[ch] = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ep0f + ep1f + ParallelMath::MakeUInt15(128), 8));
+ }
+ }
+
+ void ReconstructLDR_BC7(const MUInt15 &index, MUInt15* pixel)
+ {
+ ReconstructLDR_BC7(index, pixel, TVectorSize);
+ }
+
+ void ReconstructLDRPrecise(const MUInt15 &index, MUInt15* pixel)
+ {
+ ReconstructLDRPrecise(index, pixel, TVectorSize);
+ }
+
+ MUInt15 SelectIndexLDR(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope* rtn) const
+ {
+ MFloat dist = (pixel[0] - m_origin[0]) * m_axis[0];
+ for (int ch = 1; ch < TVectorSize; ch++)
+ dist = dist + (pixel[ch] - m_origin[ch]) * m_axis[ch];
+
+ return ParallelMath::RoundAndConvertToU15(ParallelMath::Clamp(dist, 0.0f, m_maxValue), rtn);
+ }
+
+ protected:
+ MAInt16 m_endPoint[2][TVectorSize];
+
+ private:
+ MFloat m_origin[TVectorSize];
+ MFloat m_axis[TVectorSize];
+ int m_range;
+ float m_maxValue;
+ bool m_isUniform;
+ };
+ }
+}
+
+#endif
+
diff --git a/thirdparty/cvtt/ConvectionKernels_IndexSelectorHDR.h b/thirdparty/cvtt/ConvectionKernels_IndexSelectorHDR.h
new file mode 100644
index 0000000000..84795cd689
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_IndexSelectorHDR.h
@@ -0,0 +1,155 @@
+#pragma once
+#ifndef __CVTT_INDEXSELECTORHDR_H__
+#define __CVTT_INDEXSELECTORHDR_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+#include "ConvectionKernels_IndexSelector.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ ParallelMath::SInt16 UnscaleHDRValueSigned(const ParallelMath::SInt16 &v);
+ ParallelMath::UInt15 UnscaleHDRValueUnsigned(const ParallelMath::UInt16 &v);
+
+ template<int TVectorSize>
+ class IndexSelectorHDR : public IndexSelector<TVectorSize>
+ {
+ public:
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt31 MUInt31;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+ typedef ParallelMath::Float MFloat;
+
+ private:
+
+ MUInt15 InvertSingle(const MUInt15& anIndex) const
+ {
+ MUInt15 inverted = m_maxValueMinusOne - anIndex;
+ return ParallelMath::Select(m_isInverted, inverted, anIndex);
+ }
+
+ void ReconstructHDRSignedUninverted(const MUInt15 &index, MSInt16* pixel) const
+ {
+ MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MSInt16 ep0 = ParallelMath::LosslessCast<MSInt16>::Cast(this->m_endPoint[0][ch]);
+ MSInt16 ep1 = ParallelMath::LosslessCast<MSInt16>::Cast(this->m_endPoint[1][ch]);
+
+ MSInt32 pixel32 = ParallelMath::XMultiply((ParallelMath::MakeUInt15(64) - weight), ep0) + ParallelMath::XMultiply(weight, ep1);
+
+ pixel32 = ParallelMath::RightShift(pixel32 + ParallelMath::MakeSInt32(32), 6);
+
+ pixel[ch] = UnscaleHDRValueSigned(ParallelMath::ToSInt16(pixel32));
+ }
+ }
+
+ void ReconstructHDRUnsignedUninverted(const MUInt15 &index, MSInt16* pixel) const
+ {
+ MUInt15 weight = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(g_weightReciprocals[m_range], index) + 256, 9));
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MUInt16 ep0 = ParallelMath::LosslessCast<MUInt16>::Cast(this->m_endPoint[0][ch]);
+ MUInt16 ep1 = ParallelMath::LosslessCast<MUInt16>::Cast(this->m_endPoint[1][ch]);
+
+ MUInt31 pixel31 = ParallelMath::XMultiply((ParallelMath::MakeUInt15(64) - weight), ep0) + ParallelMath::XMultiply(weight, ep1);
+
+ pixel31 = ParallelMath::RightShift(pixel31 + ParallelMath::MakeUInt31(32), 6);
+
+ pixel[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(UnscaleHDRValueUnsigned(ParallelMath::ToUInt16(pixel31)));
+ }
+ }
+
+ MFloat ErrorForInterpolatorComponent(int index, int ch, const MFloat *pixel) const
+ {
+ MFloat diff = pixel[ch] - m_reconstructedInterpolators[index][ch];
+ return diff * diff;
+ }
+
+ MFloat ErrorForInterpolator(int index, const MFloat *pixel) const
+ {
+ MFloat error = ErrorForInterpolatorComponent(index, 0, pixel);
+ for (int ch = 1; ch < TVectorSize; ch++)
+ error = error + ErrorForInterpolatorComponent(index, ch, pixel);
+ return error;
+ }
+
+ public:
+
+ void InitHDR(int range, bool isSigned, bool fastIndexing, const float *channelWeights)
+ {
+ assert(range <= 16);
+
+ m_range = range;
+
+ m_isInverted = ParallelMath::MakeBoolInt16(false);
+ m_maxValueMinusOne = ParallelMath::MakeUInt15(static_cast<uint16_t>(range - 1));
+
+ if (!fastIndexing)
+ {
+ for (int i = 0; i < range; i++)
+ {
+ MSInt16 recon2CL[TVectorSize];
+
+ if (isSigned)
+ ReconstructHDRSignedUninverted(ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), recon2CL);
+ else
+ ReconstructHDRUnsignedUninverted(ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), recon2CL);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_reconstructedInterpolators[i][ch] = ParallelMath::TwosCLHalfToFloat(recon2CL[ch]) * channelWeights[ch];
+ }
+ }
+ }
+
+ void ReconstructHDRSigned(const MUInt15 &index, MSInt16* pixel) const
+ {
+ ReconstructHDRSignedUninverted(InvertSingle(index), pixel);
+ }
+
+ void ReconstructHDRUnsigned(const MUInt15 &index, MSInt16* pixel) const
+ {
+ ReconstructHDRUnsignedUninverted(InvertSingle(index), pixel);
+ }
+
+ void ConditionalInvert(const ParallelMath::Int16CompFlag &invert)
+ {
+ m_isInverted = invert;
+ }
+
+ MUInt15 SelectIndexHDRSlow(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope*) const
+ {
+ MUInt15 index = ParallelMath::MakeUInt15(0);
+
+ MFloat bestError = ErrorForInterpolator(0, pixel);
+ for (int i = 1; i < m_range; i++)
+ {
+ MFloat error = ErrorForInterpolator(i, pixel);
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
+ ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(errorBetter), ParallelMath::MakeUInt15(static_cast<uint16_t>(i)));
+ bestError = ParallelMath::Min(bestError, error);
+ }
+
+ return InvertSingle(index);
+ }
+
+ MUInt15 SelectIndexHDRFast(const MFloat* pixel, const ParallelMath::RoundTowardNearestForScope* rtn) const
+ {
+ return InvertSingle(this->SelectIndexLDR(pixel, rtn));
+ }
+
+ private:
+ MFloat m_reconstructedInterpolators[16][TVectorSize];
+ ParallelMath::Int16CompFlag m_isInverted;
+ MUInt15 m_maxValueMinusOne;
+ int m_range;
+ };
+ }
+}
+#endif
+
diff --git a/thirdparty/cvtt/ConvectionKernels_PackedCovarianceMatrix.h b/thirdparty/cvtt/ConvectionKernels_PackedCovarianceMatrix.h
new file mode 100644
index 0000000000..7ac3d4fdda
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_PackedCovarianceMatrix.h
@@ -0,0 +1,68 @@
+#pragma once
+#ifndef __CVTT_COVARIANCEMATRIX_H__
+#define __CVTT_COVARIANCEMATRIX_H__
+
+namespace cvtt
+{
+ namespace Internal
+ {
+
+ template<int TMatrixSize>
+ class PackedCovarianceMatrix
+ {
+ public:
+ // 0: xx,
+ // 1: xy, yy
+ // 3: xz, yz, zz
+ // 6: xw, yw, zw, ww
+ // ... etc.
+ static const int PyramidSize = (TMatrixSize * (TMatrixSize + 1)) / 2;
+
+ typedef ParallelMath::Float MFloat;
+
+ PackedCovarianceMatrix()
+ {
+ for (int i = 0; i < PyramidSize; i++)
+ m_values[i] = ParallelMath::MakeFloatZero();
+ }
+
+ void Add(const ParallelMath::Float *vec, const ParallelMath::Float &weight)
+ {
+ int index = 0;
+ for (int row = 0; row < TMatrixSize; row++)
+ {
+ for (int col = 0; col <= row; col++)
+ {
+ m_values[index] = m_values[index] + vec[row] * vec[col] * weight;
+ index++;
+ }
+ }
+ }
+
+ void Product(MFloat *outVec, const MFloat *inVec)
+ {
+ for (int row = 0; row < TMatrixSize; row++)
+ {
+ MFloat sum = ParallelMath::MakeFloatZero();
+
+ int index = (row * (row + 1)) >> 1;
+ for (int col = 0; col < TMatrixSize; col++)
+ {
+ sum = sum + inVec[col] * m_values[index];
+ if (col >= row)
+ index += col + 1;
+ else
+ index++;
+ }
+
+ outVec[row] = sum;
+ }
+ }
+
+ private:
+ ParallelMath::Float m_values[PyramidSize];
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_ParallelMath.h b/thirdparty/cvtt/ConvectionKernels_ParallelMath.h
new file mode 100644
index 0000000000..9e25280f45
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_ParallelMath.h
@@ -0,0 +1,1816 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+*/
+#pragma once
+#ifndef __CVTT_PARALLELMATH_H__
+#define __CVTT_PARALLELMATH_H__
+
+#include "ConvectionKernels.h"
+#include "ConvectionKernels_Config.h"
+
+#ifdef CVTT_USE_SSE2
+#include <emmintrin.h>
+#endif
+
+#include <float.h>
+#include <assert.h>
+#include <string.h>
+#include <algorithm>
+#include <math.h>
+
+#define UNREFERENCED_PARAMETER(n) ((void)n)
+
+// Parallel math implementation
+//
+// After preprocessor defs are handled, what this should do is expose the following types:
+// SInt16 - Signed 16-bit integer
+// UInt16 - Signed 16-bit integer
+// UInt15 - Unsigned 15-bit integer
+// SInt32 - Signed 32-bit integer
+// UInt31 - Unsigned 31-bit integer
+// AInt16 - 16-bit integer of unknown signedness (only used for storage)
+// Int16CompFlag - Comparison flags from comparing 16-bit integers
+// Int32CompFlag - Comparison flags from comparing 32-bit integers
+// FloatCompFlag - Comparison flags from comparing 32-bit floats
+//
+// The reason for these distinctions are that depending on the instruction set, signed or unsigned versions of certain ops
+// (particularly max, min, compares, and right shift) may not be available. In cases where ops are not available, it's
+// necessary to do high bit manipulations to accomplish the operation with 16-bit numbers. The 15-bit and 31-bit uint types
+// can elide the bit flips if unsigned versions are not available.
+
+namespace cvtt
+{
+#ifdef CVTT_USE_SSE2
+ // SSE2 version
+ struct ParallelMath
+ {
+ typedef uint16_t ScalarUInt16;
+ typedef int16_t ScalarSInt16;
+
+ template<unsigned int TRoundingMode>
+ struct RoundForScope
+ {
+ unsigned int m_oldCSR;
+
+ RoundForScope()
+ {
+ m_oldCSR = _mm_getcsr();
+ _mm_setcsr((m_oldCSR & ~_MM_ROUND_MASK) | (TRoundingMode));
+ }
+
+ ~RoundForScope()
+ {
+ _mm_setcsr(m_oldCSR);
+ }
+ };
+
+ struct RoundTowardZeroForScope : RoundForScope<_MM_ROUND_TOWARD_ZERO>
+ {
+ };
+
+ struct RoundTowardNearestForScope : RoundForScope<_MM_ROUND_NEAREST>
+ {
+ };
+
+ struct RoundUpForScope : RoundForScope<_MM_ROUND_UP>
+ {
+ };
+
+ struct RoundDownForScope : RoundForScope<_MM_ROUND_DOWN>
+ {
+ };
+
+ static const int ParallelSize = 8;
+
+ enum Int16Subtype
+ {
+ IntSubtype_Signed,
+ IntSubtype_UnsignedFull,
+ IntSubtype_UnsignedTruncated,
+ IntSubtype_Abstract,
+ };
+
+ template<int TSubtype>
+ struct VInt16
+ {
+ __m128i m_value;
+
+ inline VInt16 operator+(int16_t other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_add_epi16(m_value, _mm_set1_epi16(static_cast<int16_t>(other)));
+ return result;
+ }
+
+ inline VInt16 operator+(const VInt16 &other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_add_epi16(m_value, other.m_value);
+ return result;
+ }
+
+ inline VInt16 operator|(const VInt16 &other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_or_si128(m_value, other.m_value);
+ return result;
+ }
+
+ inline VInt16 operator&(const VInt16 &other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_and_si128(m_value, other.m_value);
+ return result;
+ }
+
+ inline VInt16 operator-(const VInt16 &other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_sub_epi16(m_value, other.m_value);
+ return result;
+ }
+
+ inline VInt16 operator<<(int bits) const
+ {
+ VInt16 result;
+ result.m_value = _mm_slli_epi16(m_value, bits);
+ return result;
+ }
+
+ inline VInt16 operator^(const VInt16 &other) const
+ {
+ VInt16 result;
+ result.m_value = _mm_xor_si128(m_value, other.m_value);
+ return result;
+ }
+ };
+
+ typedef VInt16<IntSubtype_Signed> SInt16;
+ typedef VInt16<IntSubtype_UnsignedFull> UInt16;
+ typedef VInt16<IntSubtype_UnsignedTruncated> UInt15;
+ typedef VInt16<IntSubtype_Abstract> AInt16;
+
+ template<int TSubtype>
+ struct VInt32
+ {
+ __m128i m_values[2];
+
+ inline VInt32 operator+(const VInt32& other) const
+ {
+ VInt32 result;
+ result.m_values[0] = _mm_add_epi32(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_add_epi32(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline VInt32 operator-(const VInt32& other) const
+ {
+ VInt32 result;
+ result.m_values[0] = _mm_sub_epi32(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_sub_epi32(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline VInt32 operator<<(const int other) const
+ {
+ VInt32 result;
+ result.m_values[0] = _mm_slli_epi32(m_values[0], other);
+ result.m_values[1] = _mm_slli_epi32(m_values[1], other);
+ return result;
+ }
+
+ inline VInt32 operator|(const VInt32& other) const
+ {
+ VInt32 result;
+ result.m_values[0] = _mm_or_si128(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_or_si128(m_values[1], other.m_values[1]);
+ return result;
+ }
+ };
+
+ typedef VInt32<IntSubtype_Signed> SInt32;
+ typedef VInt32<IntSubtype_UnsignedTruncated> UInt31;
+ typedef VInt32<IntSubtype_UnsignedFull> UInt32;
+ typedef VInt32<IntSubtype_Abstract> AInt32;
+
+ template<class TTargetType>
+ struct LosslessCast
+ {
+#ifdef CVTT_PERMIT_ALIASING
+ template<int TSrcSubtype>
+ static const TTargetType& Cast(const VInt32<TSrcSubtype> &src)
+ {
+ return reinterpret_cast<VInt32<TSubtype>&>(src);
+ }
+
+ template<int TSrcSubtype>
+ static const TTargetType& Cast(const VInt16<TSrcSubtype> &src)
+ {
+ return reinterpret_cast<VInt16<TSubtype>&>(src);
+ }
+#else
+ template<int TSrcSubtype>
+ static TTargetType Cast(const VInt32<TSrcSubtype> &src)
+ {
+ TTargetType result;
+ result.m_values[0] = src.m_values[0];
+ result.m_values[1] = src.m_values[1];
+ return result;
+ }
+
+ template<int TSrcSubtype>
+ static TTargetType Cast(const VInt16<TSrcSubtype> &src)
+ {
+ TTargetType result;
+ result.m_value = src.m_value;
+ return result;
+ }
+#endif
+ };
+
+ struct Int64
+ {
+ __m128i m_values[4];
+ };
+
+ struct Float
+ {
+ __m128 m_values[2];
+
+ inline Float operator+(const Float &other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_add_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_add_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline Float operator+(float other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_add_ps(m_values[0], _mm_set1_ps(other));
+ result.m_values[1] = _mm_add_ps(m_values[1], _mm_set1_ps(other));
+ return result;
+ }
+
+ inline Float operator-(const Float& other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_sub_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_sub_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline Float operator-() const
+ {
+ Float result;
+ result.m_values[0] = _mm_sub_ps(_mm_setzero_ps(), m_values[0]);
+ result.m_values[1] = _mm_sub_ps(_mm_setzero_ps(), m_values[1]);
+ return result;
+ }
+
+ inline Float operator*(const Float& other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_mul_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_mul_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline Float operator*(float other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_mul_ps(m_values[0], _mm_set1_ps(other));
+ result.m_values[1] = _mm_mul_ps(m_values[1], _mm_set1_ps(other));
+ return result;
+ }
+
+ inline Float operator/(const Float &other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_div_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_div_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline Float operator/(float other) const
+ {
+ Float result;
+ result.m_values[0] = _mm_div_ps(m_values[0], _mm_set1_ps(other));
+ result.m_values[1] = _mm_div_ps(m_values[1], _mm_set1_ps(other));
+ return result;
+ }
+ };
+
+ struct Int16CompFlag
+ {
+ __m128i m_value;
+
+ inline Int16CompFlag operator&(const Int16CompFlag &other) const
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_and_si128(m_value, other.m_value);
+ return result;
+ }
+
+ inline Int16CompFlag operator|(const Int16CompFlag &other) const
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_or_si128(m_value, other.m_value);
+ return result;
+ }
+ };
+
+ struct Int32CompFlag
+ {
+ __m128i m_values[2];
+
+ inline Int32CompFlag operator&(const Int32CompFlag &other) const
+ {
+ Int32CompFlag result;
+ result.m_values[0] = _mm_and_si128(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_and_si128(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline Int32CompFlag operator|(const Int32CompFlag &other) const
+ {
+ Int32CompFlag result;
+ result.m_values[0] = _mm_or_si128(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_or_si128(m_values[1], other.m_values[1]);
+ return result;
+ }
+ };
+
+ struct FloatCompFlag
+ {
+ __m128 m_values[2];
+
+ inline FloatCompFlag operator&(const FloatCompFlag &other) const
+ {
+ FloatCompFlag result;
+ result.m_values[0] = _mm_and_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_and_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+
+ inline FloatCompFlag operator|(const FloatCompFlag &other) const
+ {
+ FloatCompFlag result;
+ result.m_values[0] = _mm_or_ps(m_values[0], other.m_values[0]);
+ result.m_values[1] = _mm_or_ps(m_values[1], other.m_values[1]);
+ return result;
+ }
+ };
+
+ template<int TSubtype>
+ static VInt16<TSubtype> AbstractAdd(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
+ {
+ VInt16<TSubtype> result;
+ result.m_value = _mm_add_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ template<int TSubtype>
+ static VInt16<TSubtype> AbstractSubtract(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
+ {
+ VInt16<TSubtype> result;
+ result.m_value = _mm_sub_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static Float Select(const FloatCompFlag &flag, const Float &a, const Float &b)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_or_ps(_mm_and_ps(flag.m_values[i], a.m_values[i]), _mm_andnot_ps(flag.m_values[i], b.m_values[i]));
+ return result;
+ }
+
+ template<int TSubtype>
+ static VInt16<TSubtype> Select(const Int16CompFlag &flag, const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
+ {
+ VInt16<TSubtype> result;
+ result.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, a.m_value), _mm_andnot_si128(flag.m_value, b.m_value));
+ return result;
+ }
+
+ template<int TSubtype>
+ static VInt16<TSubtype> SelectOrZero(const Int16CompFlag &flag, const VInt16<TSubtype> &a)
+ {
+ VInt16<TSubtype> result;
+ result.m_value = _mm_and_si128(flag.m_value, a.m_value);
+ return result;
+ }
+
+ template<int TSubtype>
+ static void ConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src)
+ {
+ dest.m_value = _mm_or_si128(_mm_andnot_si128(flag.m_value, dest.m_value), _mm_and_si128(flag.m_value, src.m_value));
+ }
+
+ template<int TSubtype>
+ static void ConditionalSet(VInt32<TSubtype> &dest, const Int16CompFlag &flag, const VInt32<TSubtype> &src)
+ {
+ __m128i lowFlags = _mm_unpacklo_epi16(flag.m_value, flag.m_value);
+ __m128i highFlags = _mm_unpackhi_epi16(flag.m_value, flag.m_value);
+ dest.m_values[0] = _mm_or_si128(_mm_andnot_si128(lowFlags, dest.m_values[0]), _mm_and_si128(lowFlags, src.m_values[0]));
+ dest.m_values[1] = _mm_or_si128(_mm_andnot_si128(highFlags, dest.m_values[1]), _mm_and_si128(highFlags, src.m_values[1]));
+ }
+
+ static void ConditionalSet(ParallelMath::Int16CompFlag &dest, const Int16CompFlag &flag, const ParallelMath::Int16CompFlag &src)
+ {
+ dest.m_value = _mm_or_si128(_mm_andnot_si128(flag.m_value, dest.m_value), _mm_and_si128(flag.m_value, src.m_value));
+ }
+
+ static SInt16 ConditionalNegate(const Int16CompFlag &flag, const SInt16 &v)
+ {
+ SInt16 result;
+ result.m_value = _mm_add_epi16(_mm_xor_si128(flag.m_value, v.m_value), _mm_srli_epi16(flag.m_value, 15));
+ return result;
+ }
+
+ template<int TSubtype>
+ static void NotConditionalSet(VInt16<TSubtype> &dest, const Int16CompFlag &flag, const VInt16<TSubtype> &src)
+ {
+ dest.m_value = _mm_or_si128(_mm_and_si128(flag.m_value, dest.m_value), _mm_andnot_si128(flag.m_value, src.m_value));
+ }
+
+ static void ConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src)
+ {
+ for (int i = 0; i < 2; i++)
+ dest.m_values[i] = _mm_or_ps(_mm_andnot_ps(flag.m_values[i], dest.m_values[i]), _mm_and_ps(flag.m_values[i], src.m_values[i]));
+ }
+
+ static void NotConditionalSet(Float &dest, const FloatCompFlag &flag, const Float &src)
+ {
+ for (int i = 0; i < 2; i++)
+ dest.m_values[i] = _mm_or_ps(_mm_and_ps(flag.m_values[i], dest.m_values[i]), _mm_andnot_ps(flag.m_values[i], src.m_values[i]));
+ }
+
+ static void MakeSafeDenominator(Float& v)
+ {
+ ConditionalSet(v, Equal(v, MakeFloatZero()), MakeFloat(1.0f));
+ }
+
+ static SInt16 TruncateToPrecisionSigned(const SInt16 &v, int precision)
+ {
+ int lostBits = 16 - precision;
+ if (lostBits == 0)
+ return v;
+
+ SInt16 result;
+ result.m_value = _mm_srai_epi16(_mm_slli_epi16(v.m_value, lostBits), lostBits);
+ return result;
+ }
+
+ static UInt16 TruncateToPrecisionUnsigned(const UInt16 &v, int precision)
+ {
+ int lostBits = 16 - precision;
+ if (lostBits == 0)
+ return v;
+
+ UInt16 result;
+ result.m_value = _mm_srli_epi16(_mm_slli_epi16(v.m_value, lostBits), lostBits);
+ return result;
+ }
+
+ static UInt16 Min(const UInt16 &a, const UInt16 &b)
+ {
+ __m128i bitFlip = _mm_set1_epi16(-32768);
+
+ UInt16 result;
+ result.m_value = _mm_xor_si128(_mm_min_epi16(_mm_xor_si128(a.m_value, bitFlip), _mm_xor_si128(b.m_value, bitFlip)), bitFlip);
+ return result;
+ }
+
+ static SInt16 Min(const SInt16 &a, const SInt16 &b)
+ {
+ SInt16 result;
+ result.m_value = _mm_min_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static UInt15 Min(const UInt15 &a, const UInt15 &b)
+ {
+ UInt15 result;
+ result.m_value = _mm_min_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static Float Min(const Float &a, const Float &b)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_min_ps(a.m_values[i], b.m_values[i]);
+ return result;
+ }
+
+ static UInt16 Max(const UInt16 &a, const UInt16 &b)
+ {
+ __m128i bitFlip = _mm_set1_epi16(-32768);
+
+ UInt16 result;
+ result.m_value = _mm_xor_si128(_mm_max_epi16(_mm_xor_si128(a.m_value, bitFlip), _mm_xor_si128(b.m_value, bitFlip)), bitFlip);
+ return result;
+ }
+
+ static SInt16 Max(const SInt16 &a, const SInt16 &b)
+ {
+ SInt16 result;
+ result.m_value = _mm_max_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static UInt15 Max(const UInt15 &a, const UInt15 &b)
+ {
+ UInt15 result;
+ result.m_value = _mm_max_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static Float Max(const Float &a, const Float &b)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_max_ps(a.m_values[i], b.m_values[i]);
+ return result;
+ }
+
+ static Float Clamp(const Float &v, float min, float max)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_max_ps(_mm_min_ps(v.m_values[i], _mm_set1_ps(max)), _mm_set1_ps(min));
+ return result;
+ }
+
+ static Float Reciprocal(const Float &v)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_rcp_ps(v.m_values[i]);
+ return result;
+ }
+
+ static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, UInt15 &chOut)
+ {
+ int16_t values[8];
+ for (int i = 0; i < 8; i++)
+ values[i] = inputBlocks[i].m_pixels[pxOffset][channel];
+
+ chOut.m_value = _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]);
+ }
+
+ static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, SInt16 &chOut)
+ {
+ int16_t values[8];
+ for (int i = 0; i < 8; i++)
+ values[i] = inputBlocks[i].m_pixels[pxOffset][channel];
+
+ chOut.m_value = _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]);
+ }
+
+ static Float MakeFloat(float v)
+ {
+ Float f;
+ f.m_values[0] = f.m_values[1] = _mm_set1_ps(v);
+ return f;
+ }
+
+ static Float MakeFloatZero()
+ {
+ Float f;
+ f.m_values[0] = f.m_values[1] = _mm_setzero_ps();
+ return f;
+ }
+
+ static UInt16 MakeUInt16(uint16_t v)
+ {
+ UInt16 result;
+ result.m_value = _mm_set1_epi16(static_cast<short>(v));
+ return result;
+ }
+
+ static SInt16 MakeSInt16(int16_t v)
+ {
+ SInt16 result;
+ result.m_value = _mm_set1_epi16(static_cast<short>(v));
+ return result;
+ }
+
+ static AInt16 MakeAInt16(int16_t v)
+ {
+ AInt16 result;
+ result.m_value = _mm_set1_epi16(static_cast<short>(v));
+ return result;
+ }
+
+ static UInt15 MakeUInt15(uint16_t v)
+ {
+ UInt15 result;
+ result.m_value = _mm_set1_epi16(static_cast<short>(v));
+ return result;
+ }
+
+ static SInt32 MakeSInt32(int32_t v)
+ {
+ SInt32 result;
+ result.m_values[0] = _mm_set1_epi32(v);
+ result.m_values[1] = _mm_set1_epi32(v);
+ return result;
+ }
+
+ static UInt31 MakeUInt31(uint32_t v)
+ {
+ UInt31 result;
+ result.m_values[0] = _mm_set1_epi32(v);
+ result.m_values[1] = _mm_set1_epi32(v);
+ return result;
+ }
+
+ static uint16_t Extract(const UInt16 &v, int offset)
+ {
+ return reinterpret_cast<const uint16_t*>(&v.m_value)[offset];
+ }
+
+ static int16_t Extract(const SInt16 &v, int offset)
+ {
+ return reinterpret_cast<const int16_t*>(&v.m_value)[offset];
+ }
+
+ static uint16_t Extract(const UInt15 &v, int offset)
+ {
+ return reinterpret_cast<const uint16_t*>(&v.m_value)[offset];
+ }
+
+ static int16_t Extract(const AInt16 &v, int offset)
+ {
+ return reinterpret_cast<const int16_t*>(&v.m_value)[offset];
+ }
+
+ static int32_t Extract(const SInt32 &v, int offset)
+ {
+ return reinterpret_cast<const int32_t*>(&v.m_values[offset >> 2])[offset & 3];
+ }
+
+ static float Extract(const Float &v, int offset)
+ {
+ return reinterpret_cast<const float*>(&v.m_values[offset >> 2])[offset & 3];
+ }
+
+ static bool Extract(const ParallelMath::Int16CompFlag &v, int offset)
+ {
+ return reinterpret_cast<const int16_t*>(&v.m_value)[offset] != 0;
+ }
+
+ static void PutUInt16(UInt16 &dest, int offset, uint16_t v)
+ {
+ reinterpret_cast<uint16_t*>(&dest)[offset] = v;
+ }
+
+ static void PutUInt15(UInt15 &dest, int offset, uint16_t v)
+ {
+ reinterpret_cast<uint16_t*>(&dest)[offset] = v;
+ }
+
+ static void PutSInt16(SInt16 &dest, int offset, int16_t v)
+ {
+ reinterpret_cast<int16_t*>(&dest)[offset] = v;
+ }
+
+ static float ExtractFloat(const Float& v, int offset)
+ {
+ return reinterpret_cast<const float*>(&v)[offset];
+ }
+
+ static void PutFloat(Float &dest, int offset, float v)
+ {
+ reinterpret_cast<float*>(&dest)[offset] = v;
+ }
+
+ static void PutBoolInt16(Int16CompFlag &dest, int offset, bool v)
+ {
+ reinterpret_cast<int16_t*>(&dest)[offset] = v ? -1 : 0;
+ }
+
+ static Int32CompFlag Less(const UInt31 &a, const UInt31 &b)
+ {
+ Int32CompFlag result;
+ result.m_values[0] = _mm_cmplt_epi32(a.m_values[0], b.m_values[0]);
+ result.m_values[1] = _mm_cmplt_epi32(a.m_values[1], b.m_values[1]);
+ return result;
+ }
+
+ static Int16CompFlag Less(const SInt16 &a, const SInt16 &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static Int16CompFlag Less(const UInt15 &a, const UInt15 &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static Int16CompFlag LessOrEqual(const UInt15 &a, const UInt15 &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_cmplt_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static FloatCompFlag Less(const Float &a, const Float &b)
+ {
+ FloatCompFlag result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_cmplt_ps(a.m_values[i], b.m_values[i]);
+ return result;
+ }
+
+ static FloatCompFlag LessOrEqual(const Float &a, const Float &b)
+ {
+ FloatCompFlag result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_cmple_ps(a.m_values[i], b.m_values[i]);
+ return result;
+ }
+
+ template<int TSubtype>
+ static Int16CompFlag Equal(const VInt16<TSubtype> &a, const VInt16<TSubtype> &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_cmpeq_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static FloatCompFlag Equal(const Float &a, const Float &b)
+ {
+ FloatCompFlag result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_cmpeq_ps(a.m_values[i], b.m_values[i]);
+ return result;
+ }
+
+ static Int16CompFlag Equal(const Int16CompFlag &a, const Int16CompFlag &b)
+ {
+ Int16CompFlag notResult;
+ notResult.m_value = _mm_xor_si128(a.m_value, b.m_value);
+ return Not(notResult);
+ }
+
+ static Float ToFloat(const UInt16 &v)
+ {
+ Float result;
+ result.m_values[0] = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v.m_value, _mm_setzero_si128()));
+ result.m_values[1] = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v.m_value, _mm_setzero_si128()));
+ return result;
+ }
+
+ static UInt31 ToUInt31(const UInt16 &v)
+ {
+ UInt31 result;
+ result.m_values[0] = _mm_unpacklo_epi16(v.m_value, _mm_setzero_si128());
+ result.m_values[1] = _mm_unpackhi_epi16(v.m_value, _mm_setzero_si128());
+ return result;
+ }
+
+ static SInt32 ToInt32(const UInt16 &v)
+ {
+ SInt32 result;
+ result.m_values[0] = _mm_unpacklo_epi16(v.m_value, _mm_setzero_si128());
+ result.m_values[1] = _mm_unpackhi_epi16(v.m_value, _mm_setzero_si128());
+ return result;
+ }
+
+ static SInt32 ToInt32(const UInt15 &v)
+ {
+ SInt32 result;
+ result.m_values[0] = _mm_unpacklo_epi16(v.m_value, _mm_setzero_si128());
+ result.m_values[1] = _mm_unpackhi_epi16(v.m_value, _mm_setzero_si128());
+ return result;
+ }
+
+ static SInt32 ToInt32(const SInt16 &v)
+ {
+ SInt32 result;
+ result.m_values[0] = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), v.m_value), 16);
+ result.m_values[1] = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), v.m_value), 16);
+ return result;
+ }
+
+ static Float ToFloat(const SInt16 &v)
+ {
+ Float result;
+ result.m_values[0] = _mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), v.m_value), 16));
+ result.m_values[1] = _mm_cvtepi32_ps(_mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), v.m_value), 16));
+ return result;
+ }
+
+ static Float ToFloat(const UInt15 &v)
+ {
+ Float result;
+ result.m_values[0] = _mm_cvtepi32_ps(_mm_unpacklo_epi16(v.m_value, _mm_setzero_si128()));
+ result.m_values[1] = _mm_cvtepi32_ps(_mm_unpackhi_epi16(v.m_value, _mm_setzero_si128()));
+ return result;
+ }
+
+ static Float ToFloat(const UInt31 &v)
+ {
+ Float result;
+ result.m_values[0] = _mm_cvtepi32_ps(v.m_values[0]);
+ result.m_values[1] = _mm_cvtepi32_ps(v.m_values[1]);
+ return result;
+ }
+
+ static Int16CompFlag FloatFlagToInt16(const FloatCompFlag &v)
+ {
+ __m128i lo = _mm_castps_si128(v.m_values[0]);
+ __m128i hi = _mm_castps_si128(v.m_values[1]);
+
+ Int16CompFlag result;
+ result.m_value = _mm_packs_epi32(lo, hi);
+ return result;
+ }
+
+ static FloatCompFlag Int16FlagToFloat(const Int16CompFlag &v)
+ {
+ __m128i lo = _mm_unpacklo_epi16(v.m_value, v.m_value);
+ __m128i hi = _mm_unpackhi_epi16(v.m_value, v.m_value);
+
+ FloatCompFlag result;
+ result.m_values[0] = _mm_castsi128_ps(lo);
+ result.m_values[1] = _mm_castsi128_ps(hi);
+ return result;
+ }
+
+ static Int16CompFlag Int32FlagToInt16(const Int32CompFlag &v)
+ {
+ __m128i lo = v.m_values[0];
+ __m128i hi = v.m_values[1];
+
+ Int16CompFlag result;
+ result.m_value = _mm_packs_epi32(lo, hi);
+ return result;
+ }
+
+ static Int16CompFlag MakeBoolInt16(bool b)
+ {
+ Int16CompFlag result;
+ if (b)
+ result.m_value = _mm_set1_epi16(-1);
+ else
+ result.m_value = _mm_setzero_si128();
+ return result;
+ }
+
+ static FloatCompFlag MakeBoolFloat(bool b)
+ {
+ FloatCompFlag result;
+ if (b)
+ result.m_values[0] = result.m_values[1] = _mm_castsi128_ps(_mm_set1_epi32(-1));
+ else
+ result.m_values[0] = result.m_values[1] = _mm_setzero_ps();
+ return result;
+ }
+
+ static Int16CompFlag AndNot(const Int16CompFlag &a, const Int16CompFlag &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_andnot_si128(b.m_value, a.m_value);
+ return result;
+ }
+
+ static Int16CompFlag Not(const Int16CompFlag &b)
+ {
+ Int16CompFlag result;
+ result.m_value = _mm_xor_si128(b.m_value, _mm_set1_epi32(-1));
+ return result;
+ }
+
+ static Int32CompFlag Not(const Int32CompFlag &b)
+ {
+ Int32CompFlag result;
+ result.m_values[0] = _mm_xor_si128(b.m_values[0], _mm_set1_epi32(-1));
+ result.m_values[1] = _mm_xor_si128(b.m_values[1], _mm_set1_epi32(-1));
+ return result;
+ }
+
+ static UInt16 RoundAndConvertToU16(const Float &v, const void* /*roundingMode*/)
+ {
+ __m128i lo = _mm_cvtps_epi32(_mm_add_ps(v.m_values[0], _mm_set1_ps(-32768)));
+ __m128i hi = _mm_cvtps_epi32(_mm_add_ps(v.m_values[1], _mm_set1_ps(-32768)));
+
+ __m128i packed = _mm_packs_epi32(lo, hi);
+
+ UInt16 result;
+ result.m_value = _mm_xor_si128(packed, _mm_set1_epi16(-32768));
+ return result;
+ }
+
+ static UInt15 RoundAndConvertToU15(const Float &v, const void* /*roundingMode*/)
+ {
+ __m128i lo = _mm_cvtps_epi32(v.m_values[0]);
+ __m128i hi = _mm_cvtps_epi32(v.m_values[1]);
+
+ __m128i packed = _mm_packs_epi32(lo, hi);
+
+ UInt15 result;
+ result.m_value = _mm_packs_epi32(lo, hi);
+ return result;
+ }
+
+ static SInt16 RoundAndConvertToS16(const Float &v, const void* /*roundingMode*/)
+ {
+ __m128i lo = _mm_cvtps_epi32(v.m_values[0]);
+ __m128i hi = _mm_cvtps_epi32(v.m_values[1]);
+
+ __m128i packed = _mm_packs_epi32(lo, hi);
+
+ SInt16 result;
+ result.m_value = _mm_packs_epi32(lo, hi);
+ return result;
+ }
+
+ static Float Sqrt(const Float &f)
+ {
+ Float result;
+ for (int i = 0; i < 2; i++)
+ result.m_values[i] = _mm_sqrt_ps(f.m_values[i]);
+ return result;
+ }
+
+ static UInt16 Abs(const SInt16 &a)
+ {
+ __m128i signBitsXor = _mm_srai_epi16(a.m_value, 15);
+ __m128i signBitsAdd = _mm_srli_epi16(a.m_value, 15);
+
+ UInt16 result;
+ result.m_value = _mm_add_epi16(_mm_xor_si128(a.m_value, signBitsXor), signBitsAdd);
+ return result;
+ }
+
+ static Float Abs(const Float& a)
+ {
+ __m128 invMask = _mm_set1_ps(-0.0f);
+
+ Float result;
+ result.m_values[0] = _mm_andnot_ps(invMask, a.m_values[0]);
+ result.m_values[1] = _mm_andnot_ps(invMask, a.m_values[1]);
+ return result;
+ }
+
+ static UInt16 SqDiffUInt8(const UInt15 &a, const UInt15 &b)
+ {
+ __m128i diff = _mm_sub_epi16(a.m_value, b.m_value);
+
+ UInt16 result;
+ result.m_value = _mm_mullo_epi16(diff, diff);
+ return result;
+ }
+
+ static Float SqDiffSInt16(const SInt16 &a, const SInt16 &b)
+ {
+ __m128i diffU = _mm_sub_epi16(_mm_max_epi16(a.m_value, b.m_value), _mm_min_epi16(a.m_value, b.m_value));
+
+ __m128i mulHi = _mm_mulhi_epu16(diffU, diffU);
+ __m128i mulLo = _mm_mullo_epi16(diffU, diffU);
+ __m128i sqDiffHi = _mm_unpackhi_epi16(mulLo, mulHi);
+ __m128i sqDiffLo = _mm_unpacklo_epi16(mulLo, mulHi);
+
+ Float result;
+ result.m_values[0] = _mm_cvtepi32_ps(sqDiffLo);
+ result.m_values[1] = _mm_cvtepi32_ps(sqDiffHi);
+
+ return result;
+ }
+
+ static Float TwosCLHalfToFloat(const SInt16 &v)
+ {
+ __m128i absV = _mm_add_epi16(_mm_xor_si128(v.m_value, _mm_srai_epi16(v.m_value, 15)), _mm_srli_epi16(v.m_value, 15));
+
+ __m128i signBits = _mm_and_si128(v.m_value, _mm_set1_epi16(-32768));
+ __m128i mantissa = _mm_and_si128(v.m_value, _mm_set1_epi16(0x03ff));
+ __m128i exponent = _mm_and_si128(v.m_value, _mm_set1_epi16(0x7c00));
+
+ __m128i isDenormal = _mm_cmpeq_epi16(exponent, _mm_setzero_si128());
+
+ // Convert exponent to high-bits
+ exponent = _mm_add_epi16(_mm_srli_epi16(exponent, 3), _mm_set1_epi16(14336));
+
+ __m128i denormalCorrectionHigh = _mm_and_si128(isDenormal, _mm_or_si128(signBits, _mm_set1_epi16(14336)));
+
+ __m128i highBits = _mm_or_si128(signBits, _mm_or_si128(exponent, _mm_srli_epi16(mantissa, 3)));
+ __m128i lowBits = _mm_slli_epi16(mantissa, 13);
+
+ __m128i flow = _mm_unpacklo_epi16(lowBits, highBits);
+ __m128i fhigh = _mm_unpackhi_epi16(lowBits, highBits);
+
+ __m128i correctionLow = _mm_unpacklo_epi16(_mm_setzero_si128(), denormalCorrectionHigh);
+ __m128i correctionHigh = _mm_unpackhi_epi16(_mm_setzero_si128(), denormalCorrectionHigh);
+
+ Float result;
+ result.m_values[0] = _mm_sub_ps(_mm_castsi128_ps(flow), _mm_castsi128_ps(correctionLow));
+ result.m_values[1] = _mm_sub_ps(_mm_castsi128_ps(fhigh), _mm_castsi128_ps(correctionHigh));
+
+ return result;
+ }
+
+ static Float SqDiff2CLFloat(const SInt16 &a, const Float &b)
+ {
+ Float fa = TwosCLHalfToFloat(a);
+
+ Float diff = fa - b;
+ return diff * diff;
+ }
+
+ static Float SqDiff2CL(const SInt16 &a, const SInt16 &b)
+ {
+ Float fa = TwosCLHalfToFloat(a);
+ Float fb = TwosCLHalfToFloat(b);
+
+ Float diff = fa - fb;
+ return diff * diff;
+ }
+
+ static Float SqDiff2CLFloat(const SInt16 &a, float aWeight, const Float &b)
+ {
+ Float fa = TwosCLHalfToFloat(a) * aWeight;
+
+ Float diff = fa - b;
+ return diff * diff;
+ }
+
+ static UInt16 RightShift(const UInt16 &v, int bits)
+ {
+ UInt16 result;
+ result.m_value = _mm_srli_epi16(v.m_value, bits);
+ return result;
+ }
+
+ static UInt31 RightShift(const UInt31 &v, int bits)
+ {
+ UInt31 result;
+ result.m_values[0] = _mm_srli_epi32(v.m_values[0], bits);
+ result.m_values[1] = _mm_srli_epi32(v.m_values[1], bits);
+ return result;
+ }
+
+ static SInt16 RightShift(const SInt16 &v, int bits)
+ {
+ SInt16 result;
+ result.m_value = _mm_srai_epi16(v.m_value, bits);
+ return result;
+ }
+
+ static UInt15 RightShift(const UInt15 &v, int bits)
+ {
+ UInt15 result;
+ result.m_value = _mm_srli_epi16(v.m_value, bits);
+ return result;
+ }
+
+ static SInt32 RightShift(const SInt32 &v, int bits)
+ {
+ SInt32 result;
+ result.m_values[0] = _mm_srai_epi32(v.m_values[0], bits);
+ result.m_values[1] = _mm_srai_epi32(v.m_values[1], bits);
+ return result;
+ }
+
+ static SInt16 ToSInt16(const SInt32 &v)
+ {
+ SInt16 result;
+ result.m_value = _mm_packs_epi32(v.m_values[0], v.m_values[1]);
+ return result;
+ }
+
+ static SInt16 ToSInt16(const UInt16 &v)
+ {
+ SInt16 result;
+ result.m_value = v.m_value;
+ return result;
+ }
+
+ static SInt16 ToSInt16(const UInt15 &v)
+ {
+ SInt16 result;
+ result.m_value = v.m_value;
+ return result;
+ }
+
+ static UInt16 ToUInt16(const UInt32 &v)
+ {
+ __m128i low = _mm_srai_epi32(_mm_slli_epi32(v.m_values[0], 16), 16);
+ __m128i high = _mm_srai_epi32(_mm_slli_epi32(v.m_values[1], 16), 16);
+
+ UInt16 result;
+ result.m_value = _mm_packs_epi32(low, high);
+ return result;
+ }
+
+ static UInt16 ToUInt16(const UInt31 &v)
+ {
+ __m128i low = _mm_srai_epi32(_mm_slli_epi32(v.m_values[0], 16), 16);
+ __m128i high = _mm_srai_epi32(_mm_slli_epi32(v.m_values[1], 16), 16);
+
+ UInt16 result;
+ result.m_value = _mm_packs_epi32(low, high);
+ return result;
+ }
+
+ static UInt15 ToUInt15(const UInt31 &v)
+ {
+ UInt15 result;
+ result.m_value = _mm_packs_epi32(v.m_values[0], v.m_values[1]);
+ return result;
+ }
+
+ static UInt15 ToUInt15(const SInt16 &v)
+ {
+ UInt15 result;
+ result.m_value = v.m_value;
+ return result;
+ }
+
+ static UInt15 ToUInt15(const UInt16 &v)
+ {
+ UInt15 result;
+ result.m_value = v.m_value;
+ return result;
+ }
+
+ static SInt32 XMultiply(const SInt16 &a, const SInt16 &b)
+ {
+ __m128i high = _mm_mulhi_epi16(a.m_value, b.m_value);
+ __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
+
+ SInt32 result;
+ result.m_values[0] = _mm_unpacklo_epi16(low, high);
+ result.m_values[1] = _mm_unpackhi_epi16(low, high);
+ return result;
+ }
+
+ static SInt32 XMultiply(const SInt16 &a, const UInt15 &b)
+ {
+ __m128i high = _mm_mulhi_epi16(a.m_value, b.m_value);
+ __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
+
+ SInt32 result;
+ result.m_values[0] = _mm_unpacklo_epi16(low, high);
+ result.m_values[1] = _mm_unpackhi_epi16(low, high);
+ return result;
+ }
+
+ static SInt32 XMultiply(const UInt15 &a, const SInt16 &b)
+ {
+ return XMultiply(b, a);
+ }
+
+ static UInt32 XMultiply(const UInt16 &a, const UInt16 &b)
+ {
+ __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
+ __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
+
+ UInt32 result;
+ result.m_values[0] = _mm_unpacklo_epi16(low, high);
+ result.m_values[1] = _mm_unpackhi_epi16(low, high);
+ return result;
+ }
+
+ static UInt16 CompactMultiply(const UInt16 &a, const UInt15 &b)
+ {
+ UInt16 result;
+ result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static UInt16 CompactMultiply(const UInt15 &a, const UInt15 &b)
+ {
+ UInt16 result;
+ result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static SInt16 CompactMultiply(const SInt16 &a, const UInt15 &b)
+ {
+ SInt16 result;
+ result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static SInt16 CompactMultiply(const SInt16 &a, const SInt16 &b)
+ {
+ SInt16 result;
+ result.m_value = _mm_mullo_epi16(a.m_value, b.m_value);
+ return result;
+ }
+
+ static UInt31 XMultiply(const UInt15 &a, const UInt15 &b)
+ {
+ __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
+ __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
+
+ UInt31 result;
+ result.m_values[0] = _mm_unpacklo_epi16(low, high);
+ result.m_values[1] = _mm_unpackhi_epi16(low, high);
+ return result;
+ }
+
+ static UInt31 XMultiply(const UInt16 &a, const UInt15 &b)
+ {
+ __m128i high = _mm_mulhi_epu16(a.m_value, b.m_value);
+ __m128i low = _mm_mullo_epi16(a.m_value, b.m_value);
+
+ UInt31 result;
+ result.m_values[0] = _mm_unpacklo_epi16(low, high);
+ result.m_values[1] = _mm_unpackhi_epi16(low, high);
+ return result;
+ }
+
+ static UInt31 XMultiply(const UInt15 &a, const UInt16 &b)
+ {
+ return XMultiply(b, a);
+ }
+
+ static bool AnySet(const Int16CompFlag &v)
+ {
+ return _mm_movemask_epi8(v.m_value) != 0;
+ }
+
+ static bool AllSet(const Int16CompFlag &v)
+ {
+ return _mm_movemask_epi8(v.m_value) == 0xffff;
+ }
+
+ static bool AnySet(const FloatCompFlag &v)
+ {
+ return _mm_movemask_ps(v.m_values[0]) != 0 || _mm_movemask_ps(v.m_values[1]) != 0;
+ }
+
+ static bool AllSet(const FloatCompFlag &v)
+ {
+ return _mm_movemask_ps(v.m_values[0]) == 0xf && _mm_movemask_ps(v.m_values[1]) == 0xf;
+ }
+ };
+
+#else
+ // Scalar version
+ struct ParallelMath
+ {
+ struct RoundTowardZeroForScope
+ {
+ };
+
+ struct RoundTowardNearestForScope
+ {
+ };
+
+ struct RoundUpForScope
+ {
+ };
+
+ struct RoundDownForScope
+ {
+ };
+
+ static const int ParallelSize = 1;
+
+ enum Int16Subtype
+ {
+ IntSubtype_Signed,
+ IntSubtype_UnsignedFull,
+ IntSubtype_UnsignedTruncated,
+ IntSubtype_Abstract,
+ };
+
+ typedef int32_t SInt16;
+ typedef int32_t UInt15;
+ typedef int32_t UInt16;
+ typedef int32_t AInt16;
+
+ typedef int32_t SInt32;
+ typedef int32_t UInt31;
+ typedef int32_t UInt32;
+ typedef int32_t AInt32;
+
+ typedef int32_t ScalarUInt16;
+ typedef int32_t ScalarSInt16;
+
+ typedef float Float;
+
+ template<class TTargetType>
+ struct LosslessCast
+ {
+ static const int32_t& Cast(const int32_t &src)
+ {
+ return src;
+ }
+ };
+
+ typedef bool Int16CompFlag;
+ typedef bool FloatCompFlag;
+
+ static int32_t AbstractAdd(const int32_t &a, const int32_t &b)
+ {
+ return a + b;
+ }
+
+ static int32_t AbstractSubtract(const int32_t &a, const int32_t &b)
+ {
+ return a - b;
+ }
+
+ static float Select(bool flag, float a, float b)
+ {
+ return flag ? a : b;
+ }
+
+ static int32_t Select(bool flag, int32_t a, int32_t b)
+ {
+ return flag ? a : b;
+ }
+
+ static int32_t SelectOrZero(bool flag, int32_t a)
+ {
+ return flag ? a : 0;
+ }
+
+ static void ConditionalSet(int32_t& dest, bool flag, int32_t src)
+ {
+ if (flag)
+ dest = src;
+ }
+
+ static void ConditionalSet(bool& dest, bool flag, bool src)
+ {
+ if (flag)
+ dest = src;
+ }
+
+ static int32_t ConditionalNegate(bool flag, int32_t v)
+ {
+ return (flag) ? -v : v;
+ }
+
+ static void NotConditionalSet(int32_t& dest, bool flag, int32_t src)
+ {
+ if (!flag)
+ dest = src;
+ }
+
+ static void ConditionalSet(float& dest, bool flag, float src)
+ {
+ if (flag)
+ dest = src;
+ }
+
+ static void NotConditionalSet(float& dest, bool flag, float src)
+ {
+ if (!flag)
+ dest = src;
+ }
+
+ static void MakeSafeDenominator(float& v)
+ {
+ if (v == 0.0f)
+ v = 1.0f;
+ }
+
+ static int32_t SignedRightShift(int32_t v, int bits)
+ {
+ return v >> bits;
+ }
+
+ static int32_t TruncateToPrecisionSigned(int32_t v, int precision)
+ {
+ v = (v << (32 - precision)) & 0xffffffff;
+ return SignedRightShift(v, 32 - precision);
+ }
+
+ static int32_t TruncateToPrecisionUnsigned(int32_t v, int precision)
+ {
+ return v & ((1 << precision) - 1);
+ }
+
+ static int32_t Min(int32_t a, int32_t b)
+ {
+ if (a < b)
+ return a;
+ return b;
+ }
+
+ static float Min(float a, float b)
+ {
+ if (a < b)
+ return a;
+ return b;
+ }
+
+ static int32_t Max(int32_t a, int32_t b)
+ {
+ if (a > b)
+ return a;
+ return b;
+ }
+
+ static float Max(float a, float b)
+ {
+ if (a > b)
+ return a;
+ return b;
+ }
+
+ static float Abs(float a)
+ {
+ return fabsf(a);
+ }
+
+ static int32_t Abs(int32_t a)
+ {
+ if (a < 0)
+ return -a;
+ return a;
+ }
+
+ static float Clamp(float v, float min, float max)
+ {
+ if (v < min)
+ return min;
+ if (v > max)
+ return max;
+ return v;
+ }
+
+ static float Reciprocal(float v)
+ {
+ return 1.0f / v;
+ }
+
+ static void ConvertLDRInputs(const PixelBlockU8* inputBlocks, int pxOffset, int channel, int32_t& chOut)
+ {
+ chOut = inputBlocks[0].m_pixels[pxOffset][channel];
+ }
+
+ static void ConvertHDRInputs(const PixelBlockF16* inputBlocks, int pxOffset, int channel, int32_t& chOut)
+ {
+ chOut = inputBlocks[0].m_pixels[pxOffset][channel];
+ }
+
+ static float MakeFloat(float v)
+ {
+ return v;
+ }
+
+ static float MakeFloatZero()
+ {
+ return 0.0f;
+ }
+
+ static int32_t MakeUInt16(uint16_t v)
+ {
+ return v;
+ }
+
+ static int32_t MakeSInt16(int16_t v)
+ {
+ return v;
+ }
+
+ static int32_t MakeAInt16(int16_t v)
+ {
+ return v;
+ }
+
+ static int32_t MakeUInt15(uint16_t v)
+ {
+ return v;
+ }
+
+ static int32_t MakeSInt32(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t MakeUInt31(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t Extract(int32_t v, int offset)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ return v;
+ }
+
+ static bool Extract(bool v, int offset)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ return v;
+ }
+
+ static float Extract(float v, int offset)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ return v;
+ }
+
+ static void PutUInt16(int32_t &dest, int offset, ParallelMath::ScalarUInt16 v)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ dest = v;
+ }
+
+ static void PutUInt15(int32_t &dest, int offset, ParallelMath::ScalarUInt16 v)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ dest = v;
+ }
+
+ static void PutSInt16(int32_t &dest, int offset, ParallelMath::ScalarSInt16 v)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ dest = v;
+ }
+
+ static float ExtractFloat(float v, int offset)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ return v;
+ }
+
+ static void PutFloat(float &dest, int offset, float v)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ dest = v;
+ }
+
+ static void PutBoolInt16(bool &dest, int offset, bool v)
+ {
+ UNREFERENCED_PARAMETER(offset);
+ dest = v;
+ }
+
+ static bool Less(int32_t a, int32_t b)
+ {
+ return a < b;
+ }
+
+ static bool Less(float a, float b)
+ {
+ return a < b;
+ }
+
+ static bool LessOrEqual(int32_t a, int32_t b)
+ {
+ return a < b;
+ }
+
+ static bool LessOrEqual(float a, float b)
+ {
+ return a < b;
+ }
+
+ static bool Equal(int32_t a, int32_t b)
+ {
+ return a == b;
+ }
+
+ static bool Equal(float a, float b)
+ {
+ return a == b;
+ }
+
+ static float ToFloat(int32_t v)
+ {
+ return static_cast<float>(v);
+ }
+
+ static int32_t ToUInt31(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t ToInt32(int32_t v)
+ {
+ return v;
+ }
+
+ static bool FloatFlagToInt16(bool v)
+ {
+ return v;
+ }
+
+ static bool Int32FlagToInt16(bool v)
+ {
+ return v;
+ }
+
+ static bool Int16FlagToFloat(bool v)
+ {
+ return v;
+ }
+
+ static bool MakeBoolInt16(bool b)
+ {
+ return b;
+ }
+
+ static bool MakeBoolFloat(bool b)
+ {
+ return b;
+ }
+
+ static bool AndNot(bool a, bool b)
+ {
+ return a && !b;
+ }
+
+ static bool Not(bool b)
+ {
+ return !b;
+ }
+
+ static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundTowardZeroForScope *rtz)
+ {
+ UNREFERENCED_PARAMETER(rtz);
+ return static_cast<int>(v);
+ }
+
+ static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundUpForScope *ru)
+ {
+ UNREFERENCED_PARAMETER(ru);
+ return static_cast<int>(ceilf(v));
+ }
+
+ static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundDownForScope *rd)
+ {
+ UNREFERENCED_PARAMETER(rd);
+ return static_cast<int>(floorf(v));
+ }
+
+ static int32_t RoundAndConvertToInt(float v, const ParallelMath::RoundTowardNearestForScope *rtn)
+ {
+ UNREFERENCED_PARAMETER(rtn);
+ return static_cast<int>(floorf(v + 0.5f));
+ }
+
+ template<class TRoundMode>
+ static int32_t RoundAndConvertToU16(float v, const TRoundMode *roundingMode)
+ {
+ return RoundAndConvertToInt(v, roundingMode);
+ }
+
+ template<class TRoundMode>
+ static int32_t RoundAndConvertToU15(float v, const TRoundMode *roundingMode)
+ {
+ return RoundAndConvertToInt(v, roundingMode);
+ }
+
+ template<class TRoundMode>
+ static int32_t RoundAndConvertToS16(float v, const TRoundMode *roundingMode)
+ {
+ return RoundAndConvertToInt(v, roundingMode);
+ }
+
+ static float Sqrt(float f)
+ {
+ return sqrtf(f);
+ }
+
+ static int32_t SqDiffUInt8(int32_t a, int32_t b)
+ {
+ int32_t delta = a - b;
+ return delta * delta;
+ }
+
+ static int32_t SqDiffInt16(int32_t a, int32_t b)
+ {
+ int32_t delta = a - b;
+ return delta * delta;
+ }
+
+ static int32_t SqDiffSInt16(int32_t a, int32_t b)
+ {
+ int32_t delta = a - b;
+ return delta * delta;
+ }
+
+ static float TwosCLHalfToFloat(int32_t v)
+ {
+ int32_t absV = (v < 0) ? -v : v;
+
+ int32_t signBits = (absV & -32768);
+ int32_t mantissa = (absV & 0x03ff);
+ int32_t exponent = (absV & 0x7c00);
+
+ bool isDenormal = (exponent == 0);
+
+ // Convert exponent to high-bits
+ exponent = (exponent >> 3) + 14336;
+
+ int32_t denormalCorrection = (isDenormal ? (signBits | 14336) : 0) << 16;
+
+ int32_t fBits = ((exponent | signBits) << 16) | (mantissa << 13);
+
+ float f, correction;
+ memcpy(&f, &fBits, 4);
+ memcpy(&correction, &denormalCorrection, 4);
+
+ return f - correction;
+ }
+
+ static Float SqDiff2CLFloat(const SInt16 &a, const Float &b)
+ {
+ Float fa = TwosCLHalfToFloat(a);
+
+ Float diff = fa - b;
+ return diff * diff;
+ }
+
+ static Float SqDiff2CL(const SInt16 &a, const SInt16 &b)
+ {
+ Float fa = TwosCLHalfToFloat(a);
+ Float fb = TwosCLHalfToFloat(b);
+
+ Float diff = fa - fb;
+ return diff * diff;
+ }
+
+ static Float SqDiff2CLFloat(const SInt16 &a, float aWeight, const Float &b)
+ {
+ Float fa = TwosCLHalfToFloat(a) * aWeight;
+
+ Float diff = fa - b;
+ return diff * diff;
+ }
+
+ static int32_t RightShift(int32_t v, int bits)
+ {
+ return SignedRightShift(v, bits);
+ }
+
+ static int32_t ToSInt16(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t ToUInt16(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t ToUInt15(int32_t v)
+ {
+ return v;
+ }
+
+ static int32_t XMultiply(int32_t a, int32_t b)
+ {
+ return a * b;
+ }
+
+ static int32_t CompactMultiply(int32_t a, int32_t b)
+ {
+ return a * b;
+ }
+
+ static bool AnySet(bool v)
+ {
+ return v;
+ }
+
+ static bool AllSet(bool v)
+ {
+ return v;
+ }
+ };
+
+#endif
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_S3TC.cpp b/thirdparty/cvtt/ConvectionKernels_S3TC.cpp
new file mode 100644
index 0000000000..23f1bd3314
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_S3TC.cpp
@@ -0,0 +1,1054 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels_S3TC.h"
+
+#include "ConvectionKernels_AggregatedError.h"
+#include "ConvectionKernels_BCCommon.h"
+#include "ConvectionKernels_EndpointRefiner.h"
+#include "ConvectionKernels_EndpointSelector.h"
+#include "ConvectionKernels_IndexSelector.h"
+#include "ConvectionKernels_UnfinishedEndpoints.h"
+#include "ConvectionKernels_S3TC_SingleColor.h"
+
+void cvtt::Internal::S3TCComputer::Init(MFloat& error)
+{
+ error = ParallelMath::MakeFloat(FLT_MAX);
+}
+
+void cvtt::Internal::S3TCComputer::QuantizeTo6Bits(MUInt15& v)
+{
+ MUInt15 reduced = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(v, ParallelMath::MakeUInt15(253)) + ParallelMath::MakeUInt16(512), 10));
+ v = (reduced << 2) | ParallelMath::RightShift(reduced, 4);
+}
+
+void cvtt::Internal::S3TCComputer::QuantizeTo5Bits(MUInt15& v)
+{
+ MUInt15 reduced = ParallelMath::LosslessCast<MUInt15>::Cast(ParallelMath::RightShift(ParallelMath::CompactMultiply(v, ParallelMath::MakeUInt15(249)) + ParallelMath::MakeUInt16(1024), 11));
+ v = (reduced << 3) | ParallelMath::RightShift(reduced, 2);
+}
+
+void cvtt::Internal::S3TCComputer::QuantizeTo565(MUInt15 endPoint[3])
+{
+ QuantizeTo5Bits(endPoint[0]);
+ QuantizeTo6Bits(endPoint[1]);
+ QuantizeTo5Bits(endPoint[2]);
+}
+
+cvtt::ParallelMath::Float cvtt::Internal::S3TCComputer::ParanoidFactorForSpan(const MSInt16& span)
+{
+ return ParallelMath::Abs(ParallelMath::ToFloat(span)) * 0.03f;
+}
+
+cvtt::ParallelMath::Float cvtt::Internal::S3TCComputer::ParanoidDiff(const MUInt15& a, const MUInt15& b, const MFloat& d)
+{
+ MFloat absDiff = ParallelMath::Abs(ParallelMath::ToFloat(ParallelMath::LosslessCast<MSInt16>::Cast(a) - ParallelMath::LosslessCast<MSInt16>::Cast(b)));
+ absDiff = absDiff + d;
+ return absDiff * absDiff;
+}
+
+void cvtt::Internal::S3TCComputer::TestSingleColor(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], int range, const float* channelWeights,
+ MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ float channelWeightsSq[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
+
+ MUInt15 totals[3] = { ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(0) };
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ totals[ch] = totals[ch] + pixels[px][ch];
+ }
+
+ MUInt15 average[3];
+ for (int ch = 0; ch < 3; ch++)
+ average[ch] = ParallelMath::RightShift(totals[ch] + ParallelMath::MakeUInt15(8), 4);
+
+ const Tables::S3TCSC::TableEntry* rbTable = NULL;
+ const Tables::S3TCSC::TableEntry* gTable = NULL;
+ if (flags & cvtt::Flags::S3TC_Paranoid)
+ {
+ if (range == 4)
+ {
+ rbTable = Tables::S3TCSC::g_singleColor5_3_p;
+ gTable = Tables::S3TCSC::g_singleColor6_3_p;
+ }
+ else
+ {
+ assert(range == 3);
+ rbTable = Tables::S3TCSC::g_singleColor5_2_p;
+ gTable = Tables::S3TCSC::g_singleColor6_2_p;
+ }
+ }
+ else
+ {
+ if (range == 4)
+ {
+ rbTable = Tables::S3TCSC::g_singleColor5_3;
+ gTable = Tables::S3TCSC::g_singleColor6_3;
+ }
+ else
+ {
+ assert(range == 3);
+ rbTable = Tables::S3TCSC::g_singleColor5_2;
+ gTable = Tables::S3TCSC::g_singleColor6_2;
+ }
+ }
+
+ MUInt15 interpolated[3];
+ MUInt15 eps[2][3];
+ MSInt16 spans[3];
+ for (int i = 0; i < ParallelMath::ParallelSize; i++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ {
+ uint16_t avg = ParallelMath::Extract(average[ch], i);
+ const Tables::S3TCSC::TableEntry& tableEntry = ((ch == 1) ? gTable[avg] : rbTable[avg]);
+ ParallelMath::PutUInt15(eps[0][ch], i, tableEntry.m_min);
+ ParallelMath::PutUInt15(eps[1][ch], i, tableEntry.m_max);
+ ParallelMath::PutUInt15(interpolated[ch], i, tableEntry.m_actualColor);
+ ParallelMath::PutSInt16(spans[ch], i, tableEntry.m_span);
+ }
+ }
+
+ MFloat error = ParallelMath::MakeFloatZero();
+ if (flags & cvtt::Flags::S3TC_Paranoid)
+ {
+ MFloat spanParanoidFactors[3];
+ for (int ch = 0; ch < 3; ch++)
+ spanParanoidFactors[ch] = ParanoidFactorForSpan(spans[ch]);
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ error = error + ParanoidDiff(interpolated[ch], pixels[px][ch], spanParanoidFactors[ch]) * channelWeightsSq[ch];
+ }
+ }
+ else
+ {
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ error = error + ParallelMath::ToFloat(ParallelMath::SqDiffUInt8(interpolated[ch], pixels[px][ch])) * channelWeightsSq[ch];
+ }
+ }
+
+ ParallelMath::FloatCompFlag better = ParallelMath::Less(error, bestError);
+ ParallelMath::Int16CompFlag better16 = ParallelMath::FloatFlagToInt16(better);
+
+ if (ParallelMath::AnySet(better16))
+ {
+ bestError = ParallelMath::Min(bestError, error);
+ for (int epi = 0; epi < 2; epi++)
+ for (int ch = 0; ch < 3; ch++)
+ ParallelMath::ConditionalSet(bestEndpoints[epi][ch], better16, eps[epi][ch]);
+
+ MUInt15 vindexes = ParallelMath::MakeUInt15(1);
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestIndexes[px], better16, vindexes);
+
+ ParallelMath::ConditionalSet(bestRange, better16, ParallelMath::MakeUInt15(range));
+ }
+}
+
+void cvtt::Internal::S3TCComputer::TestEndpoints(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], const MUInt15 unquantizedEndPoints[2][3], int range, const float* channelWeights,
+ MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, EndpointRefiner<3> *refiner, const ParallelMath::RoundTowardNearestForScope *rtn)
+{
+ float channelWeightsSq[3];
+
+ for (int ch = 0; ch < 3; ch++)
+ channelWeightsSq[ch] = channelWeights[ch] * channelWeights[ch];
+
+ MUInt15 endPoints[2][3];
+
+ for (int ep = 0; ep < 2; ep++)
+ for (int ch = 0; ch < 3; ch++)
+ endPoints[ep][ch] = unquantizedEndPoints[ep][ch];
+
+ QuantizeTo565(endPoints[0]);
+ QuantizeTo565(endPoints[1]);
+
+ IndexSelector<3> selector;
+ selector.Init<false>(channelWeights, endPoints, range);
+
+ MUInt15 indexes[16];
+
+ MFloat paranoidFactors[3];
+ for (int ch = 0; ch < 3; ch++)
+ paranoidFactors[ch] = ParanoidFactorForSpan(ParallelMath::LosslessCast<MSInt16>::Cast(endPoints[0][ch]) - ParallelMath::LosslessCast<MSInt16>::Cast(endPoints[1][ch]));
+
+ MFloat error = ParallelMath::MakeFloatZero();
+ AggregatedError<3> aggError;
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 index = selector.SelectIndexLDR(floatPixels[px], rtn);
+ indexes[px] = index;
+
+ if (refiner)
+ refiner->ContributeUnweightedPW(preWeightedPixels[px], index);
+
+ MUInt15 reconstructed[3];
+ selector.ReconstructLDRPrecise(index, reconstructed);
+
+ if (flags & Flags::S3TC_Paranoid)
+ {
+ for (int ch = 0; ch < 3; ch++)
+ error = error + ParanoidDiff(reconstructed[ch], pixels[px][ch], paranoidFactors[ch]) * channelWeightsSq[ch];
+ }
+ else
+ BCCommon::ComputeErrorLDR<3>(flags, reconstructed, pixels[px], aggError);
+ }
+
+ if (!(flags & Flags::S3TC_Paranoid))
+ error = aggError.Finalize(flags, channelWeightsSq);
+
+ ParallelMath::FloatCompFlag better = ParallelMath::Less(error, bestError);
+
+ if (ParallelMath::AnySet(better))
+ {
+ ParallelMath::Int16CompFlag betterInt16 = ParallelMath::FloatFlagToInt16(better);
+
+ ParallelMath::ConditionalSet(bestError, better, error);
+
+ for (int ep = 0; ep < 2; ep++)
+ for (int ch = 0; ch < 3; ch++)
+ ParallelMath::ConditionalSet(bestEndpoints[ep][ch], betterInt16, endPoints[ep][ch]);
+
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestIndexes[px], betterInt16, indexes[px]);
+
+ ParallelMath::ConditionalSet(bestRange, betterInt16, ParallelMath::MakeUInt15(static_cast<uint16_t>(range)));
+ }
+}
+
+void cvtt::Internal::S3TCComputer::TestCounts(uint32_t flags, const int *counts, int nCounts, const MUInt15 &numElements, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], bool alphaTest,
+ const MFloat floatSortedInputs[16][4], const MFloat preWeightedFloatSortedInputs[16][4], const float *channelWeights, MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange,
+ const ParallelMath::RoundTowardNearestForScope* rtn)
+{
+ UNREFERENCED_PARAMETER(alphaTest);
+ UNREFERENCED_PARAMETER(flags);
+
+ EndpointRefiner<3> refiner;
+
+ refiner.Init(nCounts, channelWeights);
+
+ bool escape = false;
+ int e = 0;
+ for (int i = 0; i < nCounts; i++)
+ {
+ for (int n = 0; n < counts[i]; n++)
+ {
+ ParallelMath::Int16CompFlag valid = ParallelMath::Less(ParallelMath::MakeUInt15(static_cast<uint16_t>(n)), numElements);
+ if (!ParallelMath::AnySet(valid))
+ {
+ escape = true;
+ break;
+ }
+
+ if (ParallelMath::AllSet(valid))
+ refiner.ContributeUnweightedPW(preWeightedFloatSortedInputs[e++], ParallelMath::MakeUInt15(static_cast<uint16_t>(i)));
+ else
+ {
+ MFloat weight = ParallelMath::Select(ParallelMath::Int16FlagToFloat(valid), ParallelMath::MakeFloat(1.0f), ParallelMath::MakeFloat(0.0f));
+ refiner.ContributePW(preWeightedFloatSortedInputs[e++], ParallelMath::MakeUInt15(static_cast<uint16_t>(i)), weight);
+ }
+ }
+
+ if (escape)
+ break;
+ }
+
+ MUInt15 endPoints[2][3];
+ refiner.GetRefinedEndpointsLDR(endPoints, rtn);
+
+ TestEndpoints(flags, pixels, floatPixels, preWeightedPixels, endPoints, nCounts, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, NULL, rtn);
+}
+
+void cvtt::Internal::S3TCComputer::PackExplicitAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride)
+{
+ UNREFERENCED_PARAMETER(flags);
+ ParallelMath::RoundTowardNearestForScope rtn;
+
+ float weights[1] = { 1.0f };
+
+ MUInt15 pixels[16];
+ MFloat floatPixels[16];
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::ConvertLDRInputs(inputs, px, inputChannel, pixels[px]);
+ floatPixels[px] = ParallelMath::ToFloat(pixels[px]);
+ }
+
+ MUInt15 ep[2][1] = { { ParallelMath::MakeUInt15(0) },{ ParallelMath::MakeUInt15(255) } };
+
+ IndexSelector<1> selector;
+ selector.Init<false>(weights, ep, 16);
+
+ MUInt15 indexes[16];
+
+ for (int px = 0; px < 16; px++)
+ indexes[px] = selector.SelectIndexLDR(&floatPixels[px], &rtn);
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ for (int px = 0; px < 16; px += 2)
+ {
+ int index0 = ParallelMath::Extract(indexes[px], block);
+ int index1 = ParallelMath::Extract(indexes[px + 1], block);
+
+ packedBlocks[px / 2] = static_cast<uint8_t>(index0 | (index1 << 4));
+ }
+
+ packedBlocks += packedBlockStride;
+ }
+}
+
+void cvtt::Internal::S3TCComputer::PackInterpolatedAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride, bool isSigned, int maxTweakRounds, int numRefineRounds)
+{
+ if (maxTweakRounds < 1)
+ maxTweakRounds = 1;
+
+ if (numRefineRounds < 1)
+ numRefineRounds = 1;
+
+ ParallelMath::RoundTowardNearestForScope rtn;
+
+ float oneWeight[1] = { 1.0f };
+
+ MUInt15 pixels[16];
+ MFloat floatPixels[16];
+
+ MUInt15 highTerminal = isSigned ? ParallelMath::MakeUInt15(254) : ParallelMath::MakeUInt15(255);
+ MUInt15 highTerminalMinusOne = highTerminal - ParallelMath::MakeUInt15(1);
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::ConvertLDRInputs(inputs, px, inputChannel, pixels[px]);
+
+ if (isSigned)
+ pixels[px] = ParallelMath::Min(pixels[px], highTerminal);
+
+ floatPixels[px] = ParallelMath::ToFloat(pixels[px]);
+ }
+
+ MUInt15 sortedPixels[16];
+ for (int px = 0; px < 16; px++)
+ sortedPixels[px] = pixels[px];
+
+ for (int sortEnd = 15; sortEnd > 0; sortEnd--)
+ {
+ for (int sortOffset = 0; sortOffset < sortEnd; sortOffset++)
+ {
+ MUInt15 a = sortedPixels[sortOffset];
+ MUInt15 b = sortedPixels[sortOffset + 1];
+
+ sortedPixels[sortOffset] = ParallelMath::Min(a, b);
+ sortedPixels[sortOffset + 1] = ParallelMath::Max(a, b);
+ }
+ }
+
+ MUInt15 zero = ParallelMath::MakeUInt15(0);
+ MUInt15 one = ParallelMath::MakeUInt15(1);
+
+ MUInt15 bestIsFullRange = zero;
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+ MUInt15 bestEP[2] = { zero, zero };
+ MUInt15 bestIndexes[16] = {
+ zero, zero, zero, zero,
+ zero, zero, zero, zero,
+ zero, zero, zero, zero,
+ zero, zero, zero, zero
+ };
+
+ // Full-precision
+ {
+ MUInt15 minEP = sortedPixels[0];
+ MUInt15 maxEP = sortedPixels[15];
+
+ MFloat base[1] = { ParallelMath::ToFloat(minEP) };
+ MFloat offset[1] = { ParallelMath::ToFloat(maxEP - minEP) };
+
+ UnfinishedEndpoints<1> ufep = UnfinishedEndpoints<1>(base, offset);
+
+ int numTweakRounds = BCCommon::TweakRoundsForRange(8);
+ if (numTweakRounds > maxTweakRounds)
+ numTweakRounds = maxTweakRounds;
+
+ for (int tweak = 0; tweak < numTweakRounds; tweak++)
+ {
+ MUInt15 ep[2][1];
+
+ ufep.FinishLDR(tweak, 8, ep[0], ep[1]);
+
+ for (int refinePass = 0; refinePass < numRefineRounds; refinePass++)
+ {
+ EndpointRefiner<1> refiner;
+ refiner.Init(8, oneWeight);
+
+ if (isSigned)
+ for (int epi = 0; epi < 2; epi++)
+ ep[epi][0] = ParallelMath::Min(ep[epi][0], highTerminal);
+
+ IndexSelector<1> indexSelector;
+ indexSelector.Init<false>(oneWeight, ep, 8);
+
+ MUInt15 indexes[16];
+
+ AggregatedError<1> aggError;
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 index = indexSelector.SelectIndexLDR(&floatPixels[px], &rtn);
+
+ MUInt15 reconstructedPixel;
+
+ indexSelector.ReconstructLDRPrecise(index, &reconstructedPixel);
+ BCCommon::ComputeErrorLDR<1>(flags, &reconstructedPixel, &pixels[px], aggError);
+
+ if (refinePass != numRefineRounds - 1)
+ refiner.ContributeUnweightedPW(&floatPixels[px], index);
+
+ indexes[px] = index;
+ }
+ MFloat error = aggError.Finalize(flags | Flags::Uniform, oneWeight);
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
+ ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
+
+ if (ParallelMath::AnySet(errorBetter16))
+ {
+ bestError = ParallelMath::Min(error, bestError);
+ ParallelMath::ConditionalSet(bestIsFullRange, errorBetter16, one);
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestIndexes[px], errorBetter16, indexes[px]);
+
+ for (int epi = 0; epi < 2; epi++)
+ ParallelMath::ConditionalSet(bestEP[epi], errorBetter16, ep[epi][0]);
+ }
+
+ if (refinePass != numRefineRounds - 1)
+ refiner.GetRefinedEndpointsLDR(ep, &rtn);
+ }
+ }
+ }
+
+ // Reduced precision with special endpoints
+ {
+ MUInt15 bestHeuristicMin = sortedPixels[0];
+ MUInt15 bestHeuristicMax = sortedPixels[15];
+
+ ParallelMath::Int16CompFlag canTryClipping;
+
+ // In reduced precision, we want try putting endpoints at the reserved indexes at the ends.
+ // The heuristic we use is to assign indexes to the end as long as they aren't off by more than half of the index range.
+ // This will usually not find anything, but it's cheap to check.
+
+ {
+ MUInt15 largestPossibleRange = bestHeuristicMax - bestHeuristicMin; // Max: 255
+ MUInt15 lowestPossibleClearance = ParallelMath::Min(bestHeuristicMin, static_cast<MUInt15>(highTerminal - bestHeuristicMax));
+
+ MUInt15 lowestPossibleClearanceTimes10 = (lowestPossibleClearance << 2) + (lowestPossibleClearance << 4);
+ canTryClipping = ParallelMath::LessOrEqual(lowestPossibleClearanceTimes10, largestPossibleRange);
+ }
+
+ if (ParallelMath::AnySet(canTryClipping))
+ {
+ MUInt15 lowClearances[16];
+ MUInt15 highClearances[16];
+ MUInt15 bestSkipCount = ParallelMath::MakeUInt15(0);
+
+ lowClearances[0] = highClearances[0] = ParallelMath::MakeUInt15(0);
+
+ for (int px = 1; px < 16; px++)
+ {
+ lowClearances[px] = sortedPixels[px - 1];
+ highClearances[px] = highTerminal - sortedPixels[16 - px];
+ }
+
+ for (uint16_t firstIndex = 0; firstIndex < 16; firstIndex++)
+ {
+ uint16_t numSkippedLow = firstIndex;
+
+ MUInt15 lowClearance = lowClearances[firstIndex];
+
+ for (uint16_t lastIndex = firstIndex; lastIndex < 16; lastIndex++)
+ {
+ uint16_t numSkippedHigh = 15 - lastIndex;
+ uint16_t numSkipped = numSkippedLow + numSkippedHigh;
+
+ MUInt15 numSkippedV = ParallelMath::MakeUInt15(numSkipped);
+
+ ParallelMath::Int16CompFlag areMoreSkipped = ParallelMath::Less(bestSkipCount, numSkippedV);
+
+ if (!ParallelMath::AnySet(areMoreSkipped))
+ continue;
+
+ MUInt15 clearance = ParallelMath::Max(highClearances[numSkippedHigh], lowClearance);
+ MUInt15 clearanceTimes10 = (clearance << 2) + (clearance << 4);
+
+ MUInt15 range = sortedPixels[lastIndex] - sortedPixels[firstIndex];
+
+ ParallelMath::Int16CompFlag isBetter = (areMoreSkipped & ParallelMath::LessOrEqual(clearanceTimes10, range));
+ ParallelMath::ConditionalSet(bestHeuristicMin, isBetter, sortedPixels[firstIndex]);
+ ParallelMath::ConditionalSet(bestHeuristicMax, isBetter, sortedPixels[lastIndex]);
+ }
+ }
+ }
+
+ MUInt15 bestSimpleMin = one;
+ MUInt15 bestSimpleMax = highTerminalMinusOne;
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::ConditionalSet(bestSimpleMin, ParallelMath::Less(zero, sortedPixels[15 - px]), sortedPixels[15 - px]);
+ ParallelMath::ConditionalSet(bestSimpleMax, ParallelMath::Less(sortedPixels[px], highTerminal), sortedPixels[px]);
+ }
+
+ MUInt15 minEPs[2] = { bestSimpleMin, bestHeuristicMin };
+ MUInt15 maxEPs[2] = { bestSimpleMax, bestHeuristicMax };
+
+ int minEPRange = 2;
+ if (ParallelMath::AllSet(ParallelMath::Equal(minEPs[0], minEPs[1])))
+ minEPRange = 1;
+
+ int maxEPRange = 2;
+ if (ParallelMath::AllSet(ParallelMath::Equal(maxEPs[0], maxEPs[1])))
+ maxEPRange = 1;
+
+ for (int minEPIndex = 0; minEPIndex < minEPRange; minEPIndex++)
+ {
+ for (int maxEPIndex = 0; maxEPIndex < maxEPRange; maxEPIndex++)
+ {
+ MFloat base[1] = { ParallelMath::ToFloat(minEPs[minEPIndex]) };
+ MFloat offset[1] = { ParallelMath::ToFloat(maxEPs[maxEPIndex] - minEPs[minEPIndex]) };
+
+ UnfinishedEndpoints<1> ufep = UnfinishedEndpoints<1>(base, offset);
+
+ int numTweakRounds = BCCommon::TweakRoundsForRange(6);
+ if (numTweakRounds > maxTweakRounds)
+ numTweakRounds = maxTweakRounds;
+
+ for (int tweak = 0; tweak < numTweakRounds; tweak++)
+ {
+ MUInt15 ep[2][1];
+
+ ufep.FinishLDR(tweak, 8, ep[0], ep[1]);
+
+ for (int refinePass = 0; refinePass < numRefineRounds; refinePass++)
+ {
+ EndpointRefiner<1> refiner;
+ refiner.Init(6, oneWeight);
+
+ if (isSigned)
+ for (int epi = 0; epi < 2; epi++)
+ ep[epi][0] = ParallelMath::Min(ep[epi][0], highTerminal);
+
+ IndexSelector<1> indexSelector;
+ indexSelector.Init<false>(oneWeight, ep, 6);
+
+ MUInt15 indexes[16];
+ MFloat error = ParallelMath::MakeFloatZero();
+
+ for (int px = 0; px < 16; px++)
+ {
+ MUInt15 selectedIndex = indexSelector.SelectIndexLDR(&floatPixels[px], &rtn);
+
+ MUInt15 reconstructedPixel;
+
+ indexSelector.ReconstructLDRPrecise(selectedIndex, &reconstructedPixel);
+
+ MFloat zeroError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &zero, &pixels[px], 1, oneWeight);
+ MFloat highTerminalError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &highTerminal, &pixels[px], 1, oneWeight);
+ MFloat selectedIndexError = BCCommon::ComputeErrorLDRSimple<1>(flags | Flags::Uniform, &reconstructedPixel, &pixels[px], 1, oneWeight);
+
+ MFloat bestPixelError = zeroError;
+ MUInt15 index = ParallelMath::MakeUInt15(6);
+
+ ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(ParallelMath::Less(highTerminalError, bestPixelError)), ParallelMath::MakeUInt15(7));
+ bestPixelError = ParallelMath::Min(bestPixelError, highTerminalError);
+
+ ParallelMath::FloatCompFlag selectedIndexBetter = ParallelMath::Less(selectedIndexError, bestPixelError);
+
+ if (ParallelMath::AllSet(selectedIndexBetter))
+ {
+ if (refinePass != numRefineRounds - 1)
+ refiner.ContributeUnweightedPW(&floatPixels[px], selectedIndex);
+ }
+ else
+ {
+ MFloat refineWeight = ParallelMath::Select(selectedIndexBetter, ParallelMath::MakeFloat(1.0f), ParallelMath::MakeFloatZero());
+
+ if (refinePass != numRefineRounds - 1)
+ refiner.ContributePW(&floatPixels[px], selectedIndex, refineWeight);
+ }
+
+ ParallelMath::ConditionalSet(index, ParallelMath::FloatFlagToInt16(selectedIndexBetter), selectedIndex);
+ bestPixelError = ParallelMath::Min(bestPixelError, selectedIndexError);
+
+ error = error + bestPixelError;
+
+ indexes[px] = index;
+ }
+
+ ParallelMath::FloatCompFlag errorBetter = ParallelMath::Less(error, bestError);
+ ParallelMath::Int16CompFlag errorBetter16 = ParallelMath::FloatFlagToInt16(errorBetter);
+
+ if (ParallelMath::AnySet(errorBetter16))
+ {
+ bestError = ParallelMath::Min(error, bestError);
+ ParallelMath::ConditionalSet(bestIsFullRange, errorBetter16, zero);
+ for (int px = 0; px < 16; px++)
+ ParallelMath::ConditionalSet(bestIndexes[px], errorBetter16, indexes[px]);
+
+ for (int epi = 0; epi < 2; epi++)
+ ParallelMath::ConditionalSet(bestEP[epi], errorBetter16, ep[epi][0]);
+ }
+
+ if (refinePass != numRefineRounds - 1)
+ refiner.GetRefinedEndpointsLDR(ep, &rtn);
+ }
+ }
+ }
+ }
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ int ep0 = ParallelMath::Extract(bestEP[0], block);
+ int ep1 = ParallelMath::Extract(bestEP[1], block);
+ int isFullRange = ParallelMath::Extract(bestIsFullRange, block);
+
+ if (isSigned)
+ {
+ ep0 -= 127;
+ ep1 -= 127;
+
+ assert(ep0 >= -127 && ep0 <= 127);
+ assert(ep1 >= -127 && ep1 <= 127);
+ }
+
+
+ bool swapEndpoints = (isFullRange != 0) != (ep0 > ep1);
+
+ if (swapEndpoints)
+ std::swap(ep0, ep1);
+
+ uint16_t dumpBits = 0;
+ int dumpBitsOffset = 0;
+ int dumpByteOffset = 2;
+ packedBlocks[0] = static_cast<uint8_t>(ep0 & 0xff);
+ packedBlocks[1] = static_cast<uint8_t>(ep1 & 0xff);
+
+ int maxValue = (isFullRange != 0) ? 7 : 5;
+
+ for (int px = 0; px < 16; px++)
+ {
+ int index = ParallelMath::Extract(bestIndexes[px], block);
+
+ if (swapEndpoints && index <= maxValue)
+ index = maxValue - index;
+
+ if (index != 0)
+ {
+ if (index == maxValue)
+ index = 1;
+ else if (index < maxValue)
+ index++;
+ }
+
+ assert(index >= 0 && index < 8);
+
+ dumpBits |= static_cast<uint16_t>(index << dumpBitsOffset);
+ dumpBitsOffset += 3;
+
+ if (dumpBitsOffset >= 8)
+ {
+ assert(dumpByteOffset < 8);
+ packedBlocks[dumpByteOffset] = static_cast<uint8_t>(dumpBits & 0xff);
+ dumpBits >>= 8;
+ dumpBitsOffset -= 8;
+ dumpByteOffset++;
+ }
+ }
+
+ assert(dumpBitsOffset == 0);
+ assert(dumpByteOffset == 8);
+
+ packedBlocks += packedBlockStride;
+ }
+}
+
+void cvtt::Internal::S3TCComputer::PackRGB(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, size_t packedBlockStride, const float channelWeights[4], bool alphaTest, float alphaThreshold, bool exhaustive, int maxTweakRounds, int numRefineRounds)
+{
+ ParallelMath::RoundTowardNearestForScope rtn;
+
+ if (numRefineRounds < 1)
+ numRefineRounds = 1;
+
+ if (maxTweakRounds < 1)
+ maxTweakRounds = 1;
+
+ EndpointSelector<3, 8> endpointSelector;
+
+ MUInt15 pixels[16][4];
+ MFloat floatPixels[16][4];
+
+ MFloat preWeightedPixels[16][4];
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ ParallelMath::ConvertLDRInputs(inputs, px, ch, pixels[px][ch]);
+ }
+
+ for (int px = 0; px < 16; px++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ floatPixels[px][ch] = ParallelMath::ToFloat(pixels[px][ch]);
+ }
+
+ if (alphaTest)
+ {
+ MUInt15 threshold = ParallelMath::MakeUInt15(static_cast<uint16_t>(floor(alphaThreshold * 255.0f + 0.5f)));
+
+ for (int px = 0; px < 16; px++)
+ {
+ ParallelMath::Int16CompFlag belowThreshold = ParallelMath::Less(pixels[px][3], threshold);
+ pixels[px][3] = ParallelMath::Select(belowThreshold, ParallelMath::MakeUInt15(0), ParallelMath::MakeUInt15(255));
+ }
+ }
+
+ BCCommon::PreWeightPixelsLDR<4>(preWeightedPixels, pixels, channelWeights);
+
+ MUInt15 minAlpha = ParallelMath::MakeUInt15(255);
+
+ for (int px = 0; px < 16; px++)
+ minAlpha = ParallelMath::Min(minAlpha, pixels[px][3]);
+
+ MFloat pixelWeights[16];
+ for (int px = 0; px < 16; px++)
+ {
+ pixelWeights[px] = ParallelMath::MakeFloat(1.0f);
+ if (alphaTest)
+ {
+ ParallelMath::Int16CompFlag isTransparent = ParallelMath::Less(pixels[px][3], ParallelMath::MakeUInt15(255));
+
+ ParallelMath::ConditionalSet(pixelWeights[px], ParallelMath::Int16FlagToFloat(isTransparent), ParallelMath::MakeFloatZero());
+ }
+ }
+
+ for (int pass = 0; pass < NumEndpointSelectorPasses; pass++)
+ {
+ for (int px = 0; px < 16; px++)
+ endpointSelector.ContributePass(preWeightedPixels[px], pass, pixelWeights[px]);
+
+ endpointSelector.FinishPass(pass);
+ }
+
+ UnfinishedEndpoints<3> ufep = endpointSelector.GetEndpoints(channelWeights);
+
+ MUInt15 bestEndpoints[2][3];
+ MUInt15 bestIndexes[16];
+ MUInt15 bestRange = ParallelMath::MakeUInt15(0);
+ MFloat bestError = ParallelMath::MakeFloat(FLT_MAX);
+
+ for (int px = 0; px < 16; px++)
+ bestIndexes[px] = ParallelMath::MakeUInt15(0);
+
+ for (int ep = 0; ep < 2; ep++)
+ for (int ch = 0; ch < 3; ch++)
+ bestEndpoints[ep][ch] = ParallelMath::MakeUInt15(0);
+
+ if (exhaustive)
+ {
+ MSInt16 sortBins[16];
+
+ {
+ // Compute an 11-bit index, change it to signed, stuff it in the high bits of the sort bins,
+ // and pack the original indexes into the low bits.
+
+ MUInt15 sortEP[2][3];
+ ufep.FinishLDR(0, 11, sortEP[0], sortEP[1]);
+
+ IndexSelector<3> sortSelector;
+ sortSelector.Init<false>(channelWeights, sortEP, 1 << 11);
+
+ for (int16_t px = 0; px < 16; px++)
+ {
+ MSInt16 sortBin = ParallelMath::LosslessCast<MSInt16>::Cast(sortSelector.SelectIndexLDR(floatPixels[px], &rtn) << 4);
+
+ if (alphaTest)
+ {
+ ParallelMath::Int16CompFlag isTransparent = ParallelMath::Less(pixels[px][3], ParallelMath::MakeUInt15(255));
+
+ ParallelMath::ConditionalSet(sortBin, isTransparent, ParallelMath::MakeSInt16(-16)); // 0xfff0
+ }
+
+ sortBin = sortBin + ParallelMath::MakeSInt16(px);
+
+ sortBins[px] = sortBin;
+ }
+ }
+
+ // Sort bins
+ for (int sortEnd = 1; sortEnd < 16; sortEnd++)
+ {
+ for (int sortLoc = sortEnd; sortLoc > 0; sortLoc--)
+ {
+ MSInt16 a = sortBins[sortLoc];
+ MSInt16 b = sortBins[sortLoc - 1];
+
+ sortBins[sortLoc] = ParallelMath::Max(a, b);
+ sortBins[sortLoc - 1] = ParallelMath::Min(a, b);
+ }
+ }
+
+ MUInt15 firstElement = ParallelMath::MakeUInt15(0);
+ for (uint16_t e = 0; e < 16; e++)
+ {
+ ParallelMath::Int16CompFlag isInvalid = ParallelMath::Less(sortBins[e], ParallelMath::MakeSInt16(0));
+ ParallelMath::ConditionalSet(firstElement, isInvalid, ParallelMath::MakeUInt15(e + 1));
+ if (!ParallelMath::AnySet(isInvalid))
+ break;
+ }
+
+ MUInt15 numElements = ParallelMath::MakeUInt15(16) - firstElement;
+
+ MUInt15 sortedInputs[16][4];
+ MFloat floatSortedInputs[16][4];
+ MFloat pwFloatSortedInputs[16][4];
+
+ for (int e = 0; e < 16; e++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ sortedInputs[e][ch] = ParallelMath::MakeUInt15(0);
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ for (int e = ParallelMath::Extract(firstElement, block); e < 16; e++)
+ {
+ ParallelMath::ScalarUInt16 sortBin = ParallelMath::Extract(sortBins[e], block);
+ int originalIndex = (sortBin & 15);
+
+ for (int ch = 0; ch < 4; ch++)
+ ParallelMath::PutUInt15(sortedInputs[15 - e][ch], block, ParallelMath::Extract(pixels[originalIndex][ch], block));
+ }
+ }
+
+ for (int e = 0; e < 16; e++)
+ {
+ for (int ch = 0; ch < 4; ch++)
+ {
+ MFloat f = ParallelMath::ToFloat(sortedInputs[e][ch]);
+ floatSortedInputs[e][ch] = f;
+ pwFloatSortedInputs[e][ch] = f * channelWeights[ch];
+ }
+ }
+
+ for (int n0 = 0; n0 <= 15; n0++)
+ {
+ int remainingFor1 = 16 - n0;
+ if (remainingFor1 == 16)
+ remainingFor1 = 15;
+
+ for (int n1 = 0; n1 <= remainingFor1; n1++)
+ {
+ int remainingFor2 = 16 - n1 - n0;
+ if (remainingFor2 == 16)
+ remainingFor2 = 15;
+
+ for (int n2 = 0; n2 <= remainingFor2; n2++)
+ {
+ int n3 = 16 - n2 - n1 - n0;
+
+ if (n3 == 16)
+ continue;
+
+ int counts[4] = { n0, n1, n2, n3 };
+
+ TestCounts(flags, counts, 4, numElements, pixels, floatPixels, preWeightedPixels, alphaTest, floatSortedInputs, pwFloatSortedInputs, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
+ }
+ }
+ }
+
+ TestSingleColor(flags, pixels, floatPixels, 4, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
+
+ if (alphaTest)
+ {
+ for (int n0 = 0; n0 <= 15; n0++)
+ {
+ int remainingFor1 = 16 - n0;
+ if (remainingFor1 == 16)
+ remainingFor1 = 15;
+
+ for (int n1 = 0; n1 <= remainingFor1; n1++)
+ {
+ int n2 = 16 - n1 - n0;
+
+ if (n2 == 16)
+ continue;
+
+ int counts[3] = { n0, n1, n2 };
+
+ TestCounts(flags, counts, 3, numElements, pixels, floatPixels, preWeightedPixels, alphaTest, floatSortedInputs, pwFloatSortedInputs, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
+ }
+ }
+
+ TestSingleColor(flags, pixels, floatPixels, 3, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &rtn);
+ }
+ }
+ else
+ {
+ int minRange = alphaTest ? 3 : 4;
+
+ for (int range = minRange; range <= 4; range++)
+ {
+ int tweakRounds = BCCommon::TweakRoundsForRange(range);
+ if (tweakRounds > maxTweakRounds)
+ tweakRounds = maxTweakRounds;
+
+ for (int tweak = 0; tweak < tweakRounds; tweak++)
+ {
+ MUInt15 endPoints[2][3];
+
+ ufep.FinishLDR(tweak, range, endPoints[0], endPoints[1]);
+
+ for (int refine = 0; refine < numRefineRounds; refine++)
+ {
+ EndpointRefiner<3> refiner;
+ refiner.Init(range, channelWeights);
+
+ TestEndpoints(flags, pixels, floatPixels, preWeightedPixels, endPoints, range, channelWeights, bestError, bestEndpoints, bestIndexes, bestRange, &refiner, &rtn);
+
+ if (refine != numRefineRounds - 1)
+ refiner.GetRefinedEndpointsLDR(endPoints, &rtn);
+ }
+ }
+ }
+ }
+
+ for (int block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ ParallelMath::ScalarUInt16 range = ParallelMath::Extract(bestRange, block);
+ assert(range == 3 || range == 4);
+
+ ParallelMath::ScalarUInt16 compressedEP[2];
+ for (int ep = 0; ep < 2; ep++)
+ {
+ ParallelMath::ScalarUInt16 endPoint[3];
+ for (int ch = 0; ch < 3; ch++)
+ endPoint[ch] = ParallelMath::Extract(bestEndpoints[ep][ch], block);
+
+ int compressed = (endPoint[0] & 0xf8) << 8;
+ compressed |= (endPoint[1] & 0xfc) << 3;
+ compressed |= (endPoint[2] & 0xf8) >> 3;
+
+ compressedEP[ep] = static_cast<ParallelMath::ScalarUInt16>(compressed);
+ }
+
+ int indexOrder[4];
+
+ if (range == 4)
+ {
+ if (compressedEP[0] == compressedEP[1])
+ {
+ indexOrder[0] = 0;
+ indexOrder[1] = 0;
+ indexOrder[2] = 0;
+ indexOrder[3] = 0;
+ }
+ else if (compressedEP[0] < compressedEP[1])
+ {
+ std::swap(compressedEP[0], compressedEP[1]);
+ indexOrder[0] = 1;
+ indexOrder[1] = 3;
+ indexOrder[2] = 2;
+ indexOrder[3] = 0;
+ }
+ else
+ {
+ indexOrder[0] = 0;
+ indexOrder[1] = 2;
+ indexOrder[2] = 3;
+ indexOrder[3] = 1;
+ }
+ }
+ else
+ {
+ assert(range == 3);
+
+ if (compressedEP[0] > compressedEP[1])
+ {
+ std::swap(compressedEP[0], compressedEP[1]);
+ indexOrder[0] = 1;
+ indexOrder[1] = 2;
+ indexOrder[2] = 0;
+ }
+ else
+ {
+ indexOrder[0] = 0;
+ indexOrder[1] = 2;
+ indexOrder[2] = 1;
+ }
+ indexOrder[3] = 3;
+ }
+
+ packedBlocks[0] = static_cast<uint8_t>(compressedEP[0] & 0xff);
+ packedBlocks[1] = static_cast<uint8_t>((compressedEP[0] >> 8) & 0xff);
+ packedBlocks[2] = static_cast<uint8_t>(compressedEP[1] & 0xff);
+ packedBlocks[3] = static_cast<uint8_t>((compressedEP[1] >> 8) & 0xff);
+
+ for (int i = 0; i < 16; i += 4)
+ {
+ int packedIndexes = 0;
+ for (int subi = 0; subi < 4; subi++)
+ {
+ ParallelMath::ScalarUInt16 index = ParallelMath::Extract(bestIndexes[i + subi], block);
+ packedIndexes |= (indexOrder[index] << (subi * 2));
+ }
+
+ packedBlocks[4 + i / 4] = static_cast<uint8_t>(packedIndexes);
+ }
+
+ packedBlocks += packedBlockStride;
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_S3TC.h b/thirdparty/cvtt/ConvectionKernels_S3TC.h
new file mode 100644
index 0000000000..aa197229c2
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_S3TC.h
@@ -0,0 +1,51 @@
+#pragma once
+#ifndef __CVTT_S3TC_H__
+#define __CVTT_S3TC_H__
+
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ template<int TVectorSize>
+ class EndpointRefiner;
+ }
+
+ struct PixelBlockU8;
+}
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ class S3TCComputer
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+
+ static void Init(MFloat& error);
+ static void QuantizeTo6Bits(MUInt15& v);
+ static void QuantizeTo5Bits(MUInt15& v);
+ static void QuantizeTo565(MUInt15 endPoint[3]);
+ static MFloat ParanoidFactorForSpan(const MSInt16& span);
+ static MFloat ParanoidDiff(const MUInt15& a, const MUInt15& b, const MFloat& d);
+ static void TestSingleColor(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], int range, const float* channelWeights,
+ MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void TestEndpoints(uint32_t flags, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], const MUInt15 unquantizedEndPoints[2][3], int range, const float* channelWeights,
+ MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange, EndpointRefiner<3> *refiner, const ParallelMath::RoundTowardNearestForScope *rtn);
+ static void TestCounts(uint32_t flags, const int *counts, int nCounts, const MUInt15 &numElements, const MUInt15 pixels[16][4], const MFloat floatPixels[16][4], const MFloat preWeightedPixels[16][4], bool alphaTest,
+ const MFloat floatSortedInputs[16][4], const MFloat preWeightedFloatSortedInputs[16][4], const float *channelWeights, MFloat &bestError, MUInt15 bestEndpoints[2][3], MUInt15 bestIndexes[16], MUInt15 &bestRange,
+ const ParallelMath::RoundTowardNearestForScope* rtn);
+ static void PackExplicitAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride);
+ static void PackInterpolatedAlpha(uint32_t flags, const PixelBlockU8* inputs, int inputChannel, uint8_t* packedBlocks, size_t packedBlockStride, bool isSigned, int maxTweakRounds, int numRefineRounds);
+ static void PackRGB(uint32_t flags, const PixelBlockU8* inputs, uint8_t* packedBlocks, size_t packedBlockStride, const float channelWeights[4], bool alphaTest, float alphaThreshold, bool exhaustive, int maxTweakRounds, int numRefineRounds);
+ };
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_S3TC_SingleColor.h b/thirdparty/cvtt/ConvectionKernels_S3TC_SingleColor.h
new file mode 100644
index 0000000000..c772b163c2
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_S3TC_SingleColor.h
@@ -0,0 +1,304 @@
+#pragma once
+#include <stdint.h>
+
+// This file is generated by the MakeTables app. Do not edit this file manually.
+
+namespace cvtt { namespace Tables { namespace S3TCSC {
+
+struct TableEntry
+{
+ uint8_t m_min;
+ uint8_t m_max;
+ uint8_t m_actualColor;
+ uint8_t m_span;
+};
+
+TableEntry g_singleColor5_3[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 2, 8 }, { 0, 8, 2, 8 }, { 8, 0, 5, 8 }, { 8, 0, 5, 8 }, { 8, 0, 5, 8 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 10, 8 }, { 0, 33, 11, 33 }, { 16, 8, 13, 8 }, { 16, 8, 13, 8 }, { 16, 8, 13, 8 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 18, 8 }, { 8, 41, 19, 33 }, { 24, 16, 21, 8 }, { 24, 16, 21, 8 }, { 33, 0, 22, 33 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 27, 9 }, { 24, 33, 27, 9 }, { 24, 33, 27, 9 }, { 24, 41, 29, 17 }, { 33, 24, 30, 9 }, { 33, 24, 30, 9 },
+ { 24, 49, 32, 25 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 33, 41, 35, 8 }, { 33, 41, 35, 8 }, { 41, 33, 38, 8 }, { 41, 33, 38, 8 }, { 41, 33, 38, 8 },
+ { 49, 24, 40, 25 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 43, 8 }, { 33, 66, 44, 33 }, { 49, 41, 46, 8 }, { 49, 41, 46, 8 }, { 49, 41, 46, 8 },
+ { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 51, 8 }, { 41, 74, 52, 33 }, { 57, 49, 54, 8 }, { 57, 49, 54, 8 }, { 66, 33, 55, 33 },
+ { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 60, 9 }, { 57, 66, 60, 9 }, { 57, 66, 60, 9 }, { 57, 74, 62, 17 }, { 66, 57, 63, 9 },
+ { 66, 57, 63, 9 }, { 57, 82, 65, 25 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 66, 74, 68, 8 }, { 66, 74, 68, 8 }, { 74, 66, 71, 8 }, { 74, 66, 71, 8 },
+ { 74, 66, 71, 8 }, { 82, 57, 73, 25 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 76, 8 }, { 66, 99, 77, 33 }, { 82, 74, 79, 8 }, { 82, 74, 79, 8 },
+ { 82, 74, 79, 8 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 84, 8 }, { 74, 107, 85, 33 }, { 90, 82, 87, 8 }, { 90, 82, 87, 8 },
+ { 99, 66, 88, 33 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 93, 9 }, { 90, 99, 93, 9 }, { 90, 99, 93, 9 }, { 90, 107, 95, 17 },
+ { 99, 90, 96, 9 }, { 99, 90, 96, 9 }, { 90, 115, 98, 25 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 99, 107, 101, 8 }, { 99, 107, 101, 8 }, { 107, 99, 104, 8 },
+ { 107, 99, 104, 8 }, { 107, 99, 104, 8 }, { 115, 90, 106, 25 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 109, 8 }, { 99, 132, 110, 33 }, { 115, 107, 112, 8 },
+ { 115, 107, 112, 8 }, { 115, 107, 112, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 117, 8 }, { 107, 140, 118, 33 }, { 123, 115, 120, 8 },
+ { 123, 115, 120, 8 }, { 132, 99, 121, 33 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 126, 9 }, { 123, 132, 126, 9 }, { 123, 132, 126, 9 },
+ { 123, 140, 128, 17 }, { 132, 123, 129, 9 }, { 132, 123, 129, 9 }, { 123, 148, 131, 25 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 132, 140, 134, 8 }, { 132, 140, 134, 8 },
+ { 140, 132, 137, 8 }, { 140, 132, 137, 8 }, { 140, 132, 137, 8 }, { 148, 123, 139, 25 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 142, 8 }, { 132, 165, 143, 33 },
+ { 148, 140, 145, 8 }, { 148, 140, 145, 8 }, { 148, 140, 145, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 150, 8 }, { 140, 173, 151, 33 },
+ { 156, 148, 153, 8 }, { 156, 148, 153, 8 }, { 165, 132, 154, 33 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 159, 9 }, { 156, 165, 159, 9 },
+ { 156, 165, 159, 9 }, { 156, 173, 161, 17 }, { 165, 156, 162, 9 }, { 165, 156, 162, 9 }, { 156, 181, 164, 25 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 165, 173, 167, 8 },
+ { 165, 173, 167, 8 }, { 173, 165, 170, 8 }, { 173, 165, 170, 8 }, { 173, 165, 170, 8 }, { 181, 156, 172, 25 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 181, 175, 8 },
+ { 165, 198, 176, 33 }, { 181, 173, 178, 8 }, { 181, 173, 178, 8 }, { 181, 173, 178, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 189, 183, 8 },
+ { 173, 206, 184, 33 }, { 189, 181, 186, 8 }, { 189, 181, 186, 8 }, { 198, 165, 187, 33 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 198, 192, 9 },
+ { 189, 198, 192, 9 }, { 189, 198, 192, 9 }, { 189, 206, 194, 17 }, { 198, 189, 195, 9 }, { 198, 189, 195, 9 }, { 189, 214, 197, 25 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
+ { 198, 206, 200, 8 }, { 198, 206, 200, 8 }, { 206, 198, 203, 8 }, { 206, 198, 203, 8 }, { 206, 198, 203, 8 }, { 214, 189, 205, 25 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
+ { 206, 214, 208, 8 }, { 198, 231, 209, 33 }, { 214, 206, 211, 8 }, { 214, 206, 211, 8 }, { 214, 206, 211, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
+ { 214, 222, 216, 8 }, { 206, 239, 217, 33 }, { 222, 214, 219, 8 }, { 222, 214, 219, 8 }, { 231, 198, 220, 33 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
+ { 222, 231, 225, 9 }, { 222, 231, 225, 9 }, { 222, 231, 225, 9 }, { 222, 239, 227, 17 }, { 231, 222, 228, 9 }, { 231, 222, 228, 9 }, { 222, 247, 230, 25 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 231, 239, 233, 8 }, { 231, 239, 233, 8 }, { 239, 231, 236, 8 }, { 239, 231, 236, 8 }, { 239, 231, 236, 8 }, { 247, 222, 238, 25 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 247, 241, 8 }, { 239, 247, 241, 8 }, { 247, 239, 244, 8 }, { 247, 239, 244, 8 }, { 247, 239, 244, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 255, 249, 8 }, { 247, 255, 249, 8 }, { 255, 247, 252, 8 }, { 255, 247, 252, 8 }, { 255, 247, 252, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor6_3[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 4, 1, 4 }, { 4, 0, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 5, 4 }, { 8, 4, 6, 4 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 12, 9, 4 }, { 12, 8, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 13, 4 }, { 16, 12, 14, 4 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 20, 17, 4 }, { 20, 16, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 21, 4 }, { 24, 20, 22, 4 }, { 0, 69, 23, 69 },
+ { 24, 24, 24, 0 }, { 24, 28, 25, 4 }, { 28, 24, 26, 4 }, { 8, 65, 27, 57 }, { 28, 28, 28, 0 }, { 28, 32, 29, 4 }, { 32, 28, 30, 4 }, { 12, 69, 31, 57 },
+ { 32, 32, 32, 0 }, { 32, 36, 33, 4 }, { 36, 32, 34, 4 }, { 20, 65, 35, 45 }, { 36, 36, 36, 0 }, { 36, 40, 37, 4 }, { 40, 36, 38, 4 }, { 24, 69, 39, 45 },
+ { 40, 40, 40, 0 }, { 40, 44, 41, 4 }, { 44, 40, 42, 4 }, { 32, 65, 43, 33 }, { 44, 44, 44, 0 }, { 44, 48, 45, 4 }, { 48, 44, 46, 4 }, { 36, 69, 47, 33 },
+ { 48, 48, 48, 0 }, { 48, 52, 49, 4 }, { 52, 48, 50, 4 }, { 44, 65, 51, 21 }, { 52, 52, 52, 0 }, { 52, 56, 53, 4 }, { 56, 52, 54, 4 }, { 48, 69, 55, 21 },
+ { 56, 56, 56, 0 }, { 56, 60, 57, 4 }, { 60, 56, 58, 4 }, { 56, 65, 59, 9 }, { 60, 60, 60, 0 }, { 60, 65, 61, 5 }, { 65, 56, 62, 9 }, { 65, 60, 63, 5 },
+ { 60, 73, 64, 13 }, { 65, 65, 65, 0 }, { 65, 69, 66, 4 }, { 69, 65, 67, 4 }, { 73, 60, 68, 13 }, { 69, 69, 69, 0 }, { 69, 73, 70, 4 }, { 73, 69, 71, 4 },
+ { 81, 56, 72, 25 }, { 73, 73, 73, 0 }, { 73, 77, 74, 4 }, { 77, 73, 75, 4 }, { 85, 60, 76, 25 }, { 77, 77, 77, 0 }, { 77, 81, 78, 4 }, { 81, 77, 79, 4 },
+ { 93, 56, 80, 37 }, { 81, 81, 81, 0 }, { 81, 85, 82, 4 }, { 85, 81, 83, 4 }, { 97, 60, 84, 37 }, { 85, 85, 85, 0 }, { 85, 89, 86, 4 }, { 89, 85, 87, 4 },
+ { 105, 56, 88, 49 }, { 89, 89, 89, 0 }, { 89, 93, 90, 4 }, { 93, 89, 91, 4 }, { 109, 60, 92, 49 }, { 93, 93, 93, 0 }, { 93, 97, 94, 4 }, { 97, 93, 95, 4 },
+ { 77, 134, 96, 57 }, { 97, 97, 97, 0 }, { 97, 101, 98, 4 }, { 101, 97, 99, 4 }, { 85, 130, 100, 45 }, { 101, 101, 101, 0 }, { 101, 105, 102, 4 }, { 105, 101, 103, 4 },
+ { 89, 134, 104, 45 }, { 105, 105, 105, 0 }, { 105, 109, 106, 4 }, { 109, 105, 107, 4 }, { 97, 130, 108, 33 }, { 109, 109, 109, 0 }, { 109, 113, 110, 4 }, { 113, 109, 111, 4 },
+ { 101, 134, 112, 33 }, { 113, 113, 113, 0 }, { 113, 117, 114, 4 }, { 117, 113, 115, 4 }, { 109, 130, 116, 21 }, { 117, 117, 117, 0 }, { 117, 121, 118, 4 }, { 121, 117, 119, 4 },
+ { 113, 134, 120, 21 }, { 121, 121, 121, 0 }, { 121, 125, 122, 4 }, { 125, 121, 123, 4 }, { 121, 130, 124, 9 }, { 125, 125, 125, 0 }, { 125, 130, 126, 5 }, { 130, 121, 127, 9 },
+ { 130, 125, 128, 5 }, { 125, 138, 129, 13 }, { 130, 130, 130, 0 }, { 130, 134, 131, 4 }, { 134, 130, 132, 4 }, { 138, 125, 133, 13 }, { 134, 134, 134, 0 }, { 134, 138, 135, 4 },
+ { 138, 134, 136, 4 }, { 146, 121, 137, 25 }, { 138, 138, 138, 0 }, { 138, 142, 139, 4 }, { 142, 138, 140, 4 }, { 150, 125, 141, 25 }, { 142, 142, 142, 0 }, { 142, 146, 143, 4 },
+ { 146, 142, 144, 4 }, { 158, 121, 145, 37 }, { 146, 146, 146, 0 }, { 146, 150, 147, 4 }, { 150, 146, 148, 4 }, { 162, 125, 149, 37 }, { 150, 150, 150, 0 }, { 150, 154, 151, 4 },
+ { 154, 150, 152, 4 }, { 170, 121, 153, 49 }, { 154, 154, 154, 0 }, { 154, 158, 155, 4 }, { 158, 154, 156, 4 }, { 174, 125, 157, 49 }, { 158, 158, 158, 0 }, { 158, 162, 159, 4 },
+ { 162, 158, 160, 4 }, { 142, 199, 161, 57 }, { 162, 162, 162, 0 }, { 162, 166, 163, 4 }, { 166, 162, 164, 4 }, { 150, 195, 165, 45 }, { 166, 166, 166, 0 }, { 166, 170, 167, 4 },
+ { 170, 166, 168, 4 }, { 154, 199, 169, 45 }, { 170, 170, 170, 0 }, { 170, 174, 171, 4 }, { 174, 170, 172, 4 }, { 162, 195, 173, 33 }, { 174, 174, 174, 0 }, { 174, 178, 175, 4 },
+ { 178, 174, 176, 4 }, { 166, 199, 177, 33 }, { 178, 178, 178, 0 }, { 178, 182, 179, 4 }, { 182, 178, 180, 4 }, { 174, 195, 181, 21 }, { 182, 182, 182, 0 }, { 182, 186, 183, 4 },
+ { 186, 182, 184, 4 }, { 178, 199, 185, 21 }, { 186, 186, 186, 0 }, { 186, 190, 187, 4 }, { 190, 186, 188, 4 }, { 186, 195, 189, 9 }, { 190, 190, 190, 0 }, { 190, 195, 191, 5 },
+ { 195, 186, 192, 9 }, { 195, 190, 193, 5 }, { 190, 203, 194, 13 }, { 195, 195, 195, 0 }, { 195, 199, 196, 4 }, { 199, 195, 197, 4 }, { 203, 190, 198, 13 }, { 199, 199, 199, 0 },
+ { 199, 203, 200, 4 }, { 203, 199, 201, 4 }, { 211, 186, 202, 25 }, { 203, 203, 203, 0 }, { 203, 207, 204, 4 }, { 207, 203, 205, 4 }, { 215, 190, 206, 25 }, { 207, 207, 207, 0 },
+ { 207, 211, 208, 4 }, { 211, 207, 209, 4 }, { 223, 186, 210, 37 }, { 211, 211, 211, 0 }, { 211, 215, 212, 4 }, { 215, 211, 213, 4 }, { 227, 190, 214, 37 }, { 215, 215, 215, 0 },
+ { 215, 219, 216, 4 }, { 219, 215, 217, 4 }, { 235, 186, 218, 49 }, { 219, 219, 219, 0 }, { 219, 223, 220, 4 }, { 223, 219, 221, 4 }, { 239, 190, 222, 49 }, { 223, 223, 223, 0 },
+ { 223, 227, 224, 4 }, { 227, 223, 225, 4 }, { 247, 186, 226, 61 }, { 227, 227, 227, 0 }, { 227, 231, 228, 4 }, { 231, 227, 229, 4 }, { 251, 190, 230, 61 }, { 231, 231, 231, 0 },
+ { 231, 235, 232, 4 }, { 235, 231, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 236, 4 }, { 239, 235, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
+ { 239, 243, 240, 4 }, { 243, 239, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 244, 4 }, { 247, 243, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 251, 248, 4 }, { 251, 247, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 252, 4 }, { 255, 251, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor5_2[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 41, 32, 17 },
+ { 24, 41, 32, 17 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 24, 49, 36, 25 }, { 24, 49, 36, 25 }, { 33, 41, 37, 8 }, { 33, 41, 37, 8 }, { 24, 57, 40, 33 },
+ { 24, 57, 40, 33 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 49, 49, 49, 0 },
+ { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 57, 57, 57, 0 },
+ { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 },
+ { 57, 74, 65, 17 }, { 57, 74, 65, 17 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 57, 82, 69, 25 }, { 57, 82, 69, 25 }, { 66, 74, 70, 8 }, { 66, 74, 70, 8 },
+ { 57, 90, 73, 33 }, { 57, 90, 73, 33 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 },
+ { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 },
+ { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 },
+ { 90, 99, 94, 9 }, { 90, 107, 98, 17 }, { 90, 107, 98, 17 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 90, 115, 102, 25 }, { 90, 115, 102, 25 }, { 99, 107, 103, 8 },
+ { 99, 107, 103, 8 }, { 90, 123, 106, 33 }, { 90, 123, 106, 33 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 111, 8 }, { 107, 115, 111, 8 },
+ { 107, 115, 111, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 119, 8 }, { 115, 123, 119, 8 },
+ { 115, 123, 119, 8 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 127, 9 }, { 123, 132, 127, 9 },
+ { 123, 132, 127, 9 }, { 123, 132, 127, 9 }, { 123, 140, 131, 17 }, { 123, 140, 131, 17 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 123, 148, 135, 25 }, { 123, 148, 135, 25 },
+ { 132, 140, 136, 8 }, { 132, 140, 136, 8 }, { 123, 156, 139, 33 }, { 123, 156, 139, 33 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 144, 8 },
+ { 140, 148, 144, 8 }, { 140, 148, 144, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 152, 8 },
+ { 148, 156, 152, 8 }, { 148, 156, 152, 8 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 160, 9 },
+ { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 173, 164, 17 }, { 156, 173, 164, 17 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 156, 181, 168, 25 },
+ { 156, 181, 168, 25 }, { 165, 173, 169, 8 }, { 165, 173, 169, 8 }, { 156, 189, 172, 33 }, { 156, 189, 172, 33 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 },
+ { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 },
+ { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 },
+ { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 206, 197, 17 }, { 189, 206, 197, 17 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
+ { 189, 214, 201, 25 }, { 189, 214, 201, 25 }, { 198, 206, 202, 8 }, { 198, 206, 202, 8 }, { 189, 222, 205, 33 }, { 189, 222, 205, 33 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
+ { 206, 206, 206, 0 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
+ { 214, 214, 214, 0 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
+ { 222, 222, 222, 0 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 239, 230, 17 }, { 222, 239, 230, 17 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 222, 247, 234, 25 }, { 222, 247, 234, 25 }, { 231, 239, 235, 8 }, { 231, 239, 235, 8 }, { 222, 255, 238, 33 }, { 222, 255, 238, 33 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 239, 239, 0 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor6_2[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 22, 4 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 28, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 32, 30, 4 }, { 32, 32, 32, 0 },
+ { 32, 32, 32, 0 }, { 32, 32, 32, 0 }, { 32, 36, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 40, 38, 4 }, { 40, 40, 40, 0 },
+ { 40, 40, 40, 0 }, { 40, 40, 40, 0 }, { 40, 44, 42, 4 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 48, 46, 4 }, { 48, 48, 48, 0 },
+ { 48, 48, 48, 0 }, { 48, 48, 48, 0 }, { 48, 52, 50, 4 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 56, 54, 4 }, { 56, 56, 56, 0 },
+ { 56, 56, 56, 0 }, { 56, 56, 56, 0 }, { 56, 60, 58, 4 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 65, 62, 5 }, { 60, 65, 62, 5 },
+ { 60, 69, 64, 9 }, { 65, 65, 65, 0 }, { 60, 73, 66, 13 }, { 65, 69, 67, 4 }, { 60, 77, 68, 17 }, { 69, 69, 69, 0 }, { 60, 81, 70, 21 }, { 69, 73, 71, 4 },
+ { 60, 85, 72, 25 }, { 73, 73, 73, 0 }, { 60, 89, 74, 29 }, { 73, 77, 75, 4 }, { 60, 93, 76, 33 }, { 77, 77, 77, 0 }, { 60, 97, 78, 37 }, { 77, 81, 79, 4 },
+ { 60, 101, 80, 41 }, { 81, 81, 81, 0 }, { 60, 105, 82, 45 }, { 81, 85, 83, 4 }, { 60, 109, 84, 49 }, { 85, 85, 85, 0 }, { 60, 113, 86, 53 }, { 85, 89, 87, 4 },
+ { 60, 117, 88, 57 }, { 89, 89, 89, 0 }, { 60, 121, 90, 61 }, { 89, 93, 91, 4 }, { 60, 125, 92, 65 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 97, 95, 4 },
+ { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 101, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 105, 103, 4 },
+ { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 109, 107, 4 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 113, 111, 4 },
+ { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 117, 115, 4 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 121, 119, 4 },
+ { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 125, 123, 4 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 130, 127, 5 },
+ { 125, 130, 127, 5 }, { 125, 134, 129, 9 }, { 130, 130, 130, 0 }, { 125, 138, 131, 13 }, { 130, 134, 132, 4 }, { 125, 142, 133, 17 }, { 134, 134, 134, 0 }, { 125, 146, 135, 21 },
+ { 134, 138, 136, 4 }, { 125, 150, 137, 25 }, { 138, 138, 138, 0 }, { 125, 154, 139, 29 }, { 138, 142, 140, 4 }, { 125, 158, 141, 33 }, { 142, 142, 142, 0 }, { 125, 162, 143, 37 },
+ { 142, 146, 144, 4 }, { 125, 166, 145, 41 }, { 146, 146, 146, 0 }, { 125, 170, 147, 45 }, { 146, 150, 148, 4 }, { 125, 174, 149, 49 }, { 150, 150, 150, 0 }, { 125, 178, 151, 53 },
+ { 150, 154, 152, 4 }, { 125, 182, 153, 57 }, { 154, 154, 154, 0 }, { 125, 186, 155, 61 }, { 154, 158, 156, 4 }, { 125, 190, 157, 65 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 },
+ { 158, 162, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 166, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 },
+ { 166, 170, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 174, 172, 4 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 },
+ { 174, 178, 176, 4 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 182, 180, 4 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 },
+ { 182, 186, 184, 4 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 190, 188, 4 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 },
+ { 190, 195, 192, 5 }, { 190, 195, 192, 5 }, { 190, 199, 194, 9 }, { 195, 195, 195, 0 }, { 190, 203, 196, 13 }, { 195, 199, 197, 4 }, { 190, 207, 198, 17 }, { 199, 199, 199, 0 },
+ { 190, 211, 200, 21 }, { 199, 203, 201, 4 }, { 190, 215, 202, 25 }, { 203, 203, 203, 0 }, { 190, 219, 204, 29 }, { 203, 207, 205, 4 }, { 190, 223, 206, 33 }, { 207, 207, 207, 0 },
+ { 190, 227, 208, 37 }, { 207, 211, 209, 4 }, { 190, 231, 210, 41 }, { 211, 211, 211, 0 }, { 190, 235, 212, 45 }, { 211, 215, 213, 4 }, { 190, 239, 214, 49 }, { 215, 215, 215, 0 },
+ { 190, 243, 216, 53 }, { 215, 219, 217, 4 }, { 190, 247, 218, 57 }, { 219, 219, 219, 0 }, { 190, 251, 220, 61 }, { 219, 223, 221, 4 }, { 190, 255, 222, 65 }, { 223, 223, 223, 0 },
+ { 223, 223, 223, 0 }, { 223, 227, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 231, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor5_3_p[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 2, 8 }, { 0, 8, 2, 8 }, { 8, 0, 5, 8 }, { 8, 0, 5, 8 }, { 8, 0, 5, 8 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 10, 8 }, { 0, 33, 11, 33 }, { 16, 8, 13, 8 }, { 16, 8, 13, 8 }, { 16, 8, 13, 8 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 18, 8 }, { 8, 41, 19, 33 }, { 24, 16, 21, 8 }, { 24, 16, 21, 8 }, { 33, 0, 22, 33 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 27, 9 }, { 24, 33, 27, 9 }, { 24, 33, 27, 9 }, { 24, 41, 29, 17 }, { 33, 24, 30, 9 }, { 33, 24, 30, 9 },
+ { 24, 49, 32, 25 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 33, 41, 35, 8 }, { 33, 41, 35, 8 }, { 41, 33, 38, 8 }, { 41, 33, 38, 8 }, { 41, 33, 38, 8 },
+ { 49, 24, 40, 25 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 43, 8 }, { 33, 66, 44, 33 }, { 49, 41, 46, 8 }, { 49, 41, 46, 8 }, { 49, 41, 46, 8 },
+ { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 51, 8 }, { 41, 74, 52, 33 }, { 57, 49, 54, 8 }, { 57, 49, 54, 8 }, { 66, 33, 55, 33 },
+ { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 60, 9 }, { 57, 66, 60, 9 }, { 57, 66, 60, 9 }, { 57, 74, 62, 17 }, { 66, 57, 63, 9 },
+ { 66, 57, 63, 9 }, { 57, 82, 65, 25 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 66, 74, 68, 8 }, { 66, 74, 68, 8 }, { 74, 66, 71, 8 }, { 74, 66, 71, 8 },
+ { 74, 66, 71, 8 }, { 82, 57, 73, 25 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 76, 8 }, { 66, 99, 77, 33 }, { 82, 74, 79, 8 }, { 82, 74, 79, 8 },
+ { 82, 74, 79, 8 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 84, 8 }, { 74, 107, 85, 33 }, { 90, 82, 87, 8 }, { 90, 82, 87, 8 },
+ { 99, 66, 88, 33 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 93, 9 }, { 90, 99, 93, 9 }, { 90, 99, 93, 9 }, { 90, 107, 95, 17 },
+ { 99, 90, 96, 9 }, { 99, 90, 96, 9 }, { 90, 115, 98, 25 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 99, 107, 101, 8 }, { 99, 107, 101, 8 }, { 107, 99, 104, 8 },
+ { 107, 99, 104, 8 }, { 107, 99, 104, 8 }, { 115, 90, 106, 25 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 109, 8 }, { 99, 132, 110, 33 }, { 115, 107, 112, 8 },
+ { 115, 107, 112, 8 }, { 115, 107, 112, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 117, 8 }, { 107, 140, 118, 33 }, { 123, 115, 120, 8 },
+ { 123, 115, 120, 8 }, { 132, 99, 121, 33 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 126, 9 }, { 123, 132, 126, 9 }, { 123, 132, 126, 9 },
+ { 123, 140, 128, 17 }, { 132, 123, 129, 9 }, { 132, 123, 129, 9 }, { 123, 148, 131, 25 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 132, 140, 134, 8 }, { 132, 140, 134, 8 },
+ { 140, 132, 137, 8 }, { 140, 132, 137, 8 }, { 140, 132, 137, 8 }, { 148, 123, 139, 25 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 142, 8 }, { 132, 165, 143, 33 },
+ { 148, 140, 145, 8 }, { 148, 140, 145, 8 }, { 148, 140, 145, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 150, 8 }, { 140, 173, 151, 33 },
+ { 156, 148, 153, 8 }, { 156, 148, 153, 8 }, { 165, 132, 154, 33 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 159, 9 }, { 156, 165, 159, 9 },
+ { 156, 165, 159, 9 }, { 156, 173, 161, 17 }, { 165, 156, 162, 9 }, { 165, 156, 162, 9 }, { 156, 181, 164, 25 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 165, 173, 167, 8 },
+ { 165, 173, 167, 8 }, { 173, 165, 170, 8 }, { 173, 165, 170, 8 }, { 173, 165, 170, 8 }, { 181, 156, 172, 25 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 181, 175, 8 },
+ { 165, 198, 176, 33 }, { 181, 173, 178, 8 }, { 181, 173, 178, 8 }, { 181, 173, 178, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 189, 183, 8 },
+ { 173, 206, 184, 33 }, { 189, 181, 186, 8 }, { 189, 181, 186, 8 }, { 198, 165, 187, 33 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 198, 192, 9 },
+ { 189, 198, 192, 9 }, { 189, 198, 192, 9 }, { 189, 206, 194, 17 }, { 198, 189, 195, 9 }, { 198, 189, 195, 9 }, { 189, 214, 197, 25 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
+ { 198, 206, 200, 8 }, { 198, 206, 200, 8 }, { 206, 198, 203, 8 }, { 206, 198, 203, 8 }, { 206, 198, 203, 8 }, { 214, 189, 205, 25 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
+ { 206, 214, 208, 8 }, { 198, 231, 209, 33 }, { 214, 206, 211, 8 }, { 214, 206, 211, 8 }, { 214, 206, 211, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
+ { 214, 222, 216, 8 }, { 206, 239, 217, 33 }, { 222, 214, 219, 8 }, { 222, 214, 219, 8 }, { 231, 198, 220, 33 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
+ { 222, 231, 225, 9 }, { 222, 231, 225, 9 }, { 222, 231, 225, 9 }, { 222, 239, 227, 17 }, { 231, 222, 228, 9 }, { 231, 222, 228, 9 }, { 222, 247, 230, 25 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 231, 239, 233, 8 }, { 231, 239, 233, 8 }, { 239, 231, 236, 8 }, { 239, 231, 236, 8 }, { 239, 231, 236, 8 }, { 247, 222, 238, 25 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 247, 241, 8 }, { 239, 247, 241, 8 }, { 247, 239, 244, 8 }, { 247, 239, 244, 8 }, { 247, 239, 244, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 255, 249, 8 }, { 247, 255, 249, 8 }, { 255, 247, 252, 8 }, { 255, 247, 252, 8 }, { 255, 247, 252, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor6_3_p[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 4, 1, 4 }, { 4, 0, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 5, 4 }, { 8, 4, 6, 4 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 12, 9, 4 }, { 12, 8, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 13, 4 }, { 16, 12, 14, 4 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 20, 17, 4 }, { 20, 16, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 21, 4 }, { 24, 20, 22, 4 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 28, 25, 4 }, { 28, 24, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 32, 29, 4 }, { 32, 28, 30, 4 }, { 32, 32, 32, 0 },
+ { 32, 32, 32, 0 }, { 32, 36, 33, 4 }, { 36, 32, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 40, 37, 4 }, { 40, 36, 38, 4 }, { 40, 40, 40, 0 },
+ { 40, 40, 40, 0 }, { 40, 44, 41, 4 }, { 44, 40, 42, 4 }, { 32, 65, 43, 33 }, { 44, 44, 44, 0 }, { 44, 48, 45, 4 }, { 48, 44, 46, 4 }, { 36, 69, 47, 33 },
+ { 48, 48, 48, 0 }, { 48, 52, 49, 4 }, { 52, 48, 50, 4 }, { 44, 65, 51, 21 }, { 52, 52, 52, 0 }, { 52, 56, 53, 4 }, { 56, 52, 54, 4 }, { 48, 69, 55, 21 },
+ { 56, 56, 56, 0 }, { 56, 60, 57, 4 }, { 60, 56, 58, 4 }, { 56, 65, 59, 9 }, { 60, 60, 60, 0 }, { 60, 65, 61, 5 }, { 65, 56, 62, 9 }, { 65, 60, 63, 5 },
+ { 60, 73, 64, 13 }, { 65, 65, 65, 0 }, { 65, 69, 66, 4 }, { 69, 65, 67, 4 }, { 73, 60, 68, 13 }, { 69, 69, 69, 0 }, { 69, 73, 70, 4 }, { 73, 69, 71, 4 },
+ { 81, 56, 72, 25 }, { 73, 73, 73, 0 }, { 73, 77, 74, 4 }, { 77, 73, 75, 4 }, { 85, 60, 76, 25 }, { 77, 77, 77, 0 }, { 77, 81, 78, 4 }, { 81, 77, 79, 4 },
+ { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 81, 85, 82, 4 }, { 85, 81, 83, 4 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 85, 89, 86, 4 }, { 89, 85, 87, 4 },
+ { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 89, 93, 90, 4 }, { 93, 89, 91, 4 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 97, 94, 4 }, { 97, 93, 95, 4 },
+ { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 101, 98, 4 }, { 101, 97, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 105, 102, 4 }, { 105, 101, 103, 4 },
+ { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 109, 106, 4 }, { 109, 105, 107, 4 }, { 97, 130, 108, 33 }, { 109, 109, 109, 0 }, { 109, 113, 110, 4 }, { 113, 109, 111, 4 },
+ { 101, 134, 112, 33 }, { 113, 113, 113, 0 }, { 113, 117, 114, 4 }, { 117, 113, 115, 4 }, { 109, 130, 116, 21 }, { 117, 117, 117, 0 }, { 117, 121, 118, 4 }, { 121, 117, 119, 4 },
+ { 113, 134, 120, 21 }, { 121, 121, 121, 0 }, { 121, 125, 122, 4 }, { 125, 121, 123, 4 }, { 121, 130, 124, 9 }, { 125, 125, 125, 0 }, { 125, 130, 126, 5 }, { 130, 121, 127, 9 },
+ { 130, 125, 128, 5 }, { 125, 138, 129, 13 }, { 130, 130, 130, 0 }, { 130, 134, 131, 4 }, { 134, 130, 132, 4 }, { 138, 125, 133, 13 }, { 134, 134, 134, 0 }, { 134, 138, 135, 4 },
+ { 138, 134, 136, 4 }, { 146, 121, 137, 25 }, { 138, 138, 138, 0 }, { 138, 142, 139, 4 }, { 142, 138, 140, 4 }, { 150, 125, 141, 25 }, { 142, 142, 142, 0 }, { 142, 146, 143, 4 },
+ { 146, 142, 144, 4 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 146, 150, 147, 4 }, { 150, 146, 148, 4 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 }, { 150, 154, 151, 4 },
+ { 154, 150, 152, 4 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 154, 158, 155, 4 }, { 158, 154, 156, 4 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 }, { 158, 162, 159, 4 },
+ { 162, 158, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 166, 163, 4 }, { 166, 162, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 166, 170, 167, 4 },
+ { 170, 166, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 174, 171, 4 }, { 174, 170, 172, 4 }, { 162, 195, 173, 33 }, { 174, 174, 174, 0 }, { 174, 178, 175, 4 },
+ { 178, 174, 176, 4 }, { 166, 199, 177, 33 }, { 178, 178, 178, 0 }, { 178, 182, 179, 4 }, { 182, 178, 180, 4 }, { 174, 195, 181, 21 }, { 182, 182, 182, 0 }, { 182, 186, 183, 4 },
+ { 186, 182, 184, 4 }, { 178, 199, 185, 21 }, { 186, 186, 186, 0 }, { 186, 190, 187, 4 }, { 190, 186, 188, 4 }, { 186, 195, 189, 9 }, { 190, 190, 190, 0 }, { 190, 195, 191, 5 },
+ { 195, 186, 192, 9 }, { 195, 190, 193, 5 }, { 190, 203, 194, 13 }, { 195, 195, 195, 0 }, { 195, 199, 196, 4 }, { 199, 195, 197, 4 }, { 203, 190, 198, 13 }, { 199, 199, 199, 0 },
+ { 199, 203, 200, 4 }, { 203, 199, 201, 4 }, { 211, 186, 202, 25 }, { 203, 203, 203, 0 }, { 203, 207, 204, 4 }, { 207, 203, 205, 4 }, { 215, 190, 206, 25 }, { 207, 207, 207, 0 },
+ { 207, 211, 208, 4 }, { 211, 207, 209, 4 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 211, 215, 212, 4 }, { 215, 211, 213, 4 }, { 215, 215, 215, 0 }, { 215, 215, 215, 0 },
+ { 215, 219, 216, 4 }, { 219, 215, 217, 4 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 219, 223, 220, 4 }, { 223, 219, 221, 4 }, { 223, 223, 223, 0 }, { 223, 223, 223, 0 },
+ { 223, 227, 224, 4 }, { 227, 223, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 231, 228, 4 }, { 231, 227, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
+ { 231, 235, 232, 4 }, { 235, 231, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 236, 4 }, { 239, 235, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
+ { 239, 243, 240, 4 }, { 243, 239, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 244, 4 }, { 247, 243, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 251, 248, 4 }, { 251, 247, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 252, 4 }, { 255, 251, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor5_2_p[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 0, 8, 4, 8 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 8, 16, 12, 8 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 16, 24, 20, 8 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 33, 28, 9 }, { 24, 41, 32, 17 },
+ { 24, 41, 32, 17 }, { 33, 33, 33, 0 }, { 33, 33, 33, 0 }, { 24, 49, 36, 25 }, { 24, 49, 36, 25 }, { 33, 41, 37, 8 }, { 33, 41, 37, 8 }, { 24, 57, 40, 33 },
+ { 24, 57, 40, 33 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 41, 41, 0 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 41, 49, 45, 8 }, { 49, 49, 49, 0 },
+ { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 49, 49, 0 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 49, 57, 53, 8 }, { 57, 57, 57, 0 },
+ { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 57, 57, 0 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 }, { 57, 66, 61, 9 },
+ { 57, 74, 65, 17 }, { 57, 74, 65, 17 }, { 66, 66, 66, 0 }, { 66, 66, 66, 0 }, { 57, 82, 69, 25 }, { 57, 82, 69, 25 }, { 66, 74, 70, 8 }, { 66, 74, 70, 8 },
+ { 57, 90, 73, 33 }, { 57, 90, 73, 33 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 74, 74, 0 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 }, { 74, 82, 78, 8 },
+ { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 82, 82, 0 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 }, { 82, 90, 86, 8 },
+ { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 90, 90, 0 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 }, { 90, 99, 94, 9 },
+ { 90, 99, 94, 9 }, { 90, 107, 98, 17 }, { 90, 107, 98, 17 }, { 99, 99, 99, 0 }, { 99, 99, 99, 0 }, { 90, 115, 102, 25 }, { 90, 115, 102, 25 }, { 99, 107, 103, 8 },
+ { 99, 107, 103, 8 }, { 90, 123, 106, 33 }, { 90, 123, 106, 33 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 107, 107, 0 }, { 107, 115, 111, 8 }, { 107, 115, 111, 8 },
+ { 107, 115, 111, 8 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 115, 115, 0 }, { 115, 123, 119, 8 }, { 115, 123, 119, 8 },
+ { 115, 123, 119, 8 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 123, 123, 0 }, { 123, 132, 127, 9 }, { 123, 132, 127, 9 },
+ { 123, 132, 127, 9 }, { 123, 132, 127, 9 }, { 123, 140, 131, 17 }, { 123, 140, 131, 17 }, { 132, 132, 132, 0 }, { 132, 132, 132, 0 }, { 123, 148, 135, 25 }, { 123, 148, 135, 25 },
+ { 132, 140, 136, 8 }, { 132, 140, 136, 8 }, { 123, 156, 139, 33 }, { 123, 156, 139, 33 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 140, 140, 0 }, { 140, 148, 144, 8 },
+ { 140, 148, 144, 8 }, { 140, 148, 144, 8 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 148, 148, 0 }, { 148, 156, 152, 8 },
+ { 148, 156, 152, 8 }, { 148, 156, 152, 8 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 156, 156, 0 }, { 156, 165, 160, 9 },
+ { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 165, 160, 9 }, { 156, 173, 164, 17 }, { 156, 173, 164, 17 }, { 165, 165, 165, 0 }, { 165, 165, 165, 0 }, { 156, 181, 168, 25 },
+ { 156, 181, 168, 25 }, { 165, 173, 169, 8 }, { 165, 173, 169, 8 }, { 156, 189, 172, 33 }, { 156, 189, 172, 33 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 }, { 173, 173, 173, 0 },
+ { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 173, 181, 177, 8 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 }, { 181, 181, 181, 0 },
+ { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 181, 189, 185, 8 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 }, { 189, 189, 189, 0 },
+ { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 198, 193, 9 }, { 189, 206, 197, 17 }, { 189, 206, 197, 17 }, { 198, 198, 198, 0 }, { 198, 198, 198, 0 },
+ { 189, 214, 201, 25 }, { 189, 214, 201, 25 }, { 198, 206, 202, 8 }, { 198, 206, 202, 8 }, { 189, 222, 205, 33 }, { 189, 222, 205, 33 }, { 206, 206, 206, 0 }, { 206, 206, 206, 0 },
+ { 206, 206, 206, 0 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 206, 214, 210, 8 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 }, { 214, 214, 214, 0 },
+ { 214, 214, 214, 0 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 214, 222, 218, 8 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 }, { 222, 222, 222, 0 },
+ { 222, 222, 222, 0 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 231, 226, 9 }, { 222, 239, 230, 17 }, { 222, 239, 230, 17 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 222, 247, 234, 25 }, { 222, 247, 234, 25 }, { 231, 239, 235, 8 }, { 231, 239, 235, 8 }, { 222, 255, 238, 33 }, { 222, 255, 238, 33 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 239, 239, 0 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 239, 247, 243, 8 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 247, 247, 0 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 247, 255, 251, 8 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+TableEntry g_singleColor6_2_p[256] =
+{
+ { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 4, 2, 4 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 4, 4, 0 }, { 4, 8, 6, 4 }, { 8, 8, 8, 0 },
+ { 8, 8, 8, 0 }, { 8, 8, 8, 0 }, { 8, 12, 10, 4 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 12, 12, 0 }, { 12, 16, 14, 4 }, { 16, 16, 16, 0 },
+ { 16, 16, 16, 0 }, { 16, 16, 16, 0 }, { 16, 20, 18, 4 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 20, 20, 0 }, { 20, 24, 22, 4 }, { 24, 24, 24, 0 },
+ { 24, 24, 24, 0 }, { 24, 24, 24, 0 }, { 24, 28, 26, 4 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 28, 28, 0 }, { 28, 32, 30, 4 }, { 32, 32, 32, 0 },
+ { 32, 32, 32, 0 }, { 32, 32, 32, 0 }, { 32, 36, 34, 4 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 36, 36, 0 }, { 36, 40, 38, 4 }, { 40, 40, 40, 0 },
+ { 40, 40, 40, 0 }, { 40, 40, 40, 0 }, { 40, 44, 42, 4 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 44, 44, 0 }, { 44, 48, 46, 4 }, { 48, 48, 48, 0 },
+ { 48, 48, 48, 0 }, { 48, 48, 48, 0 }, { 48, 52, 50, 4 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 52, 52, 0 }, { 52, 56, 54, 4 }, { 56, 56, 56, 0 },
+ { 56, 56, 56, 0 }, { 56, 56, 56, 0 }, { 56, 60, 58, 4 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 60, 60, 0 }, { 60, 65, 62, 5 }, { 60, 65, 62, 5 },
+ { 60, 69, 64, 9 }, { 65, 65, 65, 0 }, { 60, 73, 66, 13 }, { 65, 69, 67, 4 }, { 60, 77, 68, 17 }, { 69, 69, 69, 0 }, { 60, 81, 70, 21 }, { 69, 73, 71, 4 },
+ { 60, 85, 72, 25 }, { 73, 73, 73, 0 }, { 60, 89, 74, 29 }, { 73, 77, 75, 4 }, { 60, 93, 76, 33 }, { 77, 77, 77, 0 }, { 77, 77, 77, 0 }, { 77, 81, 79, 4 },
+ { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 81, 81, 81, 0 }, { 81, 85, 83, 4 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 85, 85, 85, 0 }, { 85, 89, 87, 4 },
+ { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 89, 89, 89, 0 }, { 89, 93, 91, 4 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 93, 93, 0 }, { 93, 97, 95, 4 },
+ { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 97, 97, 0 }, { 97, 101, 99, 4 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 101, 101, 0 }, { 101, 105, 103, 4 },
+ { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 105, 105, 0 }, { 105, 109, 107, 4 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 109, 109, 0 }, { 109, 113, 111, 4 },
+ { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 113, 113, 0 }, { 113, 117, 115, 4 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 117, 117, 0 }, { 117, 121, 119, 4 },
+ { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 121, 121, 0 }, { 121, 125, 123, 4 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 125, 125, 0 }, { 125, 130, 127, 5 },
+ { 125, 130, 127, 5 }, { 125, 134, 129, 9 }, { 130, 130, 130, 0 }, { 125, 138, 131, 13 }, { 130, 134, 132, 4 }, { 125, 142, 133, 17 }, { 134, 134, 134, 0 }, { 125, 146, 135, 21 },
+ { 134, 138, 136, 4 }, { 125, 150, 137, 25 }, { 138, 138, 138, 0 }, { 125, 154, 139, 29 }, { 138, 142, 140, 4 }, { 125, 158, 141, 33 }, { 142, 142, 142, 0 }, { 142, 142, 142, 0 },
+ { 142, 146, 144, 4 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 146, 146, 146, 0 }, { 146, 150, 148, 4 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 }, { 150, 150, 150, 0 },
+ { 150, 154, 152, 4 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 154, 154, 154, 0 }, { 154, 158, 156, 4 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 }, { 158, 158, 158, 0 },
+ { 158, 162, 160, 4 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 162, 162, 0 }, { 162, 166, 164, 4 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 }, { 166, 166, 166, 0 },
+ { 166, 170, 168, 4 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 170, 170, 0 }, { 170, 174, 172, 4 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 }, { 174, 174, 174, 0 },
+ { 174, 178, 176, 4 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 178, 178, 0 }, { 178, 182, 180, 4 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 }, { 182, 182, 182, 0 },
+ { 182, 186, 184, 4 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 186, 186, 0 }, { 186, 190, 188, 4 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 }, { 190, 190, 190, 0 },
+ { 190, 195, 192, 5 }, { 190, 195, 192, 5 }, { 190, 199, 194, 9 }, { 195, 195, 195, 0 }, { 190, 203, 196, 13 }, { 195, 199, 197, 4 }, { 190, 207, 198, 17 }, { 199, 199, 199, 0 },
+ { 190, 211, 200, 21 }, { 199, 203, 201, 4 }, { 190, 215, 202, 25 }, { 203, 203, 203, 0 }, { 190, 219, 204, 29 }, { 203, 207, 205, 4 }, { 190, 223, 206, 33 }, { 207, 207, 207, 0 },
+ { 207, 207, 207, 0 }, { 207, 211, 209, 4 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 211, 211, 211, 0 }, { 211, 215, 213, 4 }, { 215, 215, 215, 0 }, { 215, 215, 215, 0 },
+ { 215, 215, 215, 0 }, { 215, 219, 217, 4 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 219, 219, 219, 0 }, { 219, 223, 221, 4 }, { 223, 223, 223, 0 }, { 223, 223, 223, 0 },
+ { 223, 223, 223, 0 }, { 223, 227, 225, 4 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 227, 227, 0 }, { 227, 231, 229, 4 }, { 231, 231, 231, 0 }, { 231, 231, 231, 0 },
+ { 231, 231, 231, 0 }, { 231, 235, 233, 4 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 235, 235, 0 }, { 235, 239, 237, 4 }, { 239, 239, 239, 0 }, { 239, 239, 239, 0 },
+ { 239, 239, 239, 0 }, { 239, 243, 241, 4 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 243, 243, 0 }, { 243, 247, 245, 4 }, { 247, 247, 247, 0 }, { 247, 247, 247, 0 },
+ { 247, 247, 247, 0 }, { 247, 251, 249, 4 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 251, 251, 0 }, { 251, 255, 253, 4 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 },
+};
+
+}}}
diff --git a/thirdparty/cvtt/ConvectionKernels_SingleFile.cpp b/thirdparty/cvtt/ConvectionKernels_SingleFile.cpp
new file mode 100644
index 0000000000..ad59988655
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_SingleFile.cpp
@@ -0,0 +1,48 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if defined(CVTT_SINGLE_FILE)
+#define CVTT_SINGLE_FILE_IMPL
+
+#include "ConvectionKernels_API.cpp"
+#include "ConvectionKernels_BC67.cpp"
+#include "ConvectionKernels_BC6H_IO.cpp"
+#include "ConvectionKernels_BC7_PrioData.cpp"
+#include "ConvectionKernels_BCCommon.cpp"
+#include "ConvectionKernels_ETC.cpp"
+#include "ConvectionKernels_IndexSelector.cpp"
+#include "ConvectionKernels_S3TC.cpp"
+#include "ConvectionKernels_Util.cpp"
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_UnfinishedEndpoints.h b/thirdparty/cvtt/ConvectionKernels_UnfinishedEndpoints.h
new file mode 100644
index 0000000000..371cbe54bf
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_UnfinishedEndpoints.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#include "ConvectionKernels_Util.h"
+
+namespace cvtt
+{
+ namespace Internal
+ {
+ template<int TVectorSize>
+ class UnfinishedEndpoints
+ {
+ public:
+ typedef ParallelMath::Float MFloat;
+ typedef ParallelMath::UInt16 MUInt16;
+ typedef ParallelMath::UInt15 MUInt15;
+ typedef ParallelMath::SInt16 MSInt16;
+ typedef ParallelMath::SInt32 MSInt32;
+
+ UnfinishedEndpoints()
+ {
+ }
+
+ UnfinishedEndpoints(const MFloat *base, const MFloat *offset)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_base[ch] = base[ch];
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_offset[ch] = offset[ch];
+ }
+
+ UnfinishedEndpoints(const UnfinishedEndpoints& other)
+ {
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_base[ch] = other.m_base[ch];
+ for (int ch = 0; ch < TVectorSize; ch++)
+ m_offset[ch] = other.m_offset[ch];
+ }
+
+ void FinishHDRUnsigned(int tweak, int range, MSInt16 *outEP0, MSInt16 *outEP1, ParallelMath::RoundTowardNearestForScope *roundingMode)
+ {
+ float tweakFactors[2];
+ Util::ComputeTweakFactors(tweak, range, tweakFactors);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MUInt15 channelEPs[2];
+ for (int epi = 0; epi < 2; epi++)
+ {
+ MFloat f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[epi], 0.0f, 31743.0f);
+ channelEPs[epi] = ParallelMath::RoundAndConvertToU15(f, roundingMode);
+ }
+
+ outEP0[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(channelEPs[0]);
+ outEP1[ch] = ParallelMath::LosslessCast<MSInt16>::Cast(channelEPs[1]);
+ }
+ }
+
+ void FinishHDRSigned(int tweak, int range, MSInt16* outEP0, MSInt16* outEP1, ParallelMath::RoundTowardNearestForScope* roundingMode)
+ {
+ float tweakFactors[2];
+ Util::ComputeTweakFactors(tweak, range, tweakFactors);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MSInt16 channelEPs[2];
+ for (int epi = 0; epi < 2; epi++)
+ {
+ MFloat f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[epi], -31743.0f, 31743.0f);
+ channelEPs[epi] = ParallelMath::RoundAndConvertToS16(f, roundingMode);
+ }
+
+ outEP0[ch] = channelEPs[0];
+ outEP1[ch] = channelEPs[1];
+ }
+ }
+
+ void FinishLDR(int tweak, int range, MUInt15* outEP0, MUInt15* outEP1)
+ {
+ ParallelMath::RoundTowardNearestForScope roundingMode;
+
+ float tweakFactors[2];
+ Util::ComputeTweakFactors(tweak, range, tweakFactors);
+
+ for (int ch = 0; ch < TVectorSize; ch++)
+ {
+ MFloat ep0f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[0], 0.0f, 255.0f);
+ MFloat ep1f = ParallelMath::Clamp(m_base[ch] + m_offset[ch] * tweakFactors[1], 0.0f, 255.0f);
+ outEP0[ch] = ParallelMath::RoundAndConvertToU15(ep0f, &roundingMode);
+ outEP1[ch] = ParallelMath::RoundAndConvertToU15(ep1f, &roundingMode);
+ }
+ }
+
+ template<int TNewVectorSize>
+ UnfinishedEndpoints<TNewVectorSize> ExpandTo(float filler)
+ {
+ MFloat newBase[TNewVectorSize];
+ MFloat newOffset[TNewVectorSize];
+
+ for (int ch = 0; ch < TNewVectorSize && ch < TVectorSize; ch++)
+ {
+ newBase[ch] = m_base[ch];
+ newOffset[ch] = m_offset[ch];
+ }
+
+ MFloat fillerV = ParallelMath::MakeFloat(filler);
+
+ for (int ch = TVectorSize; ch < TNewVectorSize; ch++)
+ {
+ newBase[ch] = fillerV;
+ newOffset[ch] = ParallelMath::MakeFloatZero();
+ }
+
+ return UnfinishedEndpoints<TNewVectorSize>(newBase, newOffset);
+ }
+
+ private:
+ MFloat m_base[TVectorSize];
+ MFloat m_offset[TVectorSize];
+ };
+ }
+}
diff --git a/thirdparty/cvtt/ConvectionKernels_Util.cpp b/thirdparty/cvtt/ConvectionKernels_Util.cpp
new file mode 100644
index 0000000000..d9c25c7845
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_Util.cpp
@@ -0,0 +1,88 @@
+/*
+Convection Texture Tools
+Copyright (c) 2018-2019 Eric Lasota
+
+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.
+
+-------------------------------------------------------------------------------------
+
+Portions based on DirectX Texture Library (DirectXTex)
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+Licensed under the MIT License.
+
+http://go.microsoft.com/fwlink/?LinkId=248926
+*/
+#include "ConvectionKernels_Config.h"
+
+#if !defined(CVTT_SINGLE_FILE) || defined(CVTT_SINGLE_FILE_IMPL)
+
+#include "ConvectionKernels.h"
+#include "ConvectionKernels_ParallelMath.h"
+
+#include <algorithm>
+
+namespace cvtt
+{
+ namespace Util
+ {
+ // Signed input blocks are converted into unsigned space, with the maximum value being 254
+ void BiasSignedInput(PixelBlockU8 inputNormalized[ParallelMath::ParallelSize], const PixelBlockS8 inputSigned[ParallelMath::ParallelSize])
+ {
+ for (size_t block = 0; block < ParallelMath::ParallelSize; block++)
+ {
+ const PixelBlockS8& inputSignedBlock = inputSigned[block];
+ PixelBlockU8& inputNormalizedBlock = inputNormalized[block];
+
+ for (size_t px = 0; px < 16; px++)
+ {
+ for (size_t ch = 0; ch < 4; ch++)
+ inputNormalizedBlock.m_pixels[px][ch] = static_cast<uint8_t>(std::max<int>(inputSignedBlock.m_pixels[px][ch], -127) + 127);
+ }
+ }
+ }
+
+ void FillWeights(const Options &options, float channelWeights[4])
+ {
+ if (options.flags & Flags::Uniform)
+ channelWeights[0] = channelWeights[1] = channelWeights[2] = channelWeights[3] = 1.0f;
+ else
+ {
+ channelWeights[0] = options.redWeight;
+ channelWeights[1] = options.greenWeight;
+ channelWeights[2] = options.blueWeight;
+ channelWeights[3] = options.alphaWeight;
+ }
+ }
+
+ void ComputeTweakFactors(int tweak, int range, float *outFactors)
+ {
+ int totalUnits = range - 1;
+ int minOutsideUnits = ((tweak >> 1) & 1);
+ int maxOutsideUnits = (tweak & 1);
+ int insideUnits = totalUnits - minOutsideUnits - maxOutsideUnits;
+
+ outFactors[0] = -static_cast<float>(minOutsideUnits) / static_cast<float>(insideUnits);
+ outFactors[1] = static_cast<float>(maxOutsideUnits) / static_cast<float>(insideUnits) + 1.0f;
+ }
+ }
+}
+
+#endif
diff --git a/thirdparty/cvtt/ConvectionKernels_Util.h b/thirdparty/cvtt/ConvectionKernels_Util.h
new file mode 100644
index 0000000000..c07b9bf2aa
--- /dev/null
+++ b/thirdparty/cvtt/ConvectionKernels_Util.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "ConvectionKernels_ParallelMath.h"
+
+namespace cvtt
+{
+ struct PixelBlockU8;
+ struct PixelBlockS8;
+ struct Options;
+}
+
+namespace cvtt
+{
+ namespace Util
+ {
+ // Signed input blocks are converted into unsigned space, with the maximum value being 254
+ void BiasSignedInput(PixelBlockU8 inputNormalized[ParallelMath::ParallelSize], const PixelBlockS8 inputSigned[ParallelMath::ParallelSize]);
+ void FillWeights(const Options &options, float channelWeights[4]);
+ void ComputeTweakFactors(int tweak, int range, float *outFactors);
+ }
+}
diff --git a/thirdparty/cvtt/etc_notes.txt b/thirdparty/cvtt/etc_notes.txt
new file mode 100644
index 0000000000..bb041a8435
--- /dev/null
+++ b/thirdparty/cvtt/etc_notes.txt
@@ -0,0 +1,27 @@
+The ETC1 compressor uses modified cluster fit:
+
+Assume that there exists an ideal base color and set of selectors for a given table.
+For a given table and set of selectors, the ideal base color can be determined by subtracting the offsets from each pixel and averaging them.
+Doing that is equivalent to subtracting the average offset from the average color.
+Because positive and negative selectors of the same magnitude cancel out, the search space of possible average offsets is reduced: 57 unique offsets for the first table and 81 for the others.
+Most of the offsets result in the same color as another average offset due to quantization of the base color, so those can be de-duplicated.
+So:
+- Start with a high-precision average color.
+- Apply precomputed luma offsets to it.
+- Quantize and de-duplicate the base colors.
+- Find the ideal selectors for each base color.
+
+Differential mode is solved by just finding the best legal combination from those attempts.
+
+There are several scenarios where this is not ideal:
+- Clamping behavior can sometimes be leveraged for a more accurate block.
+- Differentials can sometimes be moved slightly closer to become legal.
+- This only works when MSE is the error metric (i.e. not normal maps)
+- This only works when pixel weights are of equal importance (i.e. not using weight by alpha or edge deblocking)
+
+T and H mode just work by generating clustering assignments by computing a chrominance line and splitting the block in half by the chrominance midpoint and using those to determine the averages.
+
+Planar mode is just solved algebraically.
+
+If you want to emulate etc2comp's default settings, add the flag ETC_UseFakeBT709 to use its modified Rec. 709 error coefficients.
+Doing that will significantly slow down encoding because it requires much more complicated quantization math. \ No newline at end of file
diff --git a/thirdparty/misc/patches/polypartition-godot-types.patch b/thirdparty/misc/patches/polypartition-godot-types.patch
index 782f02e8dc..61737f9fd2 100644
--- a/thirdparty/misc/patches/polypartition-godot-types.patch
+++ b/thirdparty/misc/patches/polypartition-godot-types.patch
@@ -1,19 +1,16 @@
diff --git a/thirdparty/misc/polypartition.cpp b/thirdparty/misc/polypartition.cpp
-index 3a8a6efa83..5e94793b79 100644
+index 3a8a6efa83..8c5409bf24 100644
--- a/thirdparty/misc/polypartition.cpp
+++ b/thirdparty/misc/polypartition.cpp
-@@ -23,10 +23,7 @@
-
- #include "polypartition.h"
-
--#include <math.h>
--#include <string.h>
+@@ -26,7 +26,6 @@
+ #include <math.h>
+ #include <string.h>
#include <algorithm>
-#include <vector>
TPPLPoly::TPPLPoly() {
hole = false;
-@@ -186,7 +183,7 @@ int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TP
+@@ -186,7 +185,7 @@ int TPPLPartition::Intersects(TPPLPoint &p11, TPPLPoint &p12, TPPLPoint &p21, TP
// Removes holes from inpolys by merging them with non-holes.
int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
TPPLPolyList polys;
@@ -22,7 +19,7 @@ index 3a8a6efa83..5e94793b79 100644
long i, i2, holepointindex, polypointindex;
TPPLPoint holepoint, polypoint, bestpolypoint;
TPPLPoint linep1, linep2;
-@@ -198,15 +195,15 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -198,15 +197,15 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
// Check for the trivial case of no holes.
hasholes = false;
@@ -42,7 +39,7 @@ index 3a8a6efa83..5e94793b79 100644
}
return 1;
}
-@@ -216,8 +213,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -216,8 +215,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
while (1) {
// Find the hole point with the largest x.
hasholes = false;
@@ -53,7 +50,7 @@ index 3a8a6efa83..5e94793b79 100644
continue;
}
-@@ -227,8 +224,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -227,8 +226,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
holepointindex = 0;
}
@@ -64,7 +61,7 @@ index 3a8a6efa83..5e94793b79 100644
holeiter = iter;
holepointindex = i;
}
-@@ -237,24 +234,24 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -237,24 +236,24 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
if (!hasholes) {
break;
}
@@ -98,7 +95,7 @@ index 3a8a6efa83..5e94793b79 100644
if (pointfound) {
v1 = Normalize(polypoint - holepoint);
v2 = Normalize(bestpolypoint - holepoint);
-@@ -263,13 +260,13 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -263,13 +262,13 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
}
}
pointvisible = true;
@@ -117,7 +114,7 @@ index 3a8a6efa83..5e94793b79 100644
if (Intersects(holepoint, polypoint, linep1, linep2)) {
pointvisible = false;
break;
-@@ -292,18 +289,18 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -292,18 +291,18 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
return 0;
}
@@ -142,7 +139,7 @@ index 3a8a6efa83..5e94793b79 100644
i2++;
}
-@@ -312,8 +309,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
+@@ -312,8 +311,8 @@ int TPPLPartition::RemoveHoles(TPPLPolyList *inpolys, TPPLPolyList *outpolys) {
polys.push_back(newpoly);
}
@@ -153,7 +150,7 @@ index 3a8a6efa83..5e94793b79 100644
}
return 1;
-@@ -524,13 +521,13 @@ int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) {
+@@ -524,13 +523,13 @@ int TPPLPartition::Triangulate_EC(TPPLPoly *poly, TPPLPolyList *triangles) {
int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
TPPLPolyList outpolys;
@@ -170,7 +167,7 @@ index 3a8a6efa83..5e94793b79 100644
return 0;
}
}
-@@ -543,7 +540,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -543,7 +542,7 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
}
TPPLPolyList triangles;
@@ -179,7 +176,7 @@ index 3a8a6efa83..5e94793b79 100644
TPPLPoly *poly1 = NULL, *poly2 = NULL;
TPPLPoly newpoly;
TPPLPoint d1, d2, p1, p2, p3;
-@@ -578,19 +575,19 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -578,19 +577,19 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
return 0;
}
@@ -203,7 +200,7 @@ index 3a8a6efa83..5e94793b79 100644
for (i21 = 0; i21 < poly2->GetNumPoints(); i21++) {
if ((d2.x != poly2->GetPoint(i21).x) || (d2.y != poly2->GetPoint(i21).y)) {
-@@ -660,16 +657,16 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -660,16 +659,16 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
}
triangles.erase(iter2);
@@ -224,7 +221,7 @@ index 3a8a6efa83..5e94793b79 100644
}
return 1;
-@@ -677,13 +674,13 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -677,13 +676,13 @@ int TPPLPartition::ConvexPartition_HM(TPPLPoly *poly, TPPLPolyList *parts) {
int TPPLPartition::ConvexPartition_HM(TPPLPolyList *inpolys, TPPLPolyList *parts) {
TPPLPolyList outpolys;
@@ -241,7 +238,7 @@ index 3a8a6efa83..5e94793b79 100644
return 0;
}
}
-@@ -824,8 +821,8 @@ int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) {
+@@ -824,8 +823,8 @@ int TPPLPartition::Triangulate_OPT(TPPLPoly *poly, TPPLPolyList *triangles) {
newdiagonal.index1 = 0;
newdiagonal.index2 = n - 1;
diagonals.push_back(newdiagonal);
@@ -252,7 +249,7 @@ index 3a8a6efa83..5e94793b79 100644
diagonals.pop_front();
bestvertex = dpstates[diagonal.index2][diagonal.index1].bestvertex;
if (bestvertex == -1) {
-@@ -873,10 +870,10 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
+@@ -873,10 +872,10 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
pairs->push_front(newdiagonal);
dpstates[a][b].weight = w;
} else {
@@ -265,7 +262,7 @@ index 3a8a6efa83..5e94793b79 100644
pairs->pop_front();
}
pairs->push_front(newdiagonal);
-@@ -885,7 +882,7 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
+@@ -885,7 +884,7 @@ void TPPLPartition::UpdateState(long a, long b, long w, long i, long j, DPState2
void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
DiagonalList *pairs = NULL;
@@ -274,7 +271,7 @@ index 3a8a6efa83..5e94793b79 100644
long top;
long w;
-@@ -902,23 +899,23 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
+@@ -902,23 +901,23 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
}
if (j - i > 1) {
pairs = &(dpstates[i][j].pairs);
@@ -305,7 +302,7 @@ index 3a8a6efa83..5e94793b79 100644
}
}
}
-@@ -927,7 +924,7 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
+@@ -927,7 +926,7 @@ void TPPLPartition::TypeA(long i, long j, long k, PartitionVertex *vertices, DPS
void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPState2 **dpstates) {
DiagonalList *pairs = NULL;
@@ -314,7 +311,7 @@ index 3a8a6efa83..5e94793b79 100644
long top;
long w;
-@@ -946,21 +943,21 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
+@@ -946,21 +945,21 @@ void TPPLPartition::TypeB(long i, long j, long k, PartitionVertex *vertices, DPS
if (k - j > 1) {
pairs = &(dpstates[j][k].pairs);
@@ -343,7 +340,7 @@ index 3a8a6efa83..5e94793b79 100644
}
} else {
w++;
-@@ -981,11 +978,11 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -981,11 +980,11 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
DiagonalList diagonals, diagonals2;
Diagonal diagonal, newdiagonal;
DiagonalList *pairs = NULL, *pairs2 = NULL;
@@ -358,7 +355,7 @@ index 3a8a6efa83..5e94793b79 100644
bool ijreal, jkreal;
n = poly->GetNumPoints();
-@@ -1110,35 +1107,35 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1110,35 +1109,35 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
newdiagonal.index1 = 0;
newdiagonal.index2 = n - 1;
diagonals.push_front(newdiagonal);
@@ -403,7 +400,7 @@ index 3a8a6efa83..5e94793b79 100644
pairs2->pop_back();
} else {
break;
-@@ -1153,21 +1150,21 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1153,21 +1152,21 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
diagonals.push_front(newdiagonal);
}
} else {
@@ -431,7 +428,7 @@ index 3a8a6efa83..5e94793b79 100644
pairs2->pop_front();
} else {
break;
-@@ -1197,8 +1194,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1197,8 +1196,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
newdiagonal.index1 = 0;
newdiagonal.index2 = n - 1;
diagonals.push_front(newdiagonal);
@@ -442,7 +439,7 @@ index 3a8a6efa83..5e94793b79 100644
diagonals.pop_front();
if ((diagonal.index2 - diagonal.index1) <= 1) {
continue;
-@@ -1210,8 +1207,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1210,8 +1209,8 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
indices.push_back(diagonal.index2);
diagonals2.push_front(diagonal);
@@ -453,7 +450,7 @@ index 3a8a6efa83..5e94793b79 100644
diagonals2.pop_front();
if ((diagonal.index2 - diagonal.index1) <= 1) {
continue;
-@@ -1220,16 +1217,16 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1220,16 +1219,16 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
jkreal = true;
pairs = &(dpstates[diagonal.index1][diagonal.index2].pairs);
if (!vertices[diagonal.index1].isConvex) {
@@ -476,7 +473,7 @@ index 3a8a6efa83..5e94793b79 100644
jkreal = false;
}
}
-@@ -1253,11 +1250,12 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1253,11 +1252,12 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
indices.push_back(j);
}
@@ -492,7 +489,7 @@ index 3a8a6efa83..5e94793b79 100644
k++;
}
parts->push_back(newpoly);
-@@ -1281,7 +1279,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
+@@ -1281,7 +1281,7 @@ int TPPLPartition::ConvexPartition_OPT(TPPLPoly *poly, TPPLPolyList *parts) {
// "Computational Geometry: Algorithms and Applications"
// by Mark de Berg, Otfried Cheong, Marc van Kreveld, and Mark Overmars.
int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monotonePolys) {
@@ -501,7 +498,7 @@ index 3a8a6efa83..5e94793b79 100644
MonotoneVertex *vertices = NULL;
long i, numvertices, vindex, vindex2, newnumvertices, maxnumvertices;
long polystartindex, polyendindex;
-@@ -1291,11 +1289,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1291,11 +1291,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
bool error = false;
numvertices = 0;
@@ -515,7 +512,7 @@ index 3a8a6efa83..5e94793b79 100644
}
maxnumvertices = numvertices * 3;
-@@ -1303,8 +1298,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1303,8 +1300,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
newnumvertices = numvertices;
polystartindex = 0;
@@ -526,7 +523,7 @@ index 3a8a6efa83..5e94793b79 100644
polyendindex = polystartindex + poly->GetNumPoints() - 1;
for (i = 0; i < poly->GetNumPoints(); i++) {
vertices[i + polystartindex].p = poly->GetPoint(i);
-@@ -1360,14 +1355,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1360,14 +1357,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
// Note that while set doesn't actually have to be implemented as
// a tree, complexity requirements for operations are the same as
// for the balanced binary search tree.
@@ -546,7 +543,7 @@ index 3a8a6efa83..5e94793b79 100644
}
// For each vertex.
-@@ -1387,13 +1382,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1387,13 +1384,14 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
newedge.p1 = v->p;
newedge.p2 = vertices[v->next].p;
newedge.index = vindex;
@@ -564,7 +561,7 @@ index 3a8a6efa83..5e94793b79 100644
error = true;
break;
}
-@@ -1412,29 +1408,30 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1412,29 +1410,30 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
newedge.p1 = v->p;
newedge.p2 = v->p;
edgeIter = edgeTree.lower_bound(newedge);
@@ -601,7 +598,7 @@ index 3a8a6efa83..5e94793b79 100644
error = true;
break;
}
-@@ -1452,25 +1449,25 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1452,25 +1451,25 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
newedge.p1 = v->p;
newedge.p2 = v->p;
edgeIter = edgeTree.lower_bound(newedge);
@@ -632,7 +629,7 @@ index 3a8a6efa83..5e94793b79 100644
error = true;
break;
}
-@@ -1488,27 +1485,28 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1488,27 +1487,28 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
newedge.p1 = v2->p;
newedge.p2 = vertices[v2->next].p;
newedge.index = vindex2;
@@ -668,7 +665,7 @@ index 3a8a6efa83..5e94793b79 100644
}
break;
}
-@@ -1569,8 +1567,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
+@@ -1569,8 +1569,8 @@ int TPPLPartition::MonotonePartition(TPPLPolyList *inpolys, TPPLPolyList *monoto
// Adds a diagonal to the doubly-connected list of vertices.
void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, long index1, long index2,
@@ -679,7 +676,7 @@ index 3a8a6efa83..5e94793b79 100644
long newindex1, newindex2;
newindex1 = *numvertices;
-@@ -1597,14 +1595,14 @@ void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, lon
+@@ -1597,14 +1597,14 @@ void TPPLPartition::AddDiagonal(MonotoneVertex *vertices, long *numvertices, lon
vertextypes[newindex1] = vertextypes[index1];
edgeTreeIterators[newindex1] = edgeTreeIterators[index1];
helpers[newindex1] = helpers[index1];
@@ -698,7 +695,7 @@ index 3a8a6efa83..5e94793b79 100644
}
}
-@@ -1830,13 +1828,13 @@ int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles
+@@ -1830,13 +1830,13 @@ int TPPLPartition::TriangulateMonotone(TPPLPoly *inPoly, TPPLPolyList *triangles
int TPPLPartition::Triangulate_MONO(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
TPPLPolyList monotone;
diff --git a/thirdparty/misc/polypartition.cpp b/thirdparty/misc/polypartition.cpp
index 5e94793b79..8c5409bf24 100644
--- a/thirdparty/misc/polypartition.cpp
+++ b/thirdparty/misc/polypartition.cpp
@@ -23,6 +23,8 @@
#include "polypartition.h"
+#include <math.h>
+#include <string.h>
#include <algorithm>
TPPLPoly::TPPLPoly() {