diff options
Diffstat (limited to 'modules')
73 files changed, 949 insertions, 379 deletions
diff --git a/modules/camera/SCsub b/modules/camera/SCsub index de97724d09..9a6147d433 100644 --- a/modules/camera/SCsub +++ b/modules/camera/SCsub @@ -9,6 +9,6 @@ if env["platform"] == "windows": env_camera.add_source_files(env.modules_sources, "register_types.cpp") env_camera.add_source_files(env.modules_sources, "camera_win.cpp") -elif env["platform"] == "osx": +elif env["platform"] == "macos": env_camera.add_source_files(env.modules_sources, "register_types.cpp") - env_camera.add_source_files(env.modules_sources, "camera_osx.mm") + env_camera.add_source_files(env.modules_sources, "camera_macos.mm") diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_macos.h index b0db844599..badf78f0e8 100644 --- a/modules/camera/camera_osx.h +++ b/modules/camera/camera_macos.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* camera_osx.h */ +/* camera_macos.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,19 +28,19 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef CAMERAOSX_H -#define CAMERAOSX_H +#ifndef CAMERA_MACOS_H +#define CAMERA_MACOS_H ///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!! // If you fix something here, make sure you fix it there as well! #include "servers/camera_server.h" -class CameraOSX : public CameraServer { +class CameraMacOS : public CameraServer { public: - CameraOSX(); + CameraMacOS(); void update_feeds(); }; -#endif /* CAMERAOSX_H */ +#endif /* CAMERA_MACOS_H */ diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_macos.mm index d199c31b2f..0b9696a3e9 100644 --- a/modules/camera/camera_osx.mm +++ b/modules/camera/camera_macos.mm @@ -1,5 +1,5 @@ /*************************************************************************/ -/* camera_osx.mm */ +/* camera_macos.mm */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -31,7 +31,7 @@ ///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!! // If you fix something here, make sure you fix it there as well! -#include "camera_osx.h" +#include "camera_macos.h" #include "servers/camera/camera_feed.h" #import <AVFoundation/AVFoundation.h> @@ -191,9 +191,9 @@ @end ////////////////////////////////////////////////////////////////////////// -// CameraFeedOSX - Subclass for camera feeds in OSX +// CameraFeedMacOS - Subclass for camera feeds in macOS -class CameraFeedOSX : public CameraFeed { +class CameraFeedMacOS : public CameraFeed { private: AVCaptureDevice *device; MyCaptureSession *capture_session; @@ -201,7 +201,7 @@ private: public: AVCaptureDevice *get_device() const; - CameraFeedOSX(); + CameraFeedMacOS(); void set_device(AVCaptureDevice *p_device); @@ -209,16 +209,16 @@ public: void deactivate_feed(); }; -AVCaptureDevice *CameraFeedOSX::get_device() const { +AVCaptureDevice *CameraFeedMacOS::get_device() const { return device; }; -CameraFeedOSX::CameraFeedOSX() { +CameraFeedMacOS::CameraFeedMacOS() { device = nullptr; capture_session = nullptr; }; -void CameraFeedOSX::set_device(AVCaptureDevice *p_device) { +void CameraFeedMacOS::set_device(AVCaptureDevice *p_device) { device = p_device; // get some info @@ -232,7 +232,7 @@ void CameraFeedOSX::set_device(AVCaptureDevice *p_device) { }; }; -bool CameraFeedOSX::activate_feed() { +bool CameraFeedMacOS::activate_feed() { if (capture_session) { // Already recording! } else { @@ -258,7 +258,7 @@ bool CameraFeedOSX::activate_feed() { return true; }; -void CameraFeedOSX::deactivate_feed() { +void CameraFeedMacOS::deactivate_feed() { // end camera capture if we have one if (capture_session) { [capture_session cleanup]; @@ -271,7 +271,7 @@ void CameraFeedOSX::deactivate_feed() { // when devices are connected/disconnected @interface MyDeviceNotifications : NSObject { - CameraOSX *camera_server; + CameraMacOS *camera_server; } @end @@ -282,7 +282,7 @@ void CameraFeedOSX::deactivate_feed() { camera_server->update_feeds(); } -- (id)initForServer:(CameraOSX *)p_server { +- (id)initForServer:(CameraMacOS *)p_server { if (self = [super init]) { camera_server = p_server; @@ -303,9 +303,9 @@ void CameraFeedOSX::deactivate_feed() { MyDeviceNotifications *device_notifications = nil; ////////////////////////////////////////////////////////////////////////// -// CameraOSX - Subclass for our camera server on OSX +// CameraMacOS - Subclass for our camera server on macOS -void CameraOSX::update_feeds() { +void CameraMacOS::update_feeds() { #if MAC_OS_X_VERSION_MIN_REQUIRED >= 101500 AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeExternalUnknown, AVCaptureDeviceTypeBuiltInWideAngleCamera, nil] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; NSArray *devices = session.devices; @@ -315,7 +315,7 @@ void CameraOSX::update_feeds() { // remove devices that are gone.. for (int i = feeds.size() - 1; i >= 0; i--) { - Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i]; + Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i]; if (![devices containsObject:feed->get_device()]) { // remove it from our array, this will also destroy it ;) @@ -326,14 +326,14 @@ void CameraOSX::update_feeds() { for (AVCaptureDevice *device in devices) { bool found = false; for (int i = 0; i < feeds.size() && !found; i++) { - Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i]; + Ref<CameraFeedMacOS> feed = (Ref<CameraFeedMacOS>)feeds[i]; if (feed->get_device() == device) { found = true; }; }; if (!found) { - Ref<CameraFeedOSX> newfeed; + Ref<CameraFeedMacOS> newfeed; newfeed.instantiate(); newfeed->set_device(device); @@ -346,7 +346,7 @@ void CameraOSX::update_feeds() { }; }; -CameraOSX::CameraOSX() { +CameraMacOS::CameraMacOS() { // Find available cameras we have at this time update_feeds(); diff --git a/modules/camera/config.py b/modules/camera/config.py index 8a22751aa7..d2b2542dd9 100644 --- a/modules/camera/config.py +++ b/modules/camera/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return platform == "osx" or platform == "windows" + return platform == "macos" or platform == "windows" def configure(env): diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp index 98a4b5ca1a..40e2224d6b 100644 --- a/modules/camera/register_types.cpp +++ b/modules/camera/register_types.cpp @@ -33,8 +33,8 @@ #if defined(WINDOWS_ENABLED) #include "camera_win.h" #endif -#if defined(OSX_ENABLED) -#include "camera_osx.h" +#if defined(MACOS_ENABLED) +#include "camera_macos.h" #endif void initialize_camera_module(ModuleInitializationLevel p_level) { @@ -45,8 +45,8 @@ void initialize_camera_module(ModuleInitializationLevel p_level) { #if defined(WINDOWS_ENABLED) CameraServer::make_default<CameraWindows>(); #endif -#if defined(OSX_ENABLED) - CameraServer::make_default<CameraOSX>(); +#if defined(MACOS_ENABLED) + CameraServer::make_default<CameraMacOS>(); #endif } diff --git a/modules/denoise/config.py b/modules/denoise/config.py index 3aa840acb0..521115dae5 100644 --- a/modules/denoise/config.py +++ b/modules/denoise/config.py @@ -4,7 +4,7 @@ def can_build(env, platform): # It's also only relevant for tools build and desktop platforms, # as doing lightmap generation and denoising on Android or HTML5 # would be a bit far-fetched. - desktop_platforms = ["linuxbsd", "osx", "windows"] + desktop_platforms = ["linuxbsd", "macos", "windows"] supported_arch = env["bits"] == "64" if env["arch"] == "arm64": supported_arch = False diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 049be47ca8..10cf783e73 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -144,6 +144,7 @@ [/codeblock] [b]Important:[/b] The path must be absolute, a local path will just return [code]null[/code]. This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. + [b]Note:[/b] You have to import the files into the engine first to load them using [method load]. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. </description> </method> <method name="preload"> @@ -285,16 +286,15 @@ <description> </description> </annotation> - <annotation name="@export_exp_easing"> + <annotation name="@export_exp_easing" qualifiers="vararg"> <return type="void" /> - <argument index="0" name="hint1" type="String" default="null" /> - <argument index="1" name="hint2" type="String" default="null" /> + <argument index="0" name="hints" type="String" default="""" /> <description> </description> </annotation> <annotation name="@export_file" qualifiers="vararg"> <return type="void" /> - <argument index="0" name="filter" type="String" default="null" /> + <argument index="0" name="filter" type="String" default="""" /> <description> </description> </annotation> @@ -341,14 +341,14 @@ </annotation> <annotation name="@export_global_file" qualifiers="vararg"> <return type="void" /> - <argument index="0" name="filter" type="String" default="null" /> + <argument index="0" name="filter" type="String" default="""" /> <description> </description> </annotation> <annotation name="@export_group"> <return type="void" /> <argument index="0" name="name" type="String" /> - <argument index="1" name="prefix" type="String" default="null" /> + <argument index="1" name="prefix" type="String" default="""" /> <description> </description> </annotation> @@ -359,7 +359,7 @@ </annotation> <annotation name="@export_node_path" qualifiers="vararg"> <return type="void" /> - <argument index="0" name="type" type="String" default="null" /> + <argument index="0" name="type" type="String" default="""" /> <description> </description> </annotation> @@ -368,21 +368,19 @@ <description> </description> </annotation> - <annotation name="@export_range"> + <annotation name="@export_range" qualifiers="vararg"> <return type="void" /> <argument index="0" name="min" type="float" /> <argument index="1" name="max" type="float" /> - <argument index="2" name="step" type="float" default="null" /> - <argument index="3" name="slider1" type="String" default="null" /> - <argument index="4" name="slider2" type="String" default="null" /> - <argument index="5" name="slider3" type="String" default="null" /> + <argument index="2" name="step" type="float" default="1.0" /> + <argument index="3" name="extra_hints" type="String" default="""" /> <description> </description> </annotation> <annotation name="@export_subgroup"> <return type="void" /> <argument index="0" name="name" type="String" /> - <argument index="1" name="prefix" type="String" default="null" /> + <argument index="1" name="prefix" type="String" default="""" /> <description> </description> </annotation> @@ -399,10 +397,10 @@ </annotation> <annotation name="@rpc" qualifiers="vararg"> <return type="void" /> - <argument index="0" name="mode" type="String" default="null" /> - <argument index="1" name="sync" type="String" default="null" /> - <argument index="2" name="transfer_mode" type="String" default="null" /> - <argument index="3" name="transfer_channel" type="int" default="null" /> + <argument index="0" name="mode" type="String" default="""" /> + <argument index="1" name="sync" type="String" default="""" /> + <argument index="2" name="transfer_mode" type="String" default="""" /> + <argument index="3" name="transfer_channel" type="int" default="0" /> <description> </description> </annotation> diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp index b86e9b386d..4372bb33ba 100644 --- a/modules/gdscript/editor/gdscript_highlighter.cpp +++ b/modules/gdscript/editor/gdscript_highlighter.cpp @@ -55,7 +55,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l bool in_function_args = false; bool in_member_variable = false; bool in_node_path = false; + bool in_node_ref = false; bool in_annotation = false; + bool in_string_name = false; bool is_hex_notation = false; bool is_bin_notation = false; bool expect_type = false; @@ -165,6 +167,12 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l if (in_node_path && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) { region_color = node_path_color; } + if (in_node_ref && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) { + region_color = node_ref_color; + } + if (in_string_name && (color_regions[in_region].start_key == "\"" || color_regions[in_region].start_key == "\'")) { + region_color = string_name_color; + } prev_color = region_color; highlighter_info["color"] = region_color; @@ -387,24 +395,42 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l in_member_variable = false; } - if (!in_node_path && in_region == -1 && (str[j] == '$' || str[j] == '%')) { + if (!in_node_path && in_region == -1 && (str[j] == '^')) { in_node_path = true; } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { in_node_path = false; } + if (!in_node_ref && in_region == -1 && (str[j] == '$' || str[j] == '%')) { + in_node_ref = true; + } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) { + in_node_ref = false; + } + if (!in_annotation && in_region == -1 && str[j] == '@') { in_annotation = true; } else if (in_region != -1 || is_a_symbol) { in_annotation = false; } + if (!in_string_name && in_region == -1 && str[j] == '&') { + in_string_name = true; + } else if (in_region != -1 || is_a_symbol) { + in_string_name = false; + } + if (in_node_path) { next_type = NODE_PATH; color = node_path_color; + } else if (in_node_ref) { + next_type = NODE_REF; + color = node_ref_color; } else if (in_annotation) { next_type = ANNOTATION; color = annotation_color; + } else if (in_string_name) { + next_type = STRING_NAME; + color = string_name_color; } else if (in_keyword) { next_type = KEYWORD; color = keyword_color; @@ -592,17 +618,23 @@ void GDScriptSyntaxHighlighter::_update_cache() { if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) { function_definition_color = Color(0.4, 0.9, 1.0); - node_path_color = Color(0.39, 0.76, 0.35); + node_path_color = Color(0.72, 0.77, 0.49); + node_ref_color = Color(0.39, 0.76, 0.35); annotation_color = Color(1.0, 0.7, 0.45); + string_name_color = Color(1.0, 0.66, 0.72); } else { function_definition_color = Color(0.0, 0.65, 0.73); - node_path_color = Color(0.32, 0.55, 0.29); + node_path_color = Color(0.62, 0.67, 0.39); + node_ref_color = Color(0.32, 0.55, 0.29); annotation_color = Color(0.8, 0.5, 0.25); + string_name_color = Color(0.9, 0.56, 0.62); } EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color); + EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_reference_color", node_ref_color); EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color); + EDITOR_DEF("text_editor/theme/highlighting/gdscript/string_name_color", string_name_color); if (text_edit_color_theme == "Default" || godot_2_theme) { EditorSettings::get_singleton()->set_initial_value( "text_editor/theme/highlighting/gdscript/function_definition_color", @@ -613,14 +645,24 @@ void GDScriptSyntaxHighlighter::_update_cache() { node_path_color, true); EditorSettings::get_singleton()->set_initial_value( + "text_editor/theme/highlighting/gdscript/node_reference_color", + node_ref_color, + true); + EditorSettings::get_singleton()->set_initial_value( "text_editor/theme/highlighting/gdscript/annotation_color", annotation_color, true); + EditorSettings::get_singleton()->set_initial_value( + "text_editor/theme/highlighting/gdscript/string_name_color", + string_name_color, + true); } function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color"); node_path_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_path_color"); + node_ref_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_reference_color"); annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color"); + string_name_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/string_name_color"); type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color"); } diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h index 92764e3891..7987582f07 100644 --- a/modules/gdscript/editor/gdscript_highlighter.h +++ b/modules/gdscript/editor/gdscript_highlighter.h @@ -54,7 +54,9 @@ private: NONE, REGION, NODE_PATH, + NODE_REF, ANNOTATION, + STRING_NAME, SYMBOL, NUMBER, FUNCTION, @@ -74,7 +76,9 @@ private: Color number_color; Color member_color; Color node_path_color; + Color node_ref_color; Color annotation_color; + Color string_name_color; Color type_color; void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index bf83353ead..964c1133ff 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -536,6 +536,9 @@ void GDScript::_update_doc() { List<PropertyInfo> props; _get_script_property_list(&props, false); for (int i = 0; i < props.size(); i++) { + if (props[i].usage & PROPERTY_USAGE_CATEGORY || props[i].usage & PROPERTY_USAGE_GROUP || props[i].usage & PROPERTY_USAGE_SUBGROUP) { + continue; + } ScriptMemberInfo scr_member_info; scr_member_info.propinfo = props[i]; scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; @@ -867,7 +870,7 @@ Error GDScript::reload(bool p_keep_state) { } // TODO: Show all error messages. _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); - ERR_FAIL_V(ERR_PARSE_ERROR); + return ERR_PARSE_ERROR; } GDScriptAnalyzer analyzer(&parser); @@ -883,7 +886,7 @@ Error GDScript::reload(bool p_keep_state) { _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); e = e->next(); } - ERR_FAIL_V(ERR_PARSE_ERROR); + return ERR_PARSE_ERROR; } bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool(); @@ -901,7 +904,7 @@ Error GDScript::reload(bool p_keep_state) { GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); - ERR_FAIL_V(ERR_COMPILATION_FAILED); + return ERR_COMPILATION_FAILED; } else { return err; } @@ -1524,7 +1527,6 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const { for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) { MethodInfo mi; mi.name = E.key; - mi.flags |= METHOD_FLAG_FROM_SCRIPT; for (int i = 0; i < E.value->get_argument_count(); i++) { mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i))); } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index af8e4b3746..e36252ada5 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN return false; } - if (codegen.locals.has(p_name)) { + if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) { return false; //shadowed } diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 0a1e1a22fb..90dcfa307e 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2004,8 +2004,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, return false; } - // Check autoloads. - if (ProjectSettings::get_singleton()->has_autoload(p_identifier)) { + // Check global variables (including autoloads). + if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) { r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]); return true; } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 233da87aee..01a672c330 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -117,18 +117,18 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation); // Export annotations. register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>); - register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true); - register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true); + register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, varray(), true); + register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true); register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>); - register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, 1, true); + register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true); register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>); register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>); register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>); - register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "slider1"), PropertyInfo(Variant::STRING, "slider2"), PropertyInfo(Variant::STRING, "slider3")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 4); - register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hint1"), PropertyInfo(Variant::STRING, "hint2")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2); + register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true); + register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true); register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>); - register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true); - register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true); + register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true); + register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true); register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>); register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>); register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>); @@ -137,12 +137,12 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); // Export grouping annotations. register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>); - register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, 1); - register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, 1); + register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray("")); + register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray("")); // Warning annotations. - register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true); + register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true); // Networking. - register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true); + register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, varray("", "", "", 0), true); } GDScriptParser::~GDScriptParser() { @@ -162,6 +162,7 @@ void GDScriptParser::clear() { for_completion = false; errors.clear(); multiline_stack.clear(); + nodes_in_progress.clear(); } void GDScriptParser::push_error(const String &p_message, const Node *p_origin) { @@ -413,6 +414,9 @@ GDScriptTokenizer::Token GDScriptParser::advance() { push_error(current.literal); current = tokenizer.scan(); } + for (Node *n : nodes_in_progress) { + update_extents(n); + } return previous; } @@ -609,6 +613,7 @@ void GDScriptParser::parse_program() { } parse_class_body(true); + complete_extents(head); #ifdef TOOLS_ENABLED for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) { @@ -649,6 +654,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() { if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) { current_class = previous_class; + complete_extents(n_class); return n_class; } @@ -661,6 +667,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() { } parse_class_body(multiline); + complete_extents(n_class); if (multiline) { consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)"); @@ -870,11 +877,13 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable() { } GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) { + VariableNode *variable = alloc_node<VariableNode>(); + if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) { + complete_extents(variable); return nullptr; } - VariableNode *variable = alloc_node<VariableNode>(); variable->identifier = parse_identifier(); variable->export_info.name = variable->identifier->name; @@ -882,10 +891,10 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper if (check(GDScriptTokenizer::Token::NEWLINE)) { if (p_allow_property) { advance(); - return parse_property(variable, true); } else { push_error(R"(Expected type after ":")"); + complete_extents(variable); return nullptr; } } else if (check((GDScriptTokenizer::Token::EQUAL))) { @@ -924,6 +933,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper } } + complete_extents(variable); end_statement("variable declaration"); return variable; @@ -932,6 +942,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) { if (p_need_indent) { if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) { + complete_extents(p_variable); return nullptr; } } @@ -941,6 +952,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var make_completion_context(COMPLETION_PROPERTY_DECLARATION, property); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) { + complete_extents(p_variable); return nullptr; } @@ -997,6 +1009,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var } function = parse_identifier(); } + complete_extents(p_variable); if (p_variable->property == VariableNode::PROP_SETGET) { end_statement("property declaration"); @@ -1011,37 +1024,37 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var void GDScriptParser::parse_property_setter(VariableNode *p_variable) { switch (p_variable->property) { case VariableNode::PROP_INLINE: { - consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)"); - if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) { - p_variable->setter_parameter = parse_identifier(); - } - consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*"); - consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*"); - + FunctionNode *function = alloc_node<FunctionNode>(); IdentifierNode *identifier = alloc_node<IdentifierNode>(); + complete_extents(identifier); identifier->name = "@" + p_variable->identifier->name + "_setter"; - - FunctionNode *function = alloc_node<FunctionNode>(); function->identifier = identifier; - FunctionNode *previous_function = current_function; - current_function = function; + consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)"); ParameterNode *parameter = alloc_node<ParameterNode>(); - parameter->identifier = p_variable->setter_parameter; - - if (parameter->identifier != nullptr) { + if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) { + reset_extents(parameter, previous); + p_variable->setter_parameter = parse_identifier(); + parameter->identifier = p_variable->setter_parameter; function->parameters_indices[parameter->identifier->name] = 0; function->parameters.push_back(parameter); + } + complete_extents(parameter); + consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*"); + consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*"); + + FunctionNode *previous_function = current_function; + current_function = function; + if (p_variable->setter_parameter != nullptr) { SuiteNode *body = alloc_node<SuiteNode>(); body->add_local(parameter, function); - function->body = parse_suite("setter declaration", body); p_variable->setter = function; } - current_function = previous_function; + complete_extents(function); break; } case VariableNode::PROP_SETGET: @@ -1059,12 +1072,13 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) { void GDScriptParser::parse_property_getter(VariableNode *p_variable) { switch (p_variable->property) { case VariableNode::PROP_INLINE: { + FunctionNode *function = alloc_node<FunctionNode>(); + consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)"); IdentifierNode *identifier = alloc_node<IdentifierNode>(); + complete_extents(identifier); identifier->name = "@" + p_variable->identifier->name + "_getter"; - - FunctionNode *function = alloc_node<FunctionNode>(); function->identifier = identifier; FunctionNode *previous_function = current_function; @@ -1072,9 +1086,10 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) { SuiteNode *body = alloc_node<SuiteNode>(); function->body = parse_suite("getter declaration", body); - p_variable->getter = function; + current_function = previous_function; + complete_extents(function); break; } case VariableNode::PROP_SETGET: @@ -1090,11 +1105,12 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) { } GDScriptParser::ConstantNode *GDScriptParser::parse_constant() { + ConstantNode *constant = alloc_node<ConstantNode>(); + if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) { return nullptr; } - ConstantNode *constant = alloc_node<ConstantNode>(); constant->identifier = parse_identifier(); if (match(GDScriptTokenizer::Token::COLON)) { @@ -1113,12 +1129,15 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() { if (constant->initializer == nullptr) { push_error(R"(Expected initializer expression for constant.)"); + complete_extents(constant); return nullptr; } } else { + complete_extents(constant); return nullptr; } + complete_extents(constant); end_statement("constant declaration"); return constant; @@ -1148,15 +1167,18 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() { parameter->default_value = parse_expression(false); } + complete_extents(parameter); return parameter; } GDScriptParser::SignalNode *GDScriptParser::parse_signal() { + SignalNode *signal = alloc_node<SignalNode>(); + if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) { + complete_extents(signal); return nullptr; } - SignalNode *signal = alloc_node<SignalNode>(); signal->identifier = parse_identifier(); if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) { @@ -1188,6 +1210,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() { consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*"); } + complete_extents(signal); end_statement("signal declaration"); return signal; @@ -1299,6 +1322,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() { } #endif // TOOLS_ENABLED + complete_extents(enum_node); end_statement("enum"); return enum_node; @@ -1350,19 +1374,22 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod } GDScriptParser::FunctionNode *GDScriptParser::parse_function() { + FunctionNode *function = alloc_node<FunctionNode>(); + bool _static = false; if (previous.type == GDScriptTokenizer::Token::STATIC) { // TODO: Improve message if user uses "static" with "var" or "const" if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) { + complete_extents(function); return nullptr; } _static = true; } - FunctionNode *function = alloc_node<FunctionNode>(); make_completion_context(COMPLETION_OVERRIDE_METHOD, function); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) { + complete_extents(function); return nullptr; } @@ -1384,6 +1411,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() { function->body = parse_suite("function declaration", body); current_function = previous_function; + complete_extents(function); return function; } @@ -1431,6 +1459,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali } pop_completion_call(); } + complete_extents(annotation); match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. @@ -1449,12 +1478,12 @@ void GDScriptParser::clear_unused_annotations() { annotation_stack.clear(); } -bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments, bool p_is_vararg) { +bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments, bool p_is_vararg) { ERR_FAIL_COND_V_MSG(valid_annotations.has(p_info.name), false, vformat(R"(Annotation "%s" already registered.)", p_info.name)); AnnotationInfo new_annotation; new_annotation.info = p_info; - new_annotation.info.default_arguments.resize(p_optional_arguments); + new_annotation.info.default_arguments = p_default_arguments; if (p_is_vararg) { new_annotation.info.flags |= METHOD_FLAG_VARARG; } @@ -1480,9 +1509,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, if (multiline) { if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) { current_suite = suite->parent_block; + complete_extents(suite); return suite; } } + reset_extents(suite, current); int error_count = 0; @@ -1532,6 +1563,8 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, } while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end()); + complete_extents(suite); + if (multiline) { if (!lambda_ended) { consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context)); @@ -1562,6 +1595,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case GDScriptTokenizer::Token::PASS: advance(); result = alloc_node<PassNode>(); + complete_extents(result); end_statement(R"("pass")"); break; case GDScriptTokenizer::Token::VAR: @@ -1609,6 +1643,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { // If this fails the expression will be nullptr, but that's the same as no return, so it's fine. n_return->return_value = parse_expression(false); } + complete_extents(n_return); result = n_return; current_suite->has_return = true; @@ -1619,6 +1654,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { case GDScriptTokenizer::Token::BREAKPOINT: advance(); result = alloc_node<BreakpointNode>(); + complete_extents(result); end_statement(R"("breakpoint")"); break; case GDScriptTokenizer::Token::ASSERT: @@ -1644,10 +1680,12 @@ GDScriptParser::Node *GDScriptParser::parse_statement() { lambda_ended = true; has_ended_lambda = true; } else { + advance(); push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name())); } + } else { + end_statement("expression"); } - end_statement("expression"); lambda_ended = lambda_ended || has_ended_lambda; result = expression; @@ -1710,6 +1748,7 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() { assert->condition = parse_expression(false); if (assert->condition == nullptr) { push_error("Expected expression to assert."); + complete_extents(assert); return nullptr; } @@ -1718,12 +1757,14 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() { assert->message = parse_expression(false); if (assert->message == nullptr) { push_error(R"(Expected error message for assert after ",".)"); + complete_extents(assert); return nullptr; } } consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*"); + complete_extents(assert); end_statement(R"("assert")"); return assert; @@ -1733,8 +1774,10 @@ GDScriptParser::BreakNode *GDScriptParser::parse_break() { if (!can_break) { push_error(R"(Cannot use "break" outside of a loop.)"); } + BreakNode *break_node = alloc_node<BreakNode>(); + complete_extents(break_node); end_statement(R"("break")"); - return alloc_node<BreakNode>(); + return break_node; } GDScriptParser::ContinueNode *GDScriptParser::parse_continue() { @@ -1742,9 +1785,10 @@ GDScriptParser::ContinueNode *GDScriptParser::parse_continue() { push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)"); } current_suite->has_continue = true; - end_statement(R"("continue")"); ContinueNode *cont = alloc_node<ContinueNode>(); cont->is_for_match = is_continue_match; + complete_extents(cont); + end_statement(R"("continue")"); return cont; } @@ -1786,6 +1830,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { suite->parent_for = n_for; n_for->loop = parse_suite(R"("for" block)", suite); + complete_extents(n_for); // Reset break/continue state. can_break = could_break; @@ -1813,15 +1858,16 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) { } if (match(GDScriptTokenizer::Token::ELIF)) { - IfNode *elif = parse_if("elif"); - SuiteNode *else_block = alloc_node<SuiteNode>(); + IfNode *elif = parse_if("elif"); else_block->statements.push_back(elif); + complete_extents(else_block); n_if->false_block = else_block; } else if (match(GDScriptTokenizer::Token::ELSE)) { consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)"); n_if->false_block = parse_suite(R"("else" block)"); } + complete_extents(n_if); if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) { current_suite->has_return = true; @@ -1845,6 +1891,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)"); if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) { + complete_extents(match); return match; } @@ -1862,7 +1909,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { } #ifdef DEBUG_ENABLED - if (have_wildcard_without_continue) { + if (have_wildcard_without_continue && !branch->patterns.is_empty()) { push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN); } @@ -1878,6 +1925,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { #endif match->branches.push_back(branch); } + complete_extents(match); consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)"); @@ -1892,6 +1940,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() { GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { MatchBranchNode *branch = alloc_node<MatchBranchNode>(); + reset_extents(branch, current); bool has_bind = false; @@ -1919,6 +1968,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { } if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) { + complete_extents(branch); return nullptr; } @@ -1939,6 +1989,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { } branch->block = parse_suite("match pattern block", suite); + complete_extents(branch); // Restore continue state. can_continue = could_continue; @@ -1949,12 +2000,14 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() { GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) { PatternNode *pattern = alloc_node<PatternNode>(); + reset_extents(pattern, current); switch (current.type) { case GDScriptTokenizer::Token::VAR: { // Bind. advance(); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) { + complete_extents(pattern); return nullptr; } pattern->pattern_type = PatternNode::PT_BIND; @@ -1965,12 +2018,14 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ if (p_root_pattern != nullptr) { if (p_root_pattern->has_bind(pattern->bind->name)) { push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name)); + complete_extents(pattern); return nullptr; } } if (current_suite->has_local(pattern->bind->name)) { push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name)); + complete_extents(pattern); return nullptr; } @@ -2023,6 +2078,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)"); } else { PatternNode *sub_pattern = alloc_node<PatternNode>(); + complete_extents(sub_pattern); sub_pattern->pattern_type = PatternNode::PT_REST; pattern->dictionary.push_back({ nullptr, sub_pattern }); pattern->rest_used = true; @@ -2071,6 +2127,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ break; } } + complete_extents(pattern); return pattern; } @@ -2104,6 +2161,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() { is_continue_match = false; n_while->loop = parse_suite(R"("while" block)"); + complete_extents(n_while); // Reset break/continue state. can_break = could_break; @@ -2176,6 +2234,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token."); } IdentifierNode *identifier = alloc_node<IdentifierNode>(); + complete_extents(identifier); identifier->name = previous.get_identifier(); if (current_suite != nullptr && current_suite->has_local(identifier->name)) { @@ -2227,6 +2286,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_ } LiteralNode *literal = alloc_node<LiteralNode>(); + complete_extents(literal); literal->value = previous.literal; return literal; } @@ -2236,6 +2296,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre push_error(R"(Cannot use "self" inside a static function.)"); } SelfNode *self = alloc_node<SelfNode>(); + complete_extents(self); self->current_class = current_class; return self; } @@ -2243,6 +2304,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptTokenizer::Token::Type op_type = previous.type; LiteralNode *constant = alloc_node<LiteralNode>(); + complete_extents(constant); switch (op_type) { case GDScriptTokenizer::Token::CONST_PI: @@ -2303,30 +2365,38 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN } break; default: + complete_extents(operation); return nullptr; // Unreachable. } + complete_extents(operation); return operation; } GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN + UnaryOpNode *operation = alloc_node<UnaryOpNode>(); + reset_extents(operation, p_previous_operand); + update_extents(operation); consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)"); ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign); - UnaryOpNode *operation = alloc_node<UnaryOpNode>(); operation->operation = UnaryOpNode::OP_LOGIC_NOT; operation->variant_op = Variant::OP_NOT; operation->operand = in_operation; + complete_extents(operation); return operation; } GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { GDScriptTokenizer::Token op = previous; BinaryOpNode *operation = alloc_node<BinaryOpNode>(); + reset_extents(operation, p_previous_operand); + update_extents(operation); Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1); operation->left_operand = p_previous_operand; operation->right_operand = parse_precedence(precedence, false); + complete_extents(operation); if (operation->right_operand == nullptr) { push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name())); @@ -2429,8 +2499,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) { // Only one ternary operation exists, so no abstraction here. TernaryOpNode *operation = alloc_node<TernaryOpNode>(); - operation->true_expr = p_previous_operand; + reset_extents(operation, p_previous_operand); + update_extents(operation); + operation->true_expr = p_previous_operand; operation->condition = parse_precedence(PREC_TERNARY, false); if (operation->condition == nullptr) { @@ -2445,6 +2517,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio push_error(R"(Expected expression after "else".)"); } + complete_extents(operation); return operation; } @@ -2497,6 +2570,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode } AssignmentNode *assignment = alloc_node<AssignmentNode>(); + reset_extents(assignment, p_previous_operand); + update_extents(assignment); + make_completion_context(COMPLETION_ASSIGN, assignment); #ifdef DEBUG_ENABLED bool has_operator = true; @@ -2561,6 +2637,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode if (assignment->assigned_value == nullptr) { push_error(R"(Expected an expression after "=".)"); } + complete_extents(assignment); #ifdef DEBUG_ENABLED if (source_variable != nullptr) { @@ -2582,6 +2659,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr push_error(R"(Expected signal or coroutine after "await".)"); } await->to_await = element; + complete_extents(await); if (current_function) { // Might be null in a getter or setter. current_function->is_coroutine = true; @@ -2610,6 +2688,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_pr } pop_multiline(); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)"); + complete_extents(array); return array; } @@ -2701,6 +2780,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode } pop_multiline(); consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)"); + complete_extents(dictionary); return dictionary; } @@ -2718,6 +2798,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) { SubscriptNode *attribute = alloc_node<SubscriptNode>(); + reset_extents(attribute, p_previous_operand); + update_extents(attribute); if (for_completion) { bool is_builtin = false; @@ -2737,17 +2819,21 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * attribute->base = p_previous_operand; if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) { + complete_extents(attribute); return attribute; } attribute->is_attribute = true; attribute->attribute = parse_identifier(); + complete_extents(attribute); return attribute; } GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) { SubscriptNode *subscript = alloc_node<SubscriptNode>(); + reset_extents(subscript, p_previous_operand); + update_extents(subscript); make_completion_context(COMPLETION_SUBSCRIPT, subscript); @@ -2760,15 +2846,19 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode * pop_multiline(); consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)"); + complete_extents(subscript); return subscript; } GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) { CastNode *cast = alloc_node<CastNode>(); + reset_extents(cast, p_previous_operand); + update_extents(cast); cast->operand = p_previous_operand; cast->cast_type = parse_type(); + complete_extents(cast); if (cast->cast_type == nullptr) { push_error(R"(Expected type specifier after "as".)"); @@ -2780,6 +2870,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_pre GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) { CallNode *call = alloc_node<CallNode>(); + reset_extents(call, p_previous_operand); if (previous.type == GDScriptTokenizer::Token::SUPER) { // Super call. @@ -2790,6 +2881,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre if (current_function == nullptr) { push_error(R"(Cannot use implicit "super" call outside of a function.)"); pop_multiline(); + complete_extents(call); return nullptr; } if (current_function->identifier) { @@ -2802,6 +2894,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre make_completion_context(COMPLETION_SUPER_METHOD, call, true); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) { pop_multiline(); + complete_extents(call); return nullptr; } IdentifierNode *identifier = parse_identifier(); @@ -2858,6 +2951,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre pop_multiline(); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*"); + complete_extents(call); return call; } @@ -2901,6 +2995,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p if (previous.type == GDScriptTokenizer::Token::PERCENT) { if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) { push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))"); + complete_extents(get_node); return nullptr; } get_node->full_path += "%"; @@ -2909,6 +3004,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p } else if (previous.type == GDScriptTokenizer::Token::SLASH) { if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) { push_error(R"("/" is only valid at the beginning of the path or after a node name.)"); + complete_extents(get_node); return nullptr; } @@ -2936,6 +3032,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p break; } push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token)); + complete_extents(get_node); return nullptr; } @@ -2949,10 +3046,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p path_state = PATH_STATE_NODE_NAME; } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) { push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name())); + complete_extents(get_node); return nullptr; } } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT)); + complete_extents(get_node); return get_node; } @@ -2976,6 +3075,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_ pop_multiline(); consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*"); + complete_extents(preload); return preload; } @@ -3025,6 +3125,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p in_lambda = true; function->body = parse_suite("lambda declaration", body, true); + complete_extents(function); + complete_extents(lambda); pop_multiline(); @@ -3069,13 +3171,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) { if (!match(GDScriptTokenizer::Token::IDENTIFIER)) { if (match(GDScriptTokenizer::Token::VOID)) { if (p_allow_void) { - TypeNode *void_type = alloc_node<TypeNode>(); + complete_extents(type); + TypeNode *void_type = type; return void_type; } else { push_error(R"("void" is only allowed for a function return type.)"); } } // Leave error message to the caller who knows the context. + complete_extents(type); return nullptr; } @@ -3088,11 +3192,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) { type->container_type = parse_type(false); // Don't allow void for array element type. if (type->container_type == nullptr) { push_error(R"(Expected type for collection after "[".)"); + complete_extents(type); type = nullptr; } else if (type->container_type->container_type != nullptr) { push_error("Nested typed collections are not supported."); } consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)"); + if (type != nullptr) { + complete_extents(type); + } return type; } @@ -3105,6 +3213,7 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) { } } + complete_extents(type); return type; } @@ -3313,7 +3422,16 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String & p_tutorials.append(Pair<String, String>(title, link)); break; case DONE: - return; + break; + } + } + if (current_class->members.size() > 0) { + const ClassNode::Member &m = current_class->members[0]; + int first_member_line = m.get_line(); + if (first_member_line == line) { + p_brief = ""; + p_desc = ""; + p_tutorials.clear(); } } } @@ -3922,6 +4040,46 @@ GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() co return type; } +void GDScriptParser::complete_extents(Node *p_node) { + while (!nodes_in_progress.is_empty() && nodes_in_progress.back()->get() != p_node) { + ERR_PRINT("Parser bug: Mismatch in extents tracking stack."); + nodes_in_progress.pop_back(); + } + if (nodes_in_progress.is_empty()) { + ERR_PRINT("Parser bug: Extents tracking stack is empty."); + } else { + nodes_in_progress.pop_back(); + } +} + +void GDScriptParser::update_extents(Node *p_node) { + p_node->end_line = previous.end_line; + p_node->end_column = previous.end_column; + p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column); + p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column); +} + +void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) { + p_node->start_line = p_token.start_line; + p_node->end_line = p_token.end_line; + p_node->start_column = p_token.start_column; + p_node->end_column = p_token.end_column; + p_node->leftmost_column = p_token.leftmost_column; + p_node->rightmost_column = p_token.rightmost_column; +} + +void GDScriptParser::reset_extents(Node *p_node, Node *p_from) { + if (p_from == nullptr) { + return; + } + p_node->start_line = p_from->start_line; + p_node->end_line = p_from->end_line; + p_node->start_column = p_from->start_column; + p_node->end_column = p_from->end_column; + p_node->leftmost_column = p_from->leftmost_column; + p_node->rightmost_column = p_from->rightmost_column; +} + /*---------- PRETTY PRINT FOR DEBUG ----------*/ #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 8d3295f25b..9c97f98fbc 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1301,6 +1301,12 @@ private: }; static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type); + List<Node *> nodes_in_progress; + void complete_extents(Node *p_node); + void update_extents(Node *p_node); + void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token); + void reset_extents(Node *p_node, Node *p_from); + template <class T> T *alloc_node() { T *node = memnew(T); @@ -1308,13 +1314,8 @@ private: node->next = list; list = node; - // TODO: Properly set positions for all nodes. - node->start_line = previous.start_line; - node->end_line = previous.end_line; - node->start_column = previous.start_column; - node->end_column = previous.end_column; - node->leftmost_column = previous.leftmost_column; - node->rightmost_column = previous.rightmost_column; + reset_extents(node, previous); + nodes_in_progress.push_back(node); return node; } @@ -1359,7 +1360,7 @@ private: SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false); // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); - bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false); + bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); bool validate_annotation_arguments(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 1d56dae982..e0beed367a 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1032,11 +1032,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif #ifdef DEBUG_ENABLED if (!valid) { - if (src->has_method(*index)) { - err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?"; - } else { - err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "')."; - } + err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "')."; OPCODE_BREAK; } *dst = ret; diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index 64891d9ee8..b1c2140039 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -38,7 +38,8 @@ #include <glslang/Public/ShaderLang.h> #include <glslang/SPIRV/GlslangToSpv.h> -static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) { +static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device) { + const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); Vector<uint8_t> ret; ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret); @@ -58,12 +59,12 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; glslang::TShader::ForbidIncluder includer; - if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { - if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) { + if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) { + if (capabilities->version_major == 1 && capabilities->version_minor == 0) { ClientVersion = glslang::EShTargetVulkan_1_0; TargetVersion = glslang::EShTargetSpv_1_0; check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0 - } else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) { + } else if (capabilities->version_major == 1 && capabilities->version_minor == 1) { ClientVersion = glslang::EShTargetVulkan_1_1; TargetVersion = glslang::EShTargetSpv_1_3; } else { @@ -90,34 +91,36 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage if (check_subgroup_support) { uint32_t stage_bit = 1 << p_stage; - if ((p_capabilities->subgroup_in_shaders & stage_bit) == stage_bit) { + uint32_t subgroup_in_shaders = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)); + uint32_t subgroup_operations = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)); + if ((subgroup_in_shaders & stage_bit) == stage_bit) { // stage supports subgroups preamble += "#define has_GL_KHR_shader_subgroup_basic 1\n"; - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_vote 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_arithmetic 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_ballot 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_shuffle 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_shuffle_relative 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_clustered 1\n"; } - if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) { + if (subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) { preamble += "#define has_GL_KHR_shader_subgroup_quad 1\n"; } } } - if (p_capabilities->supports_multiview) { + if (p_render_device->has_feature(RD::SUPPORTS_MULTIVIEW)) { preamble += "#define has_VK_KHR_multiview 1\n"; } @@ -184,9 +187,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage return ret; } -static String _get_cache_key_function_glsl(const RenderingDevice::Capabilities *p_capabilities) { +static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) { + const RD::Capabilities *capabilities = p_render_device->get_device_capabilities(); String version; - version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(p_capabilities->version_major) + ", minor=" + itos(p_capabilities->version_minor) + " , subgroup_size=" + itos(p_capabilities->subgroup_operations) + " , subgroup_ops=" + itos(p_capabilities->subgroup_operations) + " , subgroup_in_shaders=" + itos(p_capabilities->subgroup_in_shaders); + version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)); return version; } diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 676862ea5a..8002c185c7 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -292,7 +292,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) { path = path.plus_file("blender"); #endif -#if defined(OSX_ENABLED) +#if defined(MACOS_ENABLED) if (!FileAccess::exists(path)) { path = path.plus_file("Blender"); } @@ -449,8 +449,8 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() { EditorNode::get_singleton()->get_gui_base()->add_child(configure_blender_dialog); - configure_blender_dialog->get_ok_button()->set_text(TTR("Confirm Path")); - configure_blender_dialog->get_cancel_button()->set_text(TTR("Disable '.blend' Import")); + configure_blender_dialog->set_ok_button_text(TTR("Confirm Path")); + configure_blender_dialog->set_cancel_button_text(TTR("Disable '.blend' Import")); configure_blender_dialog->get_cancel_button()->set_tooltip(TTR("Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings.")); configure_blender_dialog->connect("confirmed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed)); @@ -468,7 +468,7 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() { // Autodetect auto_detected_path = ""; -#if defined(OSX_ENABLED) +#if defined(MACOS_ENABLED) { Vector<String> mdfind_paths; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index babdc9f33b..5f9099cb31 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -230,15 +230,21 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) { } Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const { - const String texture_transform = "KHR_texture_transform"; - const String punctual_lights = "KHR_lights_punctual"; Array extensions_used; - extensions_used.push_back(punctual_lights); - extensions_used.push_back(texture_transform); - state->json["extensionsUsed"] = extensions_used; Array extensions_required; - extensions_required.push_back(texture_transform); - state->json["extensionsRequired"] = extensions_required; + if (!state->lights.is_empty()) { + extensions_used.push_back("KHR_lights_punctual"); + } + if (state->use_khr_texture_transform) { + extensions_used.push_back("KHR_texture_transform"); + extensions_required.push_back("KHR_texture_transform"); + } + if (!extensions_used.is_empty()) { + state->json["extensionsUsed"] = extensions_used; + } + if (!extensions_required.is_empty()) { + state->json["extensionsRequired"] = extensions_required; + } return OK; } @@ -3305,7 +3311,11 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { } if (gltf_texture_index != -1) { bct["index"] = gltf_texture_index; - bct["extensions"] = _serialize_texture_transform_uv1(material); + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + bct["extensions"] = extensions; + state->use_khr_texture_transform = true; + } mr["baseColorTexture"] = bct; } } @@ -3436,7 +3446,11 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { } if (has_roughness || has_metalness) { mrt["index"] = orm_texture_index; - mrt["extensions"] = _serialize_texture_transform_uv1(material); + Dictionary extensions = _serialize_texture_transform_uv1(material); + if (!extensions.is_empty()) { + mrt["extensions"] = extensions; + state->use_khr_texture_transform = true; + } mr["metallicRoughnessTexture"] = mrt; } } @@ -4525,6 +4539,9 @@ void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> state) { } Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) { + if (state->lights.is_empty()) { + return OK; + } Array lights; for (GLTFLightIndex i = 0; i < state->lights.size(); i++) { Dictionary d; @@ -4551,10 +4568,6 @@ Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) { lights.push_back(d); } - if (!state->lights.size()) { - return OK; - } - Dictionary extensions; if (state->json.has("extensions")) { extensions = state->json["extensions"]; @@ -6651,45 +6664,48 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> return OK; } -Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) { - Dictionary extension; - Ref<BaseMaterial3D> mat = p_material; - if (mat.is_valid()) { - Dictionary texture_transform; +Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) { + Dictionary texture_transform; + bool is_offset = p_offset != Vector2(0.0, 0.0); + if (is_offset) { Array offset; offset.resize(2); - offset[0] = mat->get_uv2_offset().x; - offset[1] = mat->get_uv2_offset().y; + offset[0] = p_offset.x; + offset[1] = p_offset.y; texture_transform["offset"] = offset; + } + bool is_scaled = p_scale != Vector2(1.0, 1.0); + if (is_scaled) { Array scale; scale.resize(2); - scale[0] = mat->get_uv2_scale().x; - scale[1] = mat->get_uv2_scale().y; + scale[0] = p_scale.x; + scale[1] = p_scale.y; texture_transform["scale"] = scale; - // Godot doesn't support texture rotation + } + Dictionary extension; + // Note: Godot doesn't support texture rotation. + if (is_offset || is_scaled) { extension["KHR_texture_transform"] = texture_transform; } return extension; } Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) { - Dictionary extension; if (p_material.is_valid()) { - Dictionary texture_transform; - Array offset; - offset.resize(2); - offset[0] = p_material->get_uv1_offset().x; - offset[1] = p_material->get_uv1_offset().y; - texture_transform["offset"] = offset; - Array scale; - scale.resize(2); - scale[0] = p_material->get_uv1_scale().x; - scale[1] = p_material->get_uv1_scale().y; - texture_transform["scale"] = scale; - // Godot doesn't support texture rotation - extension["KHR_texture_transform"] = texture_transform; + Vector3 offset = p_material->get_uv1_offset(); + Vector3 scale = p_material->get_uv1_scale(); + return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); } - return extension; + return Dictionary(); +} + +Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) { + if (p_material.is_valid()) { + Vector3 offset = p_material->get_uv2_offset(); + Vector3 scale = p_material->get_uv2_scale(); + return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); + } + return Dictionary(); } Error GLTFDocument::_serialize_version(Ref<GLTFState> state) { diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index 2fdef19038..7a06c93d59 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -60,6 +60,7 @@ class GLTFState : public Resource { Vector<uint8_t> glb_data; bool use_named_skin_binds = false; + bool use_khr_texture_transform = false; bool discard_meshes_and_materials = false; Vector<Ref<GLTFNode>> nodes; diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index b694c109e1..f975d95079 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1262,7 +1262,7 @@ GridMapEditor::GridMapEditor() { info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); + info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); mesh_library_palette->add_child(info_message); edit_axis = Vector3::AXIS_Y; diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 2ce3715bea..83ac478a97 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -271,8 +271,8 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ mi.offset.x = best_atlas_offsets[m_i].x; mi.offset.y = best_atlas_offsets[m_i].y; mi.slice = best_atlas_offsets[m_i].z; - albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2(Vector2(), Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height())), mi.offset); - emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2(), Size2i(mi.data.emission_on_uv2->get_width(), mi.data.emission_on_uv2->get_height())), mi.offset); + albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2i(Vector2i(), mi.data.albedo_on_uv2->get_size()), mi.offset); + emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2i(), mi.data.emission_on_uv2->get_size()), mi.offset); } return BAKE_OK; @@ -1420,7 +1420,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d img2.instantiate(); img2->create(2, 2, false, Image::FORMAT_RGBAF, s); img2->convert(Image::FORMAT_RGB8); - img->blit_rect(img2, Rect2(0, 0, 2, 2), Point2((j % 3) * 2, (j / 3) * 2)); + img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2)); } img->save_png("res://3_light_probe_" + itos(i) + ".png"); } diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp index c37bea519f..98bcdea8f4 100644 --- a/modules/minimp3/audio_stream_mp3.cpp +++ b/modules/minimp3/audio_stream_mp3.cpp @@ -46,6 +46,12 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { int frames_mixed_this_step = p_frames; + int beat_length_frames = -1; + bool beat_loop = mp3_stream->has_loop() && mp3_stream->get_bpm() > 0 && mp3_stream->get_beat_count() > 0; + if (beat_loop) { + beat_length_frames = mp3_stream->get_beat_count() * mp3_stream->sample_rate * 60 / mp3_stream->get_bpm(); + } + while (todo && active) { mp3dec_frame_info_t frame_info; mp3d_sample_t *buf_frame = nullptr; @@ -54,8 +60,25 @@ int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) { if (samples_mixed) { p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]); + if (loop_fade_remaining < FADE_SIZE) { + p_buffer[p_frames - todo] += loop_fade[loop_fade_remaining] * (float(FADE_SIZE - loop_fade_remaining) / float(FADE_SIZE)); + loop_fade_remaining++; + } --todo; ++frames_mixed; + + if (beat_loop && (int)frames_mixed >= beat_length_frames) { + for (int i = 0; i < FADE_SIZE; i++) { + samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels); + loop_fade[i] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]); + if (!samples_mixed) { + break; + } + } + loop_fade_remaining = 0; + seek(mp3_stream->loop_offset); + loops++; + } } else { @@ -117,6 +140,10 @@ void AudioStreamPlaybackMP3::seek(float p_time) { mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels); } +void AudioStreamPlaybackMP3::tag_used_streams() { + mp3_stream->tag_used(get_playback_position()); +} + AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() { if (mp3d) { mp3dec_ex_close(mp3d); @@ -124,7 +151,7 @@ AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() { } } -Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() { +Ref<AudioStreamPlayback> AudioStreamMP3::instantiate_playback() { Ref<AudioStreamPlaybackMP3> mp3s; ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s, @@ -206,6 +233,36 @@ bool AudioStreamMP3::is_monophonic() const { return false; } +void AudioStreamMP3::set_bpm(double p_bpm) { + ERR_FAIL_COND(p_bpm < 0); + bpm = p_bpm; + emit_changed(); +} + +double AudioStreamMP3::get_bpm() const { + return bpm; +} + +void AudioStreamMP3::set_beat_count(int p_beat_count) { + ERR_FAIL_COND(p_beat_count < 0); + beat_count = p_beat_count; + emit_changed(); +} + +int AudioStreamMP3::get_beat_count() const { + return beat_count; +} + +void AudioStreamMP3::set_bar_beats(int p_bar_beats) { + ERR_FAIL_COND(p_bar_beats < 0); + bar_beats = p_bar_beats; + emit_changed(); +} + +int AudioStreamMP3::get_bar_beats() const { + return bar_beats; +} + void AudioStreamMP3::_bind_methods() { ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data); ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data); @@ -216,7 +273,19 @@ void AudioStreamMP3::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset); ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset); + ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamMP3::set_bpm); + ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamMP3::get_bpm); + + ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamMP3::set_beat_count); + ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamMP3::get_beat_count); + + ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamMP3::set_bar_beats); + ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamMP3::get_bar_beats); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset"); } diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h index c1a60ddccb..428ac1240e 100644 --- a/modules/minimp3/audio_stream_mp3.h +++ b/modules/minimp3/audio_stream_mp3.h @@ -41,6 +41,12 @@ class AudioStreamMP3; class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled { GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled); + enum { + FADE_SIZE = 256 + }; + AudioFrame loop_fade[FADE_SIZE]; + int loop_fade_remaining = FADE_SIZE; + mp3dec_ex_t *mp3d = nullptr; uint32_t frames_mixed = 0; bool active = false; @@ -64,6 +70,8 @@ public: virtual float get_playback_position() const override; virtual void seek(float p_time) override; + virtual void tag_used_streams() override; + AudioStreamPlaybackMP3() {} ~AudioStreamPlaybackMP3(); }; @@ -85,17 +93,30 @@ class AudioStreamMP3 : public AudioStream { float loop_offset = 0.0; void clear_data(); + double bpm = 0; + int beat_count = 0; + int bar_beats = 4; + protected: static void _bind_methods(); public: void set_loop(bool p_enable); - bool has_loop() const; + virtual bool has_loop() const override; void set_loop_offset(float p_seconds); float get_loop_offset() const; - virtual Ref<AudioStreamPlayback> instance_playback() override; + void set_bpm(double p_bpm); + virtual double get_bpm() const override; + + void set_beat_count(int p_beat_count); + virtual int get_beat_count() const override; + + void set_bar_beats(int p_bar_beats); + virtual int get_bar_beats() const override; + + virtual Ref<AudioStreamPlayback> instantiate_playback() override; virtual String get_stream_name() const override; void set_data(const Vector<uint8_t> &p_data); diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml index f5f7d3ef17..8f03681c06 100644 --- a/modules/minimp3/doc_classes/AudioStreamMP3.xml +++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml @@ -4,13 +4,42 @@ MP3 audio stream driver. </brief_description> <description> - MP3 audio stream driver. + MP3 audio stream driver. See [member data] if you want to load an MP3 file at run-time. </description> <tutorials> </tutorials> <members> + <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4"> + </member> + <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0"> + </member> + <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0"> + </member> <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()"> Contains the audio data in bytes. + You can load a file without having to import it beforehand using the code snippet below. Keep in mind that this snippet loads the whole file into memory and may not be ideal for huge files (hundreds of megabytes or more). + [codeblocks] + [gdscript] + func load_mp3(path): + var file = File.new() + file.open(path, File.READ) + var sound = AudioStreamMP3.new() + sound.data = file.get_buffer(file.get_length()) + file.close() + return sound + [/gdscript] + [csharp] + public AudioStreamMP3 LoadMP3(string path) + { + var file = new File(); + file.Open(path, File.READ); + var sound = new AudioStreamMP3(); + sound.Data = file.GetBuffer(file.GetLength()); + file.Close(); + return sound; + } + [/csharp] + [/codeblocks] </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> If [code]true[/code], the stream will automatically loop when it reaches the end. diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp index e03940f963..8526aeef38 100644 --- a/modules/minimp3/resource_importer_mp3.cpp +++ b/modules/minimp3/resource_importer_mp3.cpp @@ -34,6 +34,10 @@ #include "core/io/resource_saver.h" #include "scene/resources/texture.h" +#ifdef TOOLS_ENABLED +#include "editor/import/audio_stream_import_settings.h" +#endif + String ResourceImporterMP3::get_importer_name() const { return "mp3"; } @@ -69,14 +73,26 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const { void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4)); } -Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - bool loop = p_options["loop"]; - float loop_offset = p_options["loop_offset"]; +#ifdef TOOLS_ENABLED +bool ResourceImporterMP3::has_advanced_options() const { + return true; +} +void ResourceImporterMP3::show_advanced_options(const String &p_path) { + Ref<AudioStreamMP3> mp3_stream = import_mp3(p_path); + if (mp3_stream.is_valid()) { + AudioStreamImportSettings::get_singleton()->edit(p_path, "mp3", mp3_stream); + } +} +#endif - Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); - ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN); +Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V(f.is_null(), Ref<AudioStreamMP3>()); uint64_t len = f->get_length(); @@ -90,9 +106,27 @@ Error ResourceImporterMP3::import(const String &p_source_file, const String &p_s mp3_stream.instantiate(); mp3_stream->set_data(data); - ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(!mp3_stream->get_data().size(), Ref<AudioStreamMP3>()); + + return mp3_stream; +} + +Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + bool loop = p_options["loop"]; + float loop_offset = p_options["loop_offset"]; + double bpm = p_options["bpm"]; + float beat_count = p_options["beat_count"]; + float bar_beats = p_options["bar_beats"]; + + Ref<AudioStreamMP3> mp3_stream = import_mp3(p_source_file); + if (mp3_stream.is_null()) { + return ERR_CANT_OPEN; + } mp3_stream->set_loop(loop); mp3_stream->set_loop_offset(loop_offset); + mp3_stream->set_bpm(bpm); + mp3_stream->set_beat_count(beat_count); + mp3_stream->set_bar_beats(bar_beats); return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream); } diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h index 678a3773bb..38729d68f8 100644 --- a/modules/minimp3/resource_importer_mp3.h +++ b/modules/minimp3/resource_importer_mp3.h @@ -50,6 +50,12 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; +#ifdef TOOLS_ENABLED + virtual bool has_advanced_options() const override; + virtual void show_advanced_options(const String &p_path) override; +#endif + static Ref<AudioStreamMP3> import_mp3(const String &p_path); + virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; ResourceImporterMP3(); diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 5876b6cbf3..95f1a657a4 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -45,7 +45,7 @@ uint32_t MobileVRInterface::get_capabilities() const { Vector3 MobileVRInterface::scale_magneto(const Vector3 &p_magnetometer) { // Our magnetometer doesn't give us nice clean data. - // Well it may on Mac OS X because we're getting a calibrated value in the current implementation but Android we're getting raw data. + // Well it may on macOS because we're getting a calibrated value in the current implementation but Android we're getting raw data. // This is a fairly simple adjustment we can do to correct for the magnetometer data being elliptical Vector3 mag_raw = p_magnetometer; diff --git a/modules/mono/SCsub b/modules/mono/SCsub index 3bafa351a9..d10ebc7b47 100644 --- a/modules/mono/SCsub +++ b/modules/mono/SCsub @@ -55,7 +55,7 @@ env_mono.add_source_files(env.modules_sources, "utils/*.cpp") env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp") -if env["platform"] in ["osx", "iphone"]: +if env["platform"] in ["macos", "ios"]: env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm") env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m") elif env["platform"] == "android": diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 8e441e7e07..e69904c54b 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -63,15 +63,15 @@ def copy_file(src_dir, dst_dir, src_name, dst_name=""): def is_desktop(platform): - return platform in ["windows", "osx", "linuxbsd", "server", "uwp", "haiku"] + return platform in ["windows", "macos", "linuxbsd", "server", "uwp", "haiku"] def is_unix_like(platform): - return platform in ["osx", "linuxbsd", "server", "android", "haiku", "iphone"] + return platform in ["macos", "linuxbsd", "server", "android", "haiku", "ios"] def module_supports_tools_on(platform): - return platform not in ["android", "javascript", "iphone"] + return platform not in ["android", "javascript", "ios"] def find_wasm_src_dir(mono_root): @@ -89,7 +89,7 @@ def configure(env, env_mono): bits = env["bits"] is_android = env["platform"] == "android" is_javascript = env["platform"] == "javascript" - is_ios = env["platform"] == "iphone" + is_ios = env["platform"] == "ios" is_ios_sim = is_ios and env["arch"] in ["x86", "x86_64"] tools_enabled = env["tools"] @@ -206,7 +206,7 @@ def configure(env, env_mono): copy_file(mono_bin_path, "#bin", mono_dll_file) else: - is_apple = env["platform"] in ["osx", "iphone"] + is_apple = env["platform"] in ["macos", "ios"] is_macos = is_apple and not is_ios sharedlib_ext = ".dylib" if is_apple else ".so" @@ -221,7 +221,7 @@ def configure(env, env_mono): ) if not mono_root and is_macos: - # Try with some known directories under OSX + # Try with some known directories under macOS hint_dirs = ["/Library/Frameworks/Mono.framework/Versions/Current", "/usr/local/var/homebrew/linked/mono"] for hint_dir in hint_dirs: if os.path.isdir(hint_dir): @@ -270,7 +270,7 @@ def configure(env, env_mono): def copy_mono_lib(libname_wo_ext): copy_file( - mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.iphone.%s.a" % (libname_wo_ext, arch) + mono_lib_path, "#bin", libname_wo_ext + ".a", "%s.ios.%s.a" % (libname_wo_ext, arch) ) # Copy Mono libraries to the output folder. These are meant to be bundled with @@ -539,7 +539,7 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir): os.makedirs(target_mono_lib_dir) lib_file_names = [] - if platform == "osx": + if platform == "macos": lib_file_names = [ lib_name + ".dylib" for lib_name in ["libmono-btls-shared", "libmono-native-compat", "libMonoPosixHelper"] diff --git a/modules/mono/config.py b/modules/mono/config.py index df02d9a309..3e6584590c 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -1,4 +1,4 @@ -supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku", "javascript", "iphone"] +supported_platforms = ["windows", "macos", "linuxbsd", "server", "android", "haiku", "javascript", "ios"] def can_build(env, platform): @@ -15,7 +15,7 @@ def configure(env): from SCons.Script import BoolVariable, PathVariable, Variables, Help - default_mono_static = platform in ["iphone", "javascript"] + default_mono_static = platform in ["ios", "javascript"] default_mono_bundles_zlib = platform in ["javascript"] envvars = Variables() diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props index 0128f5c706..5a499742e9 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props @@ -57,7 +57,7 @@ <PropertyGroup Condition=" '$(GodotTargetPlatform)' == '' "> <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Linux))' ">linuxbsd</GodotTargetPlatform> <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(FreeBSD))' ">linuxbsd</GodotTargetPlatform> - <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">osx</GodotTargetPlatform> + <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(OSX))' ">macos</GodotTargetPlatform> <GodotTargetPlatform Condition=" '$([MSBuild]::IsOsPlatform(Windows))' ">windows</GodotTargetPlatform> </PropertyGroup> @@ -76,12 +76,12 @@ --> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'windows' ">GODOT_WINDOWS;GODOT_PC</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'linuxbsd' ">GODOT_LINUXBSD;GODOT_PC</GodotPlatformConstants> - <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'osx' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants> + <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'macos' ">GODOT_OSX;GODOT_MACOS;GODOT_PC</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'server' ">GODOT_SERVER;GODOT_PC</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'uwp' ">GODOT_UWP;GODOT_PC</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'haiku' ">GODOT_HAIKU;GODOT_PC</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'android' ">GODOT_ANDROID;GODOT_MOBILE</GodotPlatformConstants> - <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'iphone' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants> + <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'javascript' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants> <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants> diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs index e2f4d2f5fd..e9718cc82c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs @@ -336,10 +336,10 @@ MONO_AOT_MODE_LAST = 1000, // Add the required Mono libraries to the Xcode project - string MonoLibFile(string libFileName) => libFileName + ".iphone.fat.a"; + string MonoLibFile(string libFileName) => libFileName + ".ios.fat.a"; string MonoLibFromTemplate(string libFileName) => - Path.Combine(Internal.FullTemplatesDir, "iphone-mono-libs", MonoLibFile(libFileName)); + Path.Combine(Internal.FullExportTemplatesDir, "ios-mono-libs", MonoLibFile(libFileName)); exporter.AddIosProjectStaticLib(MonoLibFromTemplate("libmonosgen-2.0")); diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 3e46a89b7c..cca18a2a1f 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -337,7 +337,7 @@ namespace GodotTools.Export string TemplateDirName() => $"data.mono.{platform}.{bits}.{target}"; - string templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName()); + string templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName()); bool validTemplatePathFound = true; if (!Directory.Exists(templateDirPath)) @@ -347,7 +347,7 @@ namespace GodotTools.Export if (isDebug) { target = "debug"; // Support both 'release_debug' and 'debug' for the template data directory name - templateDirPath = Path.Combine(Internal.FullTemplatesDir, TemplateDirName()); + templateDirPath = Path.Combine(Internal.FullExportTemplatesDir, TemplateDirName()); validTemplatePathFound = true; if (!Directory.Exists(templateDirPath)) @@ -380,7 +380,7 @@ namespace GodotTools.Export private static bool PlatformHasTemplateDir(string platform) { - // OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest. + // macOS export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest. return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform); } @@ -398,13 +398,13 @@ namespace GodotTools.Export private static string GetBclProfileDir(string profile) { - string templatesDir = Internal.FullTemplatesDir; + string templatesDir = Internal.FullExportTemplatesDir; return Path.Combine(templatesDir, "bcl", profile); } private static string DeterminePlatformBclDir(string platform) { - string templatesDir = Internal.FullTemplatesDir; + string templatesDir = Internal.FullExportTemplatesDir; string platformBclDir = Path.Combine(templatesDir, "bcl", platform); if (!File.Exists(Path.Combine(platformBclDir, "mscorlib.dll"))) diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 69960bdbeb..b39c3d1c0d 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -263,16 +263,16 @@ namespace GodotTools var args = new List<string>(); - bool osxAppBundleInstalled = false; + bool macOSAppBundleInstalled = false; if (OS.IsMacOS) { // The package path is '/Applications/Visual Studio Code.app' const string vscodeBundleId = "com.microsoft.VSCode"; - osxAppBundleInstalled = Internal.IsOsxAppBundleInstalled(vscodeBundleId); + macOSAppBundleInstalled = Internal.IsMacOSAppBundleInstalled(vscodeBundleId); - if (osxAppBundleInstalled) + if (macOSAppBundleInstalled) { args.Add("-b"); args.Add(vscodeBundleId); @@ -307,13 +307,13 @@ namespace GodotTools if (OS.IsMacOS) { - if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath)) + if (!macOSAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath)) { GD.PushError("Cannot find code editor: VSCode"); return Error.FileNotFound; } - command = osxAppBundleInstalled ? "/usr/bin/open" : _vsCodePath; + command = macOSAppBundleInstalled ? "/usr/bin/open" : _vsCodePath; } else { diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs index 3f1d5ac3ca..7a0983a8cb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs @@ -30,7 +30,7 @@ namespace GodotTools.Ides.MonoDevelop { string bundleId = BundleIds[_editorId]; - if (Internal.IsOsxAppBundleInstalled(bundleId)) + if (Internal.IsMacOSAppBundleInstalled(bundleId)) { command = "open"; diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 77370090ec..12c90178c9 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -12,12 +12,12 @@ namespace GodotTools.Internals public static string UpdateApiAssembliesFromPrebuilt(string config) => internal_UpdateApiAssembliesFromPrebuilt(config); - public static string FullTemplatesDir => - internal_FullTemplatesDir(); + public static string FullExportTemplatesDir => + internal_FullExportTemplatesDir(); public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path); - public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId); + public static bool IsMacOSAppBundleInstalled(string bundleId) => internal_IsMacOSAppBundleInstalled(bundleId); public static bool GodotIs32Bits() => internal_GodotIs32Bits(); @@ -57,13 +57,13 @@ namespace GodotTools.Internals private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern string internal_FullTemplatesDir(); + private static extern string internal_FullExportTemplatesDir(); [MethodImpl(MethodImplOptions.InternalCall)] private static extern string internal_SimplifyGodotPath(this string path); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern bool internal_IsOsxAppBundleInstalled(string bundleId); + private static extern bool internal_IsMacOSAppBundleInstalled(string bundleId); [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool internal_GodotIs32Bits(); diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs index 2db549c623..5cef6e5c3c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs @@ -37,13 +37,13 @@ namespace GodotTools.Utils public static class Platforms { public const string Windows = "windows"; - public const string MacOS = "osx"; + public const string MacOS = "macos"; public const string LinuxBSD = "linuxbsd"; public const string Server = "server"; public const string UWP = "uwp"; public const string Haiku = "haiku"; public const string Android = "android"; - public const string iOS = "iphone"; + public const string iOS = "ios"; public const string HTML5 = "javascript"; } diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index f7f710f3f1..5e4fe90553 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -47,7 +47,7 @@ #include "../glue/cs_glue_version.gen.h" #include "../godotsharp_dirs.h" #include "../mono_gd/gd_mono_marshal.h" -#include "../utils/osx_utils.h" +#include "../utils/macos_utils.h" #include "code_completion.h" #include "godotsharp_export.h" @@ -188,8 +188,8 @@ MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_c return GDMonoMarshal::mono_string_from_godot(error_str); } -MonoString *godot_icall_Internal_FullTemplatesDir() { - String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG); +MonoString *godot_icall_Internal_FullExportTemplatesDir() { + String full_templates_dir = EditorSettings::get_singleton()->get_export_templates_dir().plus_file(VERSION_FULL_CONFIG); return GDMonoMarshal::mono_string_from_godot(full_templates_dir); } @@ -198,10 +198,10 @@ MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) { return GDMonoMarshal::mono_string_from_godot(path.simplify_path()); } -MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) { -#ifdef OSX_ENABLED +MonoBoolean godot_icall_Internal_IsMacOSAppBundleInstalled(MonoString *p_bundle_id) { +#ifdef MACOS_ENABLED String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id); - return (MonoBoolean)osx_is_app_bundle_installed(bundle_id); + return (MonoBoolean)macos_is_app_bundle_installed(bundle_id); #else (void)p_bundle_id; // UNUSED return (MonoBoolean) false; @@ -364,9 +364,9 @@ void register_editor_internal_calls() { // Internals GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullExportTemplatesDir", godot_icall_Internal_FullExportTemplatesDir); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath); - GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled); + GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsMacOSAppBundleInstalled", godot_icall_Internal_IsMacOSAppBundleInstalled); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble); GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 63af1c5892..fd97a71e47 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -118,15 +118,15 @@ namespace Godot /// <summary> /// Returns <see langword="true"/> if point is inside the plane. - /// Comparison uses a custom minimum epsilon threshold. + /// Comparison uses a custom minimum tolerance threshold. /// </summary> /// <param name="point">The point to check.</param> - /// <param name="epsilon">The tolerance threshold.</param> + /// <param name="tolerance">The tolerance threshold.</param> /// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns> - public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon) + public bool HasPoint(Vector3 point, real_t tolerance = Mathf.Epsilon) { real_t dist = _normal.Dot(point) - D; - return Mathf.Abs(dist) <= epsilon; + return Mathf.Abs(dist) <= tolerance; } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs index 412a885daa..b61954a84c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs @@ -461,7 +461,7 @@ namespace Godot } /// <summary> - /// Multiplies each component of the <see cref="Vector2i"/> + /// Divides each component of the <see cref="Vector2i"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The dividend vector.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs index abfd2ae720..0d4894f206 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs @@ -449,7 +449,7 @@ namespace Godot } /// <summary> - /// Multiplies each component of the <see cref="Vector3i"/> + /// Divides each component of the <see cref="Vector3i"/> /// by the given <see langword="int"/>. /// </summary> /// <param name="vec">The dividend vector.</param> diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index cb2b60fcce..f17b24e399 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -184,7 +184,7 @@ private: data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); #endif -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED if (!DirAccess::exists(data_editor_tools_dir)) { data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools"); } @@ -222,7 +222,7 @@ private: data_mono_bin_dir = data_mono_root_dir.plus_file("bin"); #endif -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED if (!DirAccess::exists(data_mono_root_dir)) { data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc"); data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib"); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 39a8ef22b7..d3d3bb2bef 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -55,7 +55,7 @@ #ifdef ANDROID_ENABLED #include "android_mono_config.h" #include "support/android_support.h" -#elif defined(IPHONE_ENABLED) +#elif defined(IOS_ENABLED) #include "support/ios_support.h" #endif @@ -188,7 +188,7 @@ MonoDomain *gd_initialize_mono_runtime() { MonoDomain *gd_initialize_mono_runtime() { gd_mono_debug_init(); -#if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED) +#if defined(IOS_ENABLED) || defined(ANDROID_ENABLED) // I don't know whether this actually matters or not const char *runtime_version = "mobile"; #else @@ -263,7 +263,7 @@ void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_di if (mono_reg_info.config_dir.length() && DirAccess::exists(mono_reg_info.config_dir)) { r_config_dir = mono_reg_info.config_dir; } -#elif defined(OSX_ENABLED) +#elif defined(MACOS_ENABLED) const char *c_assembly_rootdir = mono_assembly_getrootdir(); const char *c_config_dir = mono_get_config_dir(); @@ -343,7 +343,7 @@ void GDMono::initialize() { #if defined(ANDROID_ENABLED) gdmono::android::support::initialize(); -#elif defined(IPHONE_ENABLED) +#elif defined(IOS_ENABLED) gdmono::ios::support::initialize(); #endif diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h index 9fc35f8e31..93ba6a410e 100644 --- a/modules/mono/mono_gd/gd_mono_log.h +++ b/modules/mono/mono_gd/gd_mono_log.h @@ -35,7 +35,7 @@ #include "core/typedefs.h" -#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED) +#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED) // We have custom mono log callbacks for WASM and iOS #define GD_MONO_LOG_ENABLED #endif diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h index bb163b89bc..0180dee3ea 100644 --- a/modules/mono/mono_gd/gd_mono_method_thunk.h +++ b/modules/mono/mono_gd/gd_mono_method_thunk.h @@ -39,7 +39,7 @@ #include "gd_mono_method.h" #include "gd_mono_utils.h" -#if !defined(JAVASCRIPT_ENABLED) && !defined(IPHONE_ENABLED) +#if !defined(JAVASCRIPT_ENABLED) && !defined(IOS_ENABLED) #define HAVE_METHOD_THUNKS #endif diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h index 2f444d5089..03e86df698 100644 --- a/modules/mono/mono_gd/support/ios_support.h +++ b/modules/mono/mono_gd/support/ios_support.h @@ -31,7 +31,7 @@ #ifndef IOS_SUPPORT_H #define IOS_SUPPORT_H -#if defined(IPHONE_ENABLED) +#if defined(IOS_ENABLED) #include "core/string/ustring.h" @@ -45,6 +45,6 @@ void cleanup(); } // namespace ios } // namespace gdmono -#endif // IPHONE_ENABLED +#endif // IOS_ENABLED #endif // IOS_SUPPORT_H diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm index df97dfba49..7c941b9d1e 100644 --- a/modules/mono/mono_gd/support/ios_support.mm +++ b/modules/mono/mono_gd/support/ios_support.mm @@ -30,7 +30,7 @@ #include "ios_support.h" -#if defined(IPHONE_ENABLED) +#if defined(IOS_ENABLED) #import <Foundation/Foundation.h> #include <os/log.h> @@ -147,4 +147,4 @@ GD_PINVOKE_EXPORT void xamarin_start_wwan(const char *p_uri) { os_log_error(OS_LOG_DEFAULT, "Not implemented: 'xamarin_start_wwan'"); } -#endif // IPHONE_ENABLED +#endif // IOS_ENABLED diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/macos_utils.cpp index abb59420eb..cd4f7a827e 100644 --- a/modules/mono/utils/osx_utils.cpp +++ b/modules/mono/utils/macos_utils.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* osx_utils.cpp */ +/* macos_utils.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "osx_utils.h" +#include "macos_utils.h" -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED #include "core/string/print_string.h" #import <CoreFoundation/CoreFoundation.h> #import <CoreServices/CoreServices.h> -bool osx_is_app_bundle_installed(const String &p_bundle_id) { +bool macos_is_app_bundle_installed(const String &p_bundle_id) { CFStringRef bundle_id = CFStringCreateWithCString(nullptr, p_bundle_id.utf8(), kCFStringEncodingUTF8); CFArrayRef result = LSCopyApplicationURLsForBundleIdentifier(bundle_id, nullptr); CFRelease(bundle_id); diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/macos_utils.h index 2f6c6dad51..7892cb2785 100644 --- a/modules/mono/utils/osx_utils.h +++ b/modules/mono/utils/macos_utils.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* osx_utils.h */ +/* macos_utils.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,13 +30,13 @@ #include "core/string/ustring.h" -#ifndef OSX_UTILS_H -#define OSX_UTILS_H +#ifndef MACOS_UTILS_H +#define MACOS_UTILS_H -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED -bool osx_is_app_bundle_installed(const String &p_bundle_id); +bool macos_is_app_bundle_installed(const String &p_bundle_id); #endif -#endif // OSX_UTILS_H +#endif // MACOS_UTILS_H diff --git a/modules/noise/editor/noise_editor_plugin.cpp b/modules/noise/editor/noise_editor_plugin.cpp index 32c3f0aad4..27a86f45b5 100644 --- a/modules/noise/editor/noise_editor_plugin.cpp +++ b/modules/noise/editor/noise_editor_plugin.cpp @@ -54,7 +54,7 @@ public: set_custom_minimum_size(Size2(0, EDSCALE * PREVIEW_HEIGHT)); _texture_rect = memnew(TextureRect); - _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); _texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_COVERED); add_child(_texture_rect); diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp index da52ecfdd5..e18fa9e590 100644 --- a/modules/ogg/ogg_packet_sequence.cpp +++ b/modules/ogg/ogg_packet_sequence.cpp @@ -106,7 +106,7 @@ float OGGPacketSequence::get_length() const { return granule_pos / sampling_rate; } -Ref<OGGPacketSequencePlayback> OGGPacketSequence::instance_playback() { +Ref<OGGPacketSequencePlayback> OGGPacketSequence::instantiate_playback() { Ref<OGGPacketSequencePlayback> playback; playback.instantiate(); playback->ogg_packet_sequence = Ref<OGGPacketSequence>(this); diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h index 73e3cb4fff..76b673c6ac 100644 --- a/modules/ogg/ogg_packet_sequence.h +++ b/modules/ogg/ogg_packet_sequence.h @@ -86,7 +86,7 @@ public: // Returns the granule position of the last page in this sequence. int64_t get_final_granule_pos() const; - Ref<OGGPacketSequencePlayback> instance_playback(); + Ref<OGGPacketSequencePlayback> instantiate_playback(); OGGPacketSequence() {} virtual ~OGGPacketSequence() {} diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 8783e061d2..593d1ff3c1 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -35,7 +35,11 @@ if env["platform"] == "android": # may need to include java parts of the openxr loader elif env["platform"] == "linuxbsd": - env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX", "XR_USE_PLATFORM_XLIB"]) + env_thirdparty.AppendUnique(CPPDEFINES=["XR_OS_LINUX"]) + + if env["x11"]: + env_thirdparty.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"]) + # FIXME: Review what needs to be set for Android and macOS. env_thirdparty.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"]) elif env["platform"] == "windows": diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 74f708bc95..25bf496de9 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -5,10 +5,10 @@ </brief_description> <description> The OpenXR interface allows Godot to interact with OpenXR runtimes and make it possible to create XR experiences and games. - Due to the needs of OpenXR this interface works slightly different then other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset. + Due to the needs of OpenXR this interface works slightly different than other plugin based XR interfaces. It needs to be initialised when Godot starts. You need to enable OpenXR, settings for this can be found in your games project settings under the XR heading. You do need to mark a viewport for use with XR in order for Godot to know which render result should be output to the headset. </description> <tutorials> - <link title="OpenXR documentation">$DOCS_URL/tutorials/vr/openxr/index.html</link> + <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> <signals> <signal name="pose_recentered"> diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index dcb1f1d744..87e2fae2d0 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -80,8 +80,8 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin float fw, fh; picture->size(&fw, &fh); - uint32_t width = MIN(fw * p_scale, 16 * 1024); - uint32_t height = MIN(fh * p_scale, 16 * 1024); + uint32_t width = MIN(round(fw * p_scale), 16 * 1024); + uint32_t height = MIN(round(fh * p_scale), 16 * 1024); picture->size(width, height); std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen(); diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 69848a9e52..0170c007ae 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -624,15 +624,15 @@ env.Append(CPPDEFINES=["GDEXTENSION"]) env.Append(CPPPATH=["../"]) sources = Glob("../*.cpp") -if env["platform"] == "osx": - methods.write_osx_plist( - f'./bin/libtextserver_advanced.osx.{env["target"]}.framework', - f'libtextserver_advanced.osx.{env["target"]}', +if env["platform"] == "macos": + methods.write_macos_plist( + f'./bin/libtextserver_advanced.macos.{env["target"]}.framework', + f'libtextserver_advanced.macos.{env["target"]}', "org.godotengine.textserver_advanced", "ICU / HarfBuzz / Graphite Text Server", ) library = env.SharedLibrary( - f'./bin/libtextserver_advanced.osx.{env["target"]}.framework/libtextserver_advanced.osx.{env["target"]}', + f'./bin/libtextserver_advanced.macos.{env["target"]}.framework/libtextserver_advanced.macos.{env["target"]}', source=sources, ) else: diff --git a/modules/text_server_adv/gdextension_build/methods.py b/modules/text_server_adv/gdextension_build/methods.py index d404f2851e..3c5229462c 100644 --- a/modules/text_server_adv/gdextension_build/methods.py +++ b/modules/text_server_adv/gdextension_build/methods.py @@ -98,7 +98,7 @@ def make_icu_data(target, source, env): g.write("#endif") -def write_osx_plist(target, binary_name, identifier, name): +def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resourece/", exist_ok=True) f = open(f"{target}/Resourece/Info.plist", "w") diff --git a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension index 5956476a5e..11ed271ae9 100644 --- a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension +++ b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension @@ -8,5 +8,5 @@ linux.64.debug = "bin/libtextserver_advanced.linux.debug.64.so" linux.64.release = "bin/libtextserver_advanced.linux.release.64.so" windows.64.debug = "bin/libtextserver_advanced.windows.debug.64.dll" windows.64.release = "bin/libtextserver_advanced.windows.release.64.dll" -macos.debug = "bin/libtextserver_advanced.osx.debug.framework" -macos.release = "bin/libtextserver_advanced.osx.release.framework" +macos.debug = "bin/libtextserver_advanced.macos.debug.framework" +macos.release = "bin/libtextserver_advanced.macos.release.framework" diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 6076b87203..fe2279df69 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1372,13 +1372,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f } p_font_data->style_flags = 0; if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) { - p_font_data->style_flags |= FONT_BOLD; + p_font_data->style_flags.set_flag(FONT_BOLD); } if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) { - p_font_data->style_flags |= FONT_ITALIC; + p_font_data->style_flags.set_flag(FONT_ITALIC); } if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) { - p_font_data->style_flags |= FONT_FIXED_WIDTH; + p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH); } hb_face_t *hb_face = hb_font_get_face(fd->hb_handle); @@ -1629,11 +1629,12 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f hb_ot_name_id_t lbl_id; if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GSUB, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) { - String lbl; + PackedInt32Array lbl; unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1; lbl.resize(text_size); + memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size); hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw()); - ftr["label"] = lbl; + ftr["label"] = String((const char32_t *)lbl.ptr()); } ftr["type"] = _get_tag_type(feature_tags[i]); ftr["hidden"] = _get_tag_hidden(feature_tags[i]); @@ -1651,11 +1652,12 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f hb_ot_name_id_t lbl_id; if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GPOS, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) { - String lbl; + PackedInt32Array lbl; unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1; lbl.resize(text_size); + memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size); hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw()); - ftr["label"] = lbl; + ftr["label"] = String((const char32_t *)lbl.ptr()); } ftr["type"] = _get_tag_type(feature_tags[i]); ftr["hidden"] = _get_tag_hidden(feature_tags[i]); @@ -1842,7 +1844,7 @@ int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const { return face_count; } -void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) { +void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -1852,7 +1854,7 @@ void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontSty fd->style_flags = p_style; } -int64_t /*FontStyle*/ TextServerAdvanced::font_get_style(const RID &p_font_rid) const { +BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_font_rid) const { FontAdvanced *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -3992,7 +3994,7 @@ RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const { return sd->parent; } -double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) { +double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); @@ -4008,7 +4010,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double int start_pos = 0; int end_pos = sd->glyphs.size() - 1; - if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) { + if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) { int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { start = sd->glyphs.size() - 1; @@ -4034,7 +4036,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double } double justification_width; - if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) { if (sd->overrun_trim_data.trim_pos >= 0) { if (sd->para_direction == DIRECTION_RTL) { start_pos = sd->overrun_trim_data.trim_pos; @@ -4049,7 +4051,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double justification_width = sd->width; } - if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) { + if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) { // Trim spaces. while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat; @@ -4088,7 +4090,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double } } - if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) { + if ((elongation_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_KASHIDA)) { double delta_width_per_kashida = (p_width - justification_width) / elongation_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; @@ -4109,7 +4111,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double } } } - if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { + if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) { double delta_width_per_space = (p_width - justification_width) / space_count; double adv_remain = 0; for (int i = start_pos; i <= end_pos; i++) { @@ -4142,7 +4144,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double sd->fit_width_minimum_reached = true; } - if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) { sd->width = justification_width; } @@ -4205,7 +4207,7 @@ double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const Pack return 0.0; } -void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) { +void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid."); @@ -4217,14 +4219,14 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l sd->text_trimmed = false; sd->overrun_trim_data.ellipsis_glyph_buf.clear(); - bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; - bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; - bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; - bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE; + bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS); + bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY); + bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS); + bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE); Glyph *sd_glyphs = sd->glyphs.ptrw(); - if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIM || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { sd->overrun_trim_data.trim_pos = -1; sd->overrun_trim_data.ellipsis_pos = -1; return; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 87582e8bac..a772955d90 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -232,7 +232,7 @@ class TextServerAdvanced : public TextServerExtension { double embolden = 0.0; Transform2D transform; - uint32_t style_flags = 0; + BitField<TextServer::FontStyle> style_flags = 0; String font_name; String style_name; @@ -486,8 +486,8 @@ public: virtual int64_t font_get_face_count(const RID &p_font_rid) const override; - virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override; - virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override; + virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override; + virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override; virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override; virtual String font_get_style_name(const RID &p_font_rid) const override; @@ -664,7 +664,7 @@ public: virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override; virtual RID shaped_text_get_parent(const RID &p_shaped) const override; - virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override; virtual bool shaped_text_shape(const RID &p_shaped) override; @@ -676,7 +676,7 @@ public: virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override; virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override; - virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override; + virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override; virtual bool shaped_text_is_ready(const RID &p_shaped) const override; diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 6c9e10db18..de0a549900 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -184,15 +184,15 @@ env.Append(CPPDEFINES=["GDEXTENSION"]) env.Append(CPPPATH=["../"]) sources = Glob("../*.cpp") -if env["platform"] == "osx": - methods.write_osx_plist( - f'./bin/libtextserver_fallback.osx.{env["target"]}.framework', - f'libtextserver_fallback.osx.{env["target"]}', +if env["platform"] == "macos": + methods.write_macos_plist( + f'./bin/libtextserver_fallback.macos.{env["target"]}.framework', + f'libtextserver_fallback.macos.{env["target"]}', "org.godotengine.textserver_fallback", "Fallback Text Server", ) library = env.SharedLibrary( - f'./bin/libtextserver_fallback.osx.{env["target"]}.framework/libtextserver_fallback.osx.{env["target"]}', + f'./bin/libtextserver_fallback.macos.{env["target"]}.framework/libtextserver_fallback.macos.{env["target"]}', source=sources, ) else: diff --git a/modules/text_server_fb/gdextension_build/methods.py b/modules/text_server_fb/gdextension_build/methods.py index d404f2851e..3c5229462c 100644 --- a/modules/text_server_fb/gdextension_build/methods.py +++ b/modules/text_server_fb/gdextension_build/methods.py @@ -98,7 +98,7 @@ def make_icu_data(target, source, env): g.write("#endif") -def write_osx_plist(target, binary_name, identifier, name): +def write_macos_plist(target, binary_name, identifier, name): os.makedirs(f"{target}/Resourece/", exist_ok=True) f = open(f"{target}/Resourece/Info.plist", "w") diff --git a/modules/text_server_fb/gdextension_build/text_server_fb.gdextension b/modules/text_server_fb/gdextension_build/text_server_fb.gdextension index 1026c6cb85..9236555d63 100644 --- a/modules/text_server_fb/gdextension_build/text_server_fb.gdextension +++ b/modules/text_server_fb/gdextension_build/text_server_fb.gdextension @@ -8,5 +8,5 @@ linux.64.debug = "bin/libtextserver_fallback.linux.debug.64.so" linux.64.release = "bin/libtextserver_fallback.linux.release.64.so" windows.64.debug = "bin/libtextserver_fallback.windows.debug.64.dll" windows.64.release = "bin/libtextserver_fallback.windows.release.64.dll" -macos.debug = "bin/libtextserver_fallback.osx.debug.framework" -macos.release = "bin/libtextserver_fallback.osx.release.framework" +macos.debug = "bin/libtextserver_fallback.macos.debug.framework" +macos.release = "bin/libtextserver_fallback.macos.release.framework" diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 7ccc9e3533..b845beb158 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -791,13 +791,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f } p_font_data->style_flags = 0; if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) { - p_font_data->style_flags |= FONT_BOLD; + p_font_data->style_flags.set_flag(FONT_BOLD); } if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) { - p_font_data->style_flags |= FONT_ITALIC; + p_font_data->style_flags.set_flag(FONT_ITALIC); } if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) { - p_font_data->style_flags |= FONT_FIXED_WIDTH; + p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH); } // Read OpenType variations. p_font_data->supported_varaitions.clear(); @@ -891,7 +891,7 @@ void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t fd->data_size = p_data_size; } -void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) { +void TextServerFallback::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) { FontFallback *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND(!fd); @@ -964,7 +964,7 @@ int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const { return face_count; } -int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid) const { +BitField<TextServer::FontStyle> TextServerFallback::font_get_style(const RID &p_font_rid) const { FontFallback *fd = font_owner.get_or_null(p_font_rid); ERR_FAIL_COND_V(!fd, 0); @@ -2950,7 +2950,7 @@ RID TextServerFallback::shaped_text_get_parent(const RID &p_shaped) const { return sd->parent; } -double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) { +double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<JustificationFlag> p_jst_flags) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.0); @@ -2965,7 +2965,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double int start_pos = 0; int end_pos = sd->glyphs.size() - 1; - if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) { + if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) { int start, end, delta; if (sd->para_direction == DIRECTION_LTR) { start = sd->glyphs.size() - 1; @@ -2991,7 +2991,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double } double justification_width; - if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) { if (sd->overrun_trim_data.trim_pos >= 0) { end_pos = sd->overrun_trim_data.trim_pos; justification_width = sd->width_trimmed; @@ -3002,7 +3002,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double justification_width = sd->width; } - if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) { + if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) { // Trim spaces. while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat; @@ -3034,7 +3034,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double } } - if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) { + if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) { double delta_width_per_space = (p_width - justification_width) / space_count; for (int i = start_pos; i <= end_pos; i++) { Glyph &gl = sd->glyphs.write[i]; @@ -3052,7 +3052,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double sd->fit_width_minimum_reached = true; } - if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) { + if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) { sd->width = justification_width; } @@ -3187,7 +3187,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(const RID &p_shape return true; } -void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) { +void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid."); @@ -3199,14 +3199,14 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l sd->text_trimmed = false; sd->overrun_trim_data.ellipsis_glyph_buf.clear(); - bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS; - bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY; - bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS; - bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE; + bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS); + bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY); + bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS); + bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE); Glyph *sd_glyphs = sd->glyphs.ptrw(); - if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIM || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { + if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) { sd->overrun_trim_data.trim_pos = -1; sd->overrun_trim_data.ellipsis_pos = -1; return; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 8b10c9e99e..497403afd7 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -189,7 +189,7 @@ class TextServerFallback : public TextServerExtension { double embolden = 0.0; Transform2D transform; - uint32_t style_flags = 0; + BitField<TextServer::FontStyle> style_flags = 0; String font_name; String style_name; @@ -368,8 +368,8 @@ public: virtual int64_t font_get_face_count(const RID &p_font_rid) const override; - virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override; - virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override; + virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override; + virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override; virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override; virtual String font_get_style_name(const RID &p_font_rid) const override; @@ -545,7 +545,7 @@ public: virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override; virtual RID shaped_text_get_parent(const RID &p_shaped) const override; - virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; + virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override; virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override; virtual bool shaped_text_shape(const RID &p_shaped) override; @@ -557,7 +557,7 @@ public: virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override; virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override; - virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override; + virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override; virtual bool shaped_text_is_ready(const RID &p_shaped) const override; diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 8940ed6aff..dd69bf066a 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -170,7 +170,7 @@ protected: static void _bind_methods(); public: - Ref<VideoStreamPlayback> instance_playback() override { + Ref<VideoStreamPlayback> instantiate_playback() override { Ref<VideoStreamPlaybackTheora> pb = memnew(VideoStreamPlaybackTheora); pb->set_audio_track(audio_track); pb->set_file(file); diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp index ceea0eaf1d..522ed8719b 100644 --- a/modules/visual_script/editor/visual_script_editor.cpp +++ b/modules/visual_script/editor/visual_script_editor.cpp @@ -1227,7 +1227,7 @@ void VisualScriptEditor::_member_selected() { selected = ti->get_metadata(0); if (ti->get_parent() == members->get_root()->get_first_child()) { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META); #else bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -2208,7 +2208,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & String(d["type"]) == "files" || String(d["type"]) == "nodes")) { if (String(d["type"]) == "obj_property") { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(Key::META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature.")); @@ -2216,7 +2216,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & } if (String(d["type"]) == "nodes") { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(Key::META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node.")); @@ -2224,7 +2224,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant & } if (String(d["type"]) == "visual_script_variable_drag") { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(Key::META))); #else const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter.")); @@ -2287,7 +2287,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } if (String(d["type"]) == "visual_script_variable_drag") { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool use_set = Input::get_singleton()->is_key_pressed(Key::META); #else bool use_set = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -2396,7 +2396,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } if (String(d["type"]) == "files") { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool use_preload = Input::get_singleton()->is_key_pressed(Key::META); #else bool use_preload = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -2459,7 +2459,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da return; } -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool use_node = Input::get_singleton()->is_key_pressed(Key::META); #else bool use_node = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -2524,7 +2524,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da Node *node = Object::cast_to<Node>(obj); Vector2 pos = _get_pos_in_graph(p_point); -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool use_get = Input::get_singleton()->is_key_pressed(Key::META); #else bool use_get = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -3378,7 +3378,7 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua } void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) { -#ifdef OSX_ENABLED +#ifdef MACOS_ENABLED bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META); #else bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL); @@ -4628,7 +4628,7 @@ VisualScriptEditor::VisualScriptEditor() { graph = memnew(GraphEdit); add_child(graph); graph->set_v_size_flags(Control::SIZE_EXPAND_FILL); - graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE); + graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); graph->set_show_zoom_label(true); graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected)); graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); @@ -4717,7 +4717,7 @@ VisualScriptEditor::VisualScriptEditor() { function_create_dialog = memnew(ConfirmationDialog); function_create_dialog->set_title(TTR("Create Function")); function_create_dialog->add_child(function_vb); - function_create_dialog->get_ok_button()->set_text(TTR("Create")); + function_create_dialog->set_ok_button_text(TTR("Create")); function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function)); add_child(function_create_dialog); @@ -4761,7 +4761,7 @@ VisualScriptEditor::VisualScriptEditor() { graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty)); edit_signal_dialog = memnew(AcceptDialog); - edit_signal_dialog->get_ok_button()->set_text(TTR("Close")); + edit_signal_dialog->set_ok_button_text(TTR("Close")); add_child(edit_signal_dialog); signal_editor = memnew(VisualScriptEditorSignalEdit); @@ -4771,7 +4771,7 @@ VisualScriptEditor::VisualScriptEditor() { edit_signal_edit->edit(signal_editor); edit_variable_dialog = memnew(AcceptDialog); - edit_variable_dialog->get_ok_button()->set_text(TTR("Close")); + edit_variable_dialog->set_ok_button_text(TTR("Close")); add_child(edit_variable_dialog); variable_editor = memnew(VisualScriptEditorVariableEdit); diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp index ae30655301..f98ce5bb26 100644 --- a/modules/visual_script/editor/visual_script_property_selector.cpp +++ b/modules/visual_script/editor/visual_script_property_selector.cpp @@ -533,7 +533,7 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() { scroller->add_child(help_bit); help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested)); - get_ok_button()->set_text(TTR("Open")); + set_ok_button_text(TTR("Open")); get_ok_button()->set_disabled(true); set_hide_on_ok(false); } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index 89a6b03ff8..76f7317daa 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -43,28 +43,93 @@ int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram int todo = p_frames; - int start_buffer = 0; + int beat_length_frames = -1; + bool beat_loop = vorbis_stream->has_loop(); + if (beat_loop && vorbis_stream->get_bpm() > 0 && vorbis_stream->get_beat_count() > 0) { + beat_length_frames = vorbis_stream->get_beat_count() * vorbis_data->get_sampling_rate() * 60 / vorbis_stream->get_bpm(); + } while (todo > 0 && active) { AudioFrame *buffer = p_buffer; - if (start_buffer > 0) { - buffer = buffer + start_buffer; + buffer += p_frames - todo; + + int to_mix = todo; + if (beat_length_frames >= 0 && (beat_length_frames - (int)frames_mixed) < to_mix) { + to_mix = MAX(0, beat_length_frames - (int)frames_mixed); } - int mixed = _mix_frames_vorbis(buffer, todo); + + int mixed = _mix_frames_vorbis(buffer, to_mix); ERR_FAIL_COND_V(mixed < 0, 0); todo -= mixed; frames_mixed += mixed; - start_buffer += mixed; + + if (loop_fade_remaining < FADE_SIZE) { + int to_fade = loop_fade_remaining + MIN(FADE_SIZE - loop_fade_remaining, mixed); + for (int i = loop_fade_remaining; i < to_fade; i++) { + buffer[i - loop_fade_remaining] += loop_fade[i] * (float(FADE_SIZE - i) / float(FADE_SIZE)); + } + loop_fade_remaining = to_fade; + } + + if (beat_length_frames >= 0) { + /** + * Length determined by beat length + * This code is commented out because, in practice, it is prefered that the fade + * is done by the transitioner and this stream just goes on until it ends while fading out. + * + * End fade implementation is left here for reference in case at some point this feature + * is desired. + + if (!beat_loop && (int)frames_mixed > beat_length_frames - FADE_SIZE) { + print_line("beat length fade/after mix?"); + //No loop, just fade and finish + for (int i = 0; i < mixed; i++) { + int idx = frames_mixed + i - mixed; + buffer[i] *= 1.0 - float(MAX(0, (idx - (beat_length_frames - FADE_SIZE)))) / float(FADE_SIZE); + } + if ((int)frames_mixed == beat_length_frames) { + for (int i = p_frames - todo; i < p_frames; i++) { + p_buffer[i] = AudioFrame(0, 0); + } + active = false; + break; + } + } else + **/ + + if (beat_loop && beat_length_frames <= (int)frames_mixed) { + // End of file when doing beat-based looping. <= used instead of == because importer editing + if (!have_packets_left && !have_samples_left) { + //Nothing remaining, so do nothing. + loop_fade_remaining = FADE_SIZE; + } else { + // Add some loop fade; + int faded_mix = _mix_frames_vorbis(loop_fade, FADE_SIZE); + + for (int i = faded_mix; i < FADE_SIZE; i++) { + // In case lesss was mixed, pad with zeros + loop_fade[i] = AudioFrame(0, 0); + } + loop_fade_remaining = 0; + } + + seek(vorbis_stream->loop_offset); + loops++; + // We still have buffer to fill, start from this element in the next iteration. + continue; + } + } + if (!have_packets_left && !have_samples_left) { - //end of file! + // Actual end of file! bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0; if (vorbis_stream->loop && is_not_empty) { //loop seek(vorbis_stream->loop_offset); loops++; - // we still have buffer to fill, start from this element in the next iteration. - start_buffer = p_frames - todo; + // We still have buffer to fill, start from this element in the next iteration. + } else { for (int i = p_frames - todo; i < p_frames; i++) { p_buffer[i] = AudioFrame(0, 0); @@ -130,7 +195,7 @@ bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() { comment_is_allocated = true; ERR_FAIL_COND_V(vorbis_data.is_null(), false); - vorbis_data_playback = vorbis_data->instance_playback(); + vorbis_data_playback = vorbis_data->instantiate_playback(); ogg_packet *packet; int err; @@ -160,6 +225,7 @@ bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() { void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) { ERR_FAIL_COND(!ready); + loop_fade_remaining = FADE_SIZE; active = true; seek(p_from_pos); loops = 0; @@ -182,6 +248,10 @@ float AudioStreamPlaybackOGGVorbis::get_playback_position() const { return float(frames_mixed) / vorbis_data->get_sampling_rate(); } +void AudioStreamPlaybackOGGVorbis::tag_used_streams() { + vorbis_stream->tag_used(get_playback_position()); +} + void AudioStreamPlaybackOGGVorbis::seek(float p_time) { ERR_FAIL_COND(!ready); ERR_FAIL_COND(vorbis_stream.is_null()); @@ -315,7 +385,7 @@ AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() { } } -Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() { +Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instantiate_playback() { Ref<AudioStreamPlaybackOGGVorbis> ovs; ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr); @@ -347,7 +417,7 @@ void AudioStreamOGGVorbis::maybe_update_info() { vorbis_info_init(&info); vorbis_comment_init(&comment); - Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback(); + Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instantiate_playback(); for (int i = 0; i < 3; i++) { ogg_packet *packet; @@ -405,6 +475,36 @@ float AudioStreamOGGVorbis::get_length() const { return packet_sequence->get_length(); } +void AudioStreamOGGVorbis::set_bpm(double p_bpm) { + ERR_FAIL_COND(p_bpm < 0); + bpm = p_bpm; + emit_changed(); +} + +double AudioStreamOGGVorbis::get_bpm() const { + return bpm; +} + +void AudioStreamOGGVorbis::set_beat_count(int p_beat_count) { + ERR_FAIL_COND(p_beat_count < 0); + beat_count = p_beat_count; + emit_changed(); +} + +int AudioStreamOGGVorbis::get_beat_count() const { + return beat_count; +} + +void AudioStreamOGGVorbis::set_bar_beats(int p_bar_beats) { + ERR_FAIL_COND(p_bar_beats < 2); + bar_beats = p_bar_beats; + emit_changed(); +} + +int AudioStreamOGGVorbis::get_bar_beats() const { + return bar_beats; +} + bool AudioStreamOGGVorbis::is_monophonic() const { return false; } @@ -419,7 +519,19 @@ void AudioStreamOGGVorbis::_bind_methods() { ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset); ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset); + ClassDB::bind_method(D_METHOD("set_bpm", "bpm"), &AudioStreamOGGVorbis::set_bpm); + ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamOGGVorbis::get_bpm); + + ClassDB::bind_method(D_METHOD("set_beat_count", "count"), &AudioStreamOGGVorbis::set_beat_count); + ClassDB::bind_method(D_METHOD("get_beat_count"), &AudioStreamOGGVorbis::get_beat_count); + + ClassDB::bind_method(D_METHOD("set_bar_beats", "count"), &AudioStreamOGGVorbis::set_bar_beats); + ClassDB::bind_method(D_METHOD("get_bar_beats"), &AudioStreamOGGVorbis::get_bar_beats); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), "set_bpm", "get_bpm"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,1,or_greater"), "set_beat_count", "get_beat_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,1,or_greater"), "set_bar_beats", "get_bar_beats"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset"); } diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index b09ef9ff5d..22c2eb4d73 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -45,6 +45,12 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { bool active = false; int loops = 0; + enum { + FADE_SIZE = 256 + }; + AudioFrame loop_fade[FADE_SIZE]; + int loop_fade_remaining = FADE_SIZE; + vorbis_info info; vorbis_comment comment; vorbis_dsp_state dsp_state; @@ -66,6 +72,7 @@ class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled { Ref<OGGPacketSequencePlayback> vorbis_data_playback; Ref<AudioStreamOGGVorbis> vorbis_stream; + int _mix_frames(AudioFrame *p_buffer, int p_frames); int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames); // Allocates vorbis data structures. Returns true upon success, false on failure. @@ -85,6 +92,8 @@ public: virtual float get_playback_position() const override; virtual void seek(float p_time) override; + virtual void tag_used_streams() override; + AudioStreamPlaybackOGGVorbis() {} ~AudioStreamPlaybackOGGVorbis(); }; @@ -107,17 +116,30 @@ class AudioStreamOGGVorbis : public AudioStream { Ref<OGGPacketSequence> packet_sequence; + double bpm = 0; + int beat_count = 0; + int bar_beats = 4; + protected: static void _bind_methods(); public: void set_loop(bool p_enable); - bool has_loop() const; + virtual bool has_loop() const override; void set_loop_offset(float p_seconds); float get_loop_offset() const; - virtual Ref<AudioStreamPlayback> instance_playback() override; + void set_bpm(double p_bpm); + virtual double get_bpm() const override; + + void set_beat_count(int p_beat_count); + virtual int get_beat_count() const override; + + void set_bar_beats(int p_bar_beats); + virtual int get_bar_beats() const override; + + virtual Ref<AudioStreamPlayback> instantiate_playback() override; virtual String get_stream_name() const override; void set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence); diff --git a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml index 2f210a6cb4..f87296dcd8 100644 --- a/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml @@ -7,6 +7,12 @@ <tutorials> </tutorials> <members> + <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4"> + </member> + <member name="beat_count" type="int" setter="set_beat_count" getter="get_beat_count" default="0"> + </member> + <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0"> + </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> If [code]true[/code], the stream will automatically loop when it reaches the end. </member> diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 7ee6446313..9461d531fd 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -30,13 +30,16 @@ #include "resource_importer_ogg_vorbis.h" -#include "audio_stream_ogg_vorbis.h" #include "core/io/file_access.h" #include "core/io/resource_saver.h" #include "scene/resources/texture.h" #include "thirdparty/libogg/ogg/ogg.h" #include "thirdparty/libvorbis/vorbis/codec.h" +#ifdef TOOLS_ENABLED +#include "editor/import/audio_stream_import_settings.h" +#endif + String ResourceImporterOGGVorbis::get_importer_name() const { return "oggvorbisstr"; } @@ -72,14 +75,14 @@ String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const { void ResourceImporterOGGVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "bpm", PROPERTY_HINT_RANGE, "0,400,0.01,or_greater"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "beat_count", PROPERTY_HINT_RANGE, "0,512,or_greater"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4)); } -Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - bool loop = p_options["loop"]; - float loop_offset = p_options["loop_offset"]; - - Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'."); +Ref<AudioStreamOGGVorbis> ResourceImporterOGGVorbis::import_ogg_vorbis(const String &p_path) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOGGVorbis>(), "Cannot open file '" + p_path + "'."); uint64_t len = f->get_length(); @@ -107,16 +110,16 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin size_t packet_count = 0; bool done = false; while (!done) { - ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err)); while (ogg_sync_pageout(&sync_state, &page) != 1) { if (cursor >= len) { done = true; break; } - ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err)); char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE); - ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); - ERR_FAIL_COND_V(cursor > len, Error::ERR_INVALID_DATA); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOGGVorbis>()); size_t copy_size = len - cursor; if (copy_size > OGG_SYNC_BUFFER_SIZE) { copy_size = OGG_SYNC_BUFFER_SIZE; @@ -124,22 +127,22 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin memcpy(sync_buf, &file_data[cursor], copy_size); ogg_sync_wrote(&sync_state, copy_size); cursor += copy_size; - ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err)); } if (done) { break; } - ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err)); + ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Ref<AudioStreamOGGVorbis>(), "Ogg sync error " + itos(err)); // Have a page now. if (!initialized_stream) { if (ogg_stream_init(&stream_state, ogg_page_serialno(&page))) { - ERR_FAIL_V_MSG(Error::ERR_OUT_OF_MEMORY, "Failed allocating memory for OGG Vorbis stream."); + ERR_FAIL_V_MSG(Ref<AudioStreamOGGVorbis>(), "Failed allocating memory for OGG Vorbis stream."); } initialized_stream = true; } ogg_stream_pagein(&stream_state, &page); - ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err)); + ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Ref<AudioStreamOGGVorbis>(), "Ogg stream error " + itos(err)); int desync_iters = 0; Vector<Vector<uint8_t>> packet_data; @@ -150,7 +153,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin if (err == -1) { // According to the docs this is usually recoverable, but don't sit here spinning forever. desync_iters++; - ERR_FAIL_COND_V_MSG(desync_iters > 100, Error::ERR_INVALID_DATA, "Packet sync issue during ogg import"); + ERR_FAIL_COND_V_MSG(desync_iters > 100, Ref<AudioStreamOGGVorbis>(), "Packet sync issue during ogg import"); continue; } else if (err == 0) { // Not enough data to fully reconstruct a packet. Go on to the next page. @@ -183,12 +186,45 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin ogg_sync_clear(&sync_state); if (ogg_packet_sequence->get_packet_granule_positions().is_empty()) { - ERR_FAIL_V_MSG(Error::ERR_FILE_CORRUPT, "OGG Vorbis decoding failed. Check that your data is a valid OGG Vorbis audio stream."); + ERR_FAIL_V_MSG(Ref<AudioStreamOGGVorbis>(), "OGG Vorbis decoding failed. Check that your data is a valid OGG Vorbis audio stream."); } ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence); + + return ogg_vorbis_stream; +} + +#ifdef TOOLS_ENABLED + +bool ResourceImporterOGGVorbis::has_advanced_options() const { + return true; +} + +void ResourceImporterOGGVorbis::show_advanced_options(const String &p_path) { + Ref<AudioStreamOGGVorbis> ogg_stream = import_ogg_vorbis(p_path); + if (ogg_stream.is_valid()) { + AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream); + } +} +#endif + +Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + bool loop = p_options["loop"]; + float loop_offset = p_options["loop_offset"]; + double bpm = p_options["bpm"]; + int beat_count = p_options["beat_count"]; + int bar_beats = p_options["bar_beats"]; + + Ref<AudioStreamOGGVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file); + if (ogg_vorbis_stream.is_null()) { + return ERR_CANT_OPEN; + } + ogg_vorbis_stream->set_loop(loop); ogg_vorbis_stream->set_loop_offset(loop_offset); + ogg_vorbis_stream->set_bpm(bpm); + ogg_vorbis_stream->set_beat_count(beat_count); + ogg_vorbis_stream->set_bar_beats(bar_beats); return ResourceSaver::save(p_save_path + ".oggvorbisstr", ogg_vorbis_stream); } diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h index 3b4a68a1fd..e6e98a29c1 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.h +++ b/modules/vorbis/resource_importer_ogg_vorbis.h @@ -31,6 +31,7 @@ #ifndef RESOURCE_IMPORTER_OGG_VORBIS_H #define RESOURCE_IMPORTER_OGG_VORBIS_H +#include "audio_stream_ogg_vorbis.h" #include "core/io/resource_importer.h" class ResourceImporterOGGVorbis : public ResourceImporter { @@ -43,7 +44,13 @@ class ResourceImporterOGGVorbis : public ResourceImporter { private: // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0; + static Ref<AudioStreamOGGVorbis> import_ogg_vorbis(const String &p_path); + public: +#ifdef TOOLS_ENABLED + virtual bool has_advanced_options() const override; + virtual void show_advanced_options(const String &p_path) override; +#endif virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual String get_save_extension() const override; virtual String get_resource_type() const override; |