diff options
106 files changed, 1810 insertions, 484 deletions
diff --git a/SConstruct b/SConstruct index 90cc99dd47..ab4fe118c8 100644 --- a/SConstruct +++ b/SConstruct @@ -55,7 +55,7 @@ custom_tools = ["default"] platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False)) -if os.name == "nt" and (platform_arg == "android" or ARGUMENTS.get("use_mingw", False)): +if os.name == "nt" and (platform_arg == "android" or methods.get_cmdline_bool("use_mingw", False)): custom_tools = ["mingw"] elif platform_arg == "javascript": # Use generic POSIX build toolchain for Emscripten. @@ -95,7 +95,7 @@ env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL)) customs = ["custom.py"] -profile = ARGUMENTS.get("profile", False) +profile = methods.get_cmdline_bool("profile", False) if profile: if os.path.isfile(profile): customs.append(profile) @@ -325,17 +325,17 @@ if selected_platform in platform_list: env.Alias("compiledb", env.CompilationDatabase()) # 'dev' and 'production' are aliases to set default options if they haven't been set - # manually by the user. We use `ARGUMENTS.get()` to check if they were manually set. + # manually by the user. if env["dev"]: - env["verbose"] = ARGUMENTS.get("verbose", True) + env["verbose"] = methods.get_cmdline_bool("verbose", True) env["warnings"] = ARGUMENTS.get("warnings", "extra") - env["werror"] = ARGUMENTS.get("werror", True) + env["werror"] = methods.get_cmdline_bool("werror", True) if env["tools"]: - env["tests"] = ARGUMENTS.get("tests", True) + env["tests"] = methods.get_cmdline_bool("tests", True) if env["production"]: - env["use_static_cpp"] = ARGUMENTS.get("use_static_cpp", True) - env["use_lto"] = ARGUMENTS.get("use_lto", True) - env["debug_symbols"] = ARGUMENTS.get("debug_symbols", False) + env["use_static_cpp"] = methods.get_cmdline_bool("use_static_cpp", True) + env["use_lto"] = methods.get_cmdline_bool("use_lto", True) + env["debug_symbols"] = methods.get_cmdline_bool("debug_symbols", False) if not env["tools"] and env["target"] == "debug": print( "WARNING: Requested `production` build with `tools=no target=debug`, " diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 0da6680a7b..47c75cfa28 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -276,6 +276,10 @@ String _OS::get_environment(const String &p_var) const { return OS::get_singleton()->get_environment(p_var); } +bool _OS::set_environment(const String &p_var, const String &p_value) const { + return OS::get_singleton()->set_environment(p_var, p_value); +} + String _OS::get_name() const { return OS::get_singleton()->get_name(); } @@ -318,18 +322,6 @@ uint64_t _OS::get_static_memory_peak_usage() const { return OS::get_singleton()->get_static_memory_peak_usage(); } -int _OS::get_exit_code() const { - return OS::get_singleton()->get_exit_code(); -} - -void _OS::set_exit_code(int p_code) { - if (p_code < 0 || p_code > 125) { - WARN_PRINT("For portability reasons, the exit code should be set between 0 and 125 (inclusive)."); - } - - OS::get_singleton()->set_exit_code(p_code); -} - /** * Get current datetime with consideration for utc and * dst @@ -711,8 +703,9 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("shell_open", "uri"), &_OS::shell_open); ClassDB::bind_method(D_METHOD("get_process_id"), &_OS::get_process_id); - ClassDB::bind_method(D_METHOD("get_environment", "environment"), &_OS::get_environment); - ClassDB::bind_method(D_METHOD("has_environment", "environment"), &_OS::has_environment); + ClassDB::bind_method(D_METHOD("get_environment", "variable"), &_OS::get_environment); + ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &_OS::set_environment); + ClassDB::bind_method(D_METHOD("has_environment", "variable"), &_OS::has_environment); ClassDB::bind_method(D_METHOD("get_name"), &_OS::get_name); ClassDB::bind_method(D_METHOD("get_cmdline_args"), &_OS::get_cmdline_args); @@ -725,9 +718,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_datetime_from_unix_time", "unix_time_val"), &_OS::get_datetime_from_unix_time); ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime", "datetime"), &_OS::get_unix_time_from_datetime); - ClassDB::bind_method(D_METHOD("get_exit_code"), &_OS::get_exit_code); - ClassDB::bind_method(D_METHOD("set_exit_code", "code"), &_OS::set_exit_code); - ClassDB::bind_method(D_METHOD("delay_usec", "usec"), &_OS::delay_usec); ClassDB::bind_method(D_METHOD("delay_msec", "msec"), &_OS::delay_msec); ClassDB::bind_method(D_METHOD("get_ticks_msec"), &_OS::get_ticks_msec); @@ -772,7 +762,6 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions); ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions); - ADD_PROPERTY(PropertyInfo(Variant::INT, "exit_code"), "set_exit_code", "get_exit_code"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "low_processor_usage_mode"), "set_low_processor_usage_mode", "is_in_low_processor_usage_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "low_processor_usage_mode_sleep_usec"), "set_low_processor_usage_mode_sleep_usec", "get_low_processor_usage_mode_sleep_usec"); diff --git a/core/core_bind.h b/core/core_bind.h index 8a4885b82b..3920116ca4 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -172,6 +172,7 @@ public: bool has_environment(const String &p_var) const; String get_environment(const String &p_var) const; + bool set_environment(const String &p_var, const String &p_value) const; String get_name() const; Vector<String> get_cmdline_args(); @@ -198,8 +199,6 @@ public: void set_use_file_access_save_and_swap(bool p_enable); - int get_exit_code() const; - void set_exit_code(int p_code); Dictionary get_date(bool utc) const; Dictionary get_time(bool utc) const; Dictionary get_datetime(bool utc) const; diff --git a/core/core_constants.cpp b/core/core_constants.cpp index ef5dbf17bb..f9edff1899 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -125,6 +125,9 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(VERTICAL); BIND_CORE_ENUM_CONSTANT(HORIZONTAL); + BIND_CORE_ENUM_CONSTANT(CLOCKWISE); + BIND_CORE_ENUM_CONSTANT(COUNTERCLOCKWISE); + BIND_CORE_ENUM_CONSTANT(HALIGN_LEFT); BIND_CORE_ENUM_CONSTANT(HALIGN_CENTER); BIND_CORE_ENUM_CONSTANT(HALIGN_RIGHT); diff --git a/core/input/input.cpp b/core/input/input.cpp index 94a18b5b4f..f928ae7654 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -241,10 +241,18 @@ bool Input::is_joy_button_pressed(int p_device, int p_button) const { } bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { +#ifdef DEBUG_ENABLED + bool has_action = InputMap::get_singleton()->has_action(p_action); + ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); +#endif return action_state.has(p_action) && action_state[p_action].pressed && (p_exact ? action_state[p_action].exact : true); } bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { +#ifdef DEBUG_ENABLED + bool has_action = InputMap::get_singleton()->has_action(p_action); + ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); +#endif const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; @@ -262,6 +270,10 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con } bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { +#ifdef DEBUG_ENABLED + bool has_action = InputMap::get_singleton()->has_action(p_action); + ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); +#endif const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return false; @@ -279,6 +291,10 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co } float Input::get_action_strength(const StringName &p_action, bool p_exact) const { +#ifdef DEBUG_ENABLED + bool has_action = InputMap::get_singleton()->has_action(p_action); + ERR_FAIL_COND_V_MSG(!has_action, false, "Request for nonexistent InputMap action '" + String(p_action) + "'."); +#endif const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) { return 0.0f; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index b31c431ead..e0b25fa092 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -434,8 +434,8 @@ const OrderedHashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_undo", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(KEY_Y | KEY_MASK_CMD)); inputs.push_back(InputEventKey::create_reference(KEY_Z | KEY_MASK_CMD | KEY_MASK_SHIFT)); + inputs.push_back(InputEventKey::create_reference(KEY_Y | KEY_MASK_CMD)); default_builtin_cache.insert("ui_redo", inputs); // ///// UI Text Input Shortcuts ///// @@ -694,7 +694,7 @@ void InputMap::load_default() { // For the editor, only add keyboard actions. if (iek.is_valid()) { - action_add_event(fullname, I->get()); + action_add_event(name, I->get()); } } } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 3c84e6b656..343adbe592 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -92,7 +92,7 @@ private: PathMD5() {} - PathMD5(const Vector<uint8_t> p_buf) { + PathMD5(const Vector<uint8_t> &p_buf) { a = *((uint64_t *)&p_buf[0]); b = *((uint64_t *)&p_buf[8]); } diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 3e460726f6..5ca0eb884a 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -352,6 +352,12 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi } } +void ResourceFormatImporter::get_importers(List<Ref<ResourceImporter>> *r_importers) { + for (int i = 0; i < importers.size(); i++) { + r_importers->push_back(importers[i]); + } +} + Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const String &p_extension) const { Ref<ResourceImporter> importer; float priority = 0; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index bda8b74b73..91efec5534 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -82,6 +82,7 @@ public: Ref<ResourceImporter> get_importer_by_name(const String &p_name) const; Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const; void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers); + void get_importers(List<Ref<ResourceImporter>> *r_importers); bool are_import_settings_valid(const String &p_path) const; String get_import_settings_hash() const; diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 905be6089d..1574634aad 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -36,63 +36,6 @@ VARIANT_ENUM_CAST(XMLParser::NodeType); -static bool _equalsn(const char32_t *str1, const char32_t *str2, int len) { - int i; - for (i = 0; i < len && str1[i] && str2[i]; ++i) { - if (str1[i] != str2[i]) { - return false; - } - } - - // if one (or both) of the strings was smaller then they - // are only equal if they have the same length - return (i == len) || (str1[i] == 0 && str2[i] == 0); -} - -String XMLParser::_replace_special_characters(const String &origstr) { - int pos = origstr.find("&"); - int oldPos = 0; - - if (pos == -1) { - return origstr; - } - - String newstr; - - while (pos != -1 && pos < origstr.length() - 2) { - // check if it is one of the special characters - - int specialChar = -1; - for (int i = 0; i < (int)special_characters.size(); ++i) { - const char32_t *p = &origstr[pos] + 1; - - if (_equalsn(&special_characters[i][1], p, special_characters[i].length() - 1)) { - specialChar = i; - break; - } - } - - if (specialChar != -1) { - newstr += (origstr.substr(oldPos, pos - oldPos)); - newstr += (special_characters[specialChar][0]); - pos += special_characters[specialChar].length(); - } else { - newstr += (origstr.substr(oldPos, pos - oldPos + 1)); - pos += 1; - } - - // find next & - oldPos = pos; - pos = origstr.find("&", pos); - } - - if (oldPos < origstr.length() - 1) { - newstr += (origstr.substr(oldPos, origstr.length() - oldPos)); - } - - return newstr; -} - static inline bool _is_white_space(char c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); } @@ -116,7 +59,7 @@ bool XMLParser::_set_text(char *start, char *end) { // set current text to the parsed text, and replace xml special characters String s = String::utf8(start, (int)(end - start)); - node_name = _replace_special_characters(s); + node_name = s.xml_unescape(); // current XML node type is text node_type = NODE_TEXT; @@ -292,7 +235,7 @@ void XMLParser::_parse_opening_xml_element() { String s = String::utf8(attributeValueBegin, (int)(attributeValueEnd - attributeValueBegin)); - attr.value = _replace_special_characters(s); + attr.value = s.xml_unescape(); attributes.push_back(attr); } else { // tag is closed directly @@ -555,11 +498,6 @@ int XMLParser::get_current_line() const { } XMLParser::XMLParser() { - special_characters.push_back("&"); - special_characters.push_back("<lt;"); - special_characters.push_back(">gt;"); - special_characters.push_back("\"quot;"); - special_characters.push_back("'apos;"); } XMLParser::~XMLParser() { diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 01af6a90ad..847edf958d 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -68,8 +68,6 @@ private: char *data = nullptr; char *P = nullptr; uint64_t length = 0; - void unescape(String &p_str); - Vector<String> special_characters; String node_name; bool node_empty = false; NodeType node_type = NODE_NONE; diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp index 783750b9e6..d67be14d33 100644 --- a/core/math/geometry_2d.cpp +++ b/core/math/geometry_2d.cpp @@ -94,6 +94,10 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re // 256x8192 atlas (won't work anywhere). ERR_FAIL_COND(p_rects.size() == 0); + for (int i = 0; i < p_rects.size(); i++) { + ERR_FAIL_COND(p_rects[i].width <= 0); + ERR_FAIL_COND(p_rects[i].height <= 0); + } Vector<_AtlasWorkRect> wrects; wrects.resize(p_rects.size()); diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index a4a7463bfd..6628b760e0 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -775,6 +775,8 @@ Vector<Plane> Geometry3D::build_box_planes(const Vector3 &p_extents) { } Vector<Plane> Geometry3D::build_cylinder_planes(real_t p_radius, real_t p_height, int p_sides, Vector3::Axis p_axis) { + ERR_FAIL_INDEX_V(p_axis, 3, Vector<Plane>()); + Vector<Plane> planes; const double sides_step = Math_TAU / p_sides; @@ -796,6 +798,8 @@ Vector<Plane> Geometry3D::build_cylinder_planes(real_t p_radius, real_t p_height } Vector<Plane> Geometry3D::build_sphere_planes(real_t p_radius, int p_lats, int p_lons, Vector3::Axis p_axis) { + ERR_FAIL_INDEX_V(p_axis, 3, Vector<Plane>()); + Vector<Plane> planes; Vector3 axis; @@ -827,6 +831,8 @@ Vector<Plane> Geometry3D::build_sphere_planes(real_t p_radius, int p_lats, int p } Vector<Plane> Geometry3D::build_capsule_planes(real_t p_radius, real_t p_height, int p_sides, int p_lats, Vector3::Axis p_axis) { + ERR_FAIL_INDEX_V(p_axis, 3, Vector<Plane>()); + Vector<Plane> planes; Vector3 axis; diff --git a/core/os/os.h b/core/os/os.h index 77a54ba68a..e41d788e12 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -40,6 +40,7 @@ #include "core/templates/vector.h" #include <stdarg.h> +#include <stdlib.h> class OS { static OS *singleton; @@ -53,7 +54,7 @@ class OS { bool _debug_stdout = false; String _local_clipboard; bool _no_window = false; - int _exit_code = 0; + int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure int _orientation; bool _allow_hidpi = false; bool _allow_layered = false; diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 88744eed63..aea370787d 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -92,6 +92,7 @@ bool Thread::is_started() const { void Thread::wait_to_finish() { if (id != 0) { + ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish."); thread.join(); std::thread empty_thread; thread.swap(empty_thread); diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 0901944360..9cee218735 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -39,13 +39,14 @@ #include "main/main.h" #endif -// ISO 639-1 language codes, with the addition of glibc locales with their -// regional identifiers. This list must match the language names (in English) -// of locale_names. +// ISO 639-1 language codes (and a couple of three-letter ISO 639-2 codes), +// with the addition of glibc locales with their regional identifiers. +// This list must match the language names (in English) of locale_names. // // References: // - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes // - https://lh.2xlibre.net/locales/ +// - https://iso639-3.sil.org/ static const char *locale_list[] = { "aa", // Afar @@ -100,6 +101,7 @@ static const char *locale_list[] = { "bo", // Tibetan "bo_CN", // Tibetan (China) "bo_IN", // Tibetan (India) + "br", // Breton "br_FR", // Breton (France) "brx_IN", // Bodo (India) "bs_BA", // Bosnian (Bosnia and Herzegovina) @@ -201,6 +203,7 @@ static const char *locale_list[] = { "gd_GB", // Scottish Gaelic (United Kingdom) "gez_ER", // Geez (Eritrea) "gez_ET", // Geez (Ethiopia) + "gl", // Galician "gl_ES", // Galician (Spain) "gu_IN", // Gujarati (India) "gv_GB", // Manx (United Kingdom) @@ -273,6 +276,7 @@ static const char *locale_list[] = { "ml_IN", // Malayalam (India) "mni_IN", // Manipuri (India) "mn_MN", // Mongolian (Mongolia) + "mr", // Marathi "mr_IN", // Marathi (India) "ms", // Malay "ms_MY", // Malay (Malaysia) @@ -302,6 +306,7 @@ static const char *locale_list[] = { "om", // Oromo "om_ET", // Oromo (Ethiopia) "om_KE", // Oromo (Kenya) + "or", // Oriya "or_IN", // Oriya (India) "os_RU", // Ossetian (Russia) "pa_IN", // Panjabi (India) @@ -386,6 +391,8 @@ static const char *locale_list[] = { "tr_TR", // Turkish (Turkey) "ts_ZA", // Tsonga (South Africa) "tt_RU", // Tatar (Russia) + "tzm", // Central Atlas Tamazight + "tzm_MA", // Central Atlas Tamazight (Marrocos) "ug_CN", // Uighur (China) "uk", // Ukrainian "uk_UA", // Ukrainian (Ukraine) @@ -468,6 +475,7 @@ static const char *locale_names[] = { "Tibetan", "Tibetan (China)", "Tibetan (India)", + "Breton", "Breton (France)", "Bodo (India)", "Bosnian (Bosnia and Herzegovina)", @@ -569,6 +577,7 @@ static const char *locale_names[] = { "Scottish Gaelic (United Kingdom)", "Geez (Eritrea)", "Geez (Ethiopia)", + "Galician", "Galician (Spain)", "Gujarati (India)", "Manx (United Kingdom)", @@ -641,6 +650,7 @@ static const char *locale_names[] = { "Malayalam (India)", "Manipuri (India)", "Mongolian (Mongolia)", + "Marathi", "Marathi (India)", "Malay", "Malay (Malaysia)", @@ -670,6 +680,7 @@ static const char *locale_names[] = { "Oromo", "Oromo (Ethiopia)", "Oromo (Kenya)", + "Oriya", "Oriya (India)", "Ossetian (Russia)", "Panjabi (India)", @@ -754,6 +765,8 @@ static const char *locale_names[] = { "Turkish (Turkey)", "Tsonga (South Africa)", "Tatar (Russia)", + "Central Atlas Tamazight", + "Central Atlas Tamazight (Marrocos)", "Uighur (China)", "Ukrainian", "Ukrainian (Ukraine)", diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 59fda65d43..a57c7b2504 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3888,25 +3888,55 @@ static _FORCE_INLINE_ int _xml_unescape(const char32_t *p_src, int p_src_len, ch if (p_src_len >= 4 && p_src[1] == '#') { char32_t c = 0; - - for (int i = 2; i < p_src_len; i++) { - eat = i + 1; - char32_t ct = p_src[i]; - if (ct == ';') { - break; - } else if (ct >= '0' && ct <= '9') { - ct = ct - '0'; - } else if (ct >= 'a' && ct <= 'f') { - ct = (ct - 'a') + 10; - } else if (ct >= 'A' && ct <= 'F') { - ct = (ct - 'A') + 10; - } else { - continue; + bool overflow = false; + if (p_src[2] == 'x') { + // Hex entity &#x<num>; + for (int i = 3; i < p_src_len; i++) { + eat = i + 1; + char32_t ct = p_src[i]; + if (ct == ';') { + break; + } else if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = (ct - 'a') + 10; + } else if (ct >= 'A' && ct <= 'F') { + ct = (ct - 'A') + 10; + } else { + break; + } + if (c > (UINT32_MAX >> 4)) { + overflow = true; + break; + } + c <<= 4; + c |= ct; + } + } else { + // Decimal entity &#<num>; + for (int i = 2; i < p_src_len; i++) { + eat = i + 1; + char32_t ct = p_src[i]; + if (ct == ';' || ct < '0' || ct > '9') { + break; + } + } + if (p_src[eat - 1] == ';') { + int64_t val = String::to_int(p_src + 2, eat - 3); + if (val > 0 && val <= UINT32_MAX) { + c = (char32_t)val; + } else { + overflow = true; + } } - c <<= 4; - c |= ct; } + // Value must be non-zero, in the range of char32_t, + // actually end with ';'. If invalid, leave the entity as-is + if (c == '\0' || overflow || p_src[eat - 1] != ';') { + eat = 1; + c = *p_src; + } if (p_dst) { *p_dst = c; } diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index 525d9e77cb..c985593473 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -142,7 +142,7 @@ public: _FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; } _FORCE_INLINE_ void set(int p_index, const T &p_elem) { - CRASH_BAD_INDEX(p_index, size()); + ERR_FAIL_INDEX(p_index, size()); _copy_on_write(); _get_data()[p_index] = p_elem; } diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 9d8e262cc9..490bd45b7b 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -88,6 +88,7 @@ VARIANT_ENUM_CAST(Vector3::Axis); VARIANT_ENUM_CAST(Error); VARIANT_ENUM_CAST(Side); +VARIANT_ENUM_CAST(ClockDirection); VARIANT_ENUM_CAST(Corner); VARIANT_ENUM_CAST(Orientation); VARIANT_ENUM_CAST(HAlign); diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index a02f077cf7..9711a2a35b 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -187,7 +187,7 @@ </description> </signal> <signal name="body_entered"> - <argument index="0" name="body" type="Node"> + <argument index="0" name="body" type="Node2D"> </argument> <description> Emitted when a [PhysicsBody2D] or [TileMap] enters this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. @@ -195,7 +195,7 @@ </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Node"> + <argument index="0" name="body" type="Node2D"> </argument> <description> Emitted when a [PhysicsBody2D] or [TileMap] exits this Area2D. Requires [member monitoring] to be set to [code]true[/code]. [TileMap]s are detected if the [TileSet] has Collision [Shape2D]s. @@ -205,7 +205,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Node"> + <argument index="1" name="body" type="Node2D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -222,7 +222,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Node"> + <argument index="1" name="body" type="Node2D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index bd43d619dd..4271769155 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -197,7 +197,7 @@ </description> </signal> <signal name="body_entered"> - <argument index="0" name="body" type="Node"> + <argument index="0" name="body" type="Node3D"> </argument> <description> Emitted when a [PhysicsBody3D] or [GridMap] enters this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. @@ -205,7 +205,7 @@ </description> </signal> <signal name="body_exited"> - <argument index="0" name="body" type="Node"> + <argument index="0" name="body" type="Node3D"> </argument> <description> Emitted when a [PhysicsBody3D] or [GridMap] exits this Area3D. Requires [member monitoring] to be set to [code]true[/code]. [GridMap]s are detected if the [MeshLibrary] has Collision [Shape3D]s. @@ -215,7 +215,7 @@ <signal name="body_shape_entered"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Node"> + <argument index="1" name="body" type="Node3D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> @@ -232,7 +232,7 @@ <signal name="body_shape_exited"> <argument index="0" name="body_id" type="int"> </argument> - <argument index="1" name="body" type="Node"> + <argument index="1" name="body" type="Node3D"> </argument> <argument index="2" name="body_shape" type="int"> </argument> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index e5285587eb..c5e820e9fe 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -869,7 +869,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. + Shows the given control at the mouse pointer. A good time to call this method is in [method get_drag_data]. The control must not be in the scene tree. You should not free the control, and you should not keep a reference to the control beyond the duration of the drag. It will be deleted automatically after the drag has ended. [codeblocks] [gdscript] export (Color, RGBA) var color = Color(1, 0, 0, 1) diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml index 7e99510124..6c54af05cd 100644 --- a/doc/classes/FontData.xml +++ b/doc/classes/FontData.xml @@ -11,6 +11,45 @@ <tutorials> </tutorials> <methods> + <method name="bitmap_add_char"> + <return type="void"> + </return> + <argument index="0" name="char" type="int"> + </argument> + <argument index="1" name="texture_idx" type="int"> + </argument> + <argument index="2" name="rect" type="Rect2"> + </argument> + <argument index="3" name="align" type="Vector2"> + </argument> + <argument index="4" name="advance" type="float"> + </argument> + <description> + Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance. + </description> + </method> + <method name="bitmap_add_kerning_pair"> + <return type="void"> + </return> + <argument index="0" name="A" type="int"> + </argument> + <argument index="1" name="B" type="int"> + </argument> + <argument index="2" name="kerning" type="int"> + </argument> + <description> + Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character. + </description> + </method> + <method name="bitmap_add_texture"> + <return type="void"> + </return> + <argument index="0" name="texture" type="Texture"> + </argument> + <description> + Adds a texture to the bitmap font. + </description> + </method> <method name="draw_glyph" qualifiers="const"> <return type="Vector2"> </return> @@ -265,6 +304,19 @@ Note: For non-scalable fonts [code]base_size[/code] is ignored, use [method get_base_size] to check actual font size. </description> </method> + <method name="new_bitmap"> + <return type="void"> + </return> + <argument index="0" name="height" type="float"> + </argument> + <argument index="1" name="ascent" type="float"> + </argument> + <argument index="2" name="base_size" type="int"> + </argument> + <description> + Creates new, empty bitmap font. + </description> + </method> <method name="remove_language_support_override"> <return type="void"> </return> diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index f6602d5f4d..057a2b8d1a 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -207,10 +207,11 @@ <method name="get_environment" qualifiers="const"> <return type="String"> </return> - <argument index="0" name="environment" type="String"> + <argument index="0" name="variable" type="String"> </argument> <description> - Returns an environment variable. + Returns the value of an environment variable. Returns an empty string if the environment variable doesn't exist. + [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows. </description> </method> <method name="get_executable_path" qualifiers="const"> @@ -383,10 +384,11 @@ <method name="has_environment" qualifiers="const"> <return type="bool"> </return> - <argument index="0" name="environment" type="String"> + <argument index="0" name="variable" type="String"> </argument> <description> - Returns [code]true[/code] if an environment variable exists. + Returns [code]true[/code] if the environment variable with the name [code]variable[/code] exists. + [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows. </description> </method> <method name="has_feature" qualifiers="const"> @@ -501,6 +503,18 @@ [b]Note:[/b] This method is implemented on Android. </description> </method> + <method name="set_environment" qualifiers="const"> + <return type="bool"> + </return> + <argument index="0" name="variable" type="String"> + </argument> + <argument index="1" name="value" type="String"> + </argument> + <description> + Sets the value of the environment variable [code]variable[/code] to [code]value[/code]. The environment variable will be set for the Godot process and any process executed with [method execute] after running [method set_environment]. The environment variable will [i]not[/i] persist to processes run after the Godot process was terminated. + [b]Note:[/b] Double-check the casing of [code]variable[/code]. Environment variable names are case-sensitive on all platforms except Windows. + </description> + </method> <method name="set_thread_name"> <return type="int" enum="Error"> </return> @@ -535,10 +549,6 @@ </method> </methods> <members> - <member name="exit_code" type="int" setter="set_exit_code" getter="get_exit_code" default="0"> - The exit code passed to the OS when the main loop exits. By convention, an exit code of [code]0[/code] indicates success whereas a non-zero exit code indicates an error. For portability reasons, the exit code should be set between 0 and 125 (inclusive). - [b]Note:[/b] This value will be ignored if using [method SceneTree.quit] with an [code]exit_code[/code] argument passed. - </member> <member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false"> If [code]true[/code], the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile. </member> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index b056eff658..b68ca2ac67 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -460,6 +460,9 @@ <member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color( 1, 0.2, 0.1, 0.8 )"> Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu. </member> + <member name="debug/shapes/collision/draw_2d_outlines" type="bool" setter="" getter="" default="true"> + Sets whether 2D physics will display collision outlines in game when "Visible Collision Shapes" is enabled in the Debug menu. + </member> <member name="debug/shapes/collision/max_contacts_displayed" type="int" setter="" getter="" default="10000"> Maximum number of contact points between collision shapes to display when "Visible Collision Shapes" is enabled in the Debug menu. </member> diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml index f65d013bfc..c54e2f4b88 100644 --- a/doc/classes/SceneTree.xml +++ b/doc/classes/SceneTree.xml @@ -181,10 +181,12 @@ <method name="quit"> <return type="void"> </return> - <argument index="0" name="exit_code" type="int" default="-1"> + <argument index="0" name="exit_code" type="int" default="0"> </argument> <description> - Quits the application at the end of the current iteration. A process [code]exit_code[/code] can optionally be passed as an argument. If this argument is [code]0[/code] or greater, it will override the [member OS.exit_code] defined before quitting the application. + Quits the application at the end of the current iteration. Argument [code]exit_code[/code] can optionally be given (defaulting to 0) to customize the exit status code. + By convention, an exit code of [code]0[/code] indicates success whereas a non-zero exit code indicates an error. + For portability reasons, the exit code should be set between 0 and 125 (inclusive). </description> </method> <method name="reload_current_scene"> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 79ab6e28e7..5635ec2be0 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -9,6 +9,19 @@ <tutorials> </tutorials> <methods> + <method name="create_font_bitmap"> + <return type="RID"> + </return> + <argument index="0" name="height" type="float"> + </argument> + <argument index="1" name="ascent" type="float"> + </argument> + <argument index="2" name="base_size" type="int"> + </argument> + <description> + Creates new, empty bitmap font. To free the resulting font, use [method free_rid] method. + </description> + </method> <method name="create_font_memory"> <return type="RID"> </return> @@ -78,6 +91,51 @@ Draws box displaying character hexadecimal code. Used for replacing missing characters. </description> </method> + <method name="font_bitmap_add_char"> + <return type="void"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <argument index="1" name="char" type="int"> + </argument> + <argument index="2" name="texture_idx" type="int"> + </argument> + <argument index="3" name="rect" type="Rect2"> + </argument> + <argument index="4" name="align" type="Vector2"> + </argument> + <argument index="5" name="advance" type="float"> + </argument> + <description> + Adds a character to the font, where [code]character[/code] is the Unicode value, [code]texture[/code] is the texture index, [code]rect[/code] is the region in the texture (in pixels!), [code]align[/code] is the (optional) alignment for the character and [code]advance[/code] is the (optional) advance. + </description> + </method> + <method name="font_bitmap_add_kerning_pair"> + <return type="void"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <argument index="1" name="A" type="int"> + </argument> + <argument index="2" name="B" type="int"> + </argument> + <argument index="3" name="kerning" type="int"> + </argument> + <description> + Adds a kerning pair to the bitmap font as a difference. Kerning pairs are special cases where a typeface advance is determined by the next character. + </description> + </method> + <method name="font_bitmap_add_texture"> + <return type="void"> + </return> + <argument index="0" name="font" type="RID"> + </argument> + <argument index="1" name="texture" type="Texture"> + </argument> + <description> + Adds a texture to the bitmap font. + </description> + </method> <method name="font_draw_glyph" qualifiers="const"> <return type="Vector2"> </return> diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 3a63100012..a9ed1bc2b9 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -236,7 +236,10 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String bool can_instance = (p_cpp_type && ClassDB::can_instance(p_type)) || !p_cpp_type; if (!can_instance) { r_item->set_custom_color(0, search_options->get_theme_color("disabled_font_color", "Editor")); + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, "NodeDisabled")); r_item->set_selectable(0, false); + } else { + r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); } if (search_box->get_text() != "") { @@ -253,7 +256,6 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const String const String &description = DTR(EditorHelp::get_doc_data()->class_list[p_type].brief_description); r_item->set_tooltip(0, description); - r_item->set_icon(0, EditorNode::get_singleton()->get_class_icon(p_type, icon_fallback)); if (!p_cpp_type && !script_type) { Ref<Texture2D> icon = EditorNode::get_editor_data().get_custom_types()[custom_type_parents[p_type]][custom_type_indices[p_type]].icon; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 949306de42..4f60258d95 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -507,6 +507,11 @@ void EditorExportPlatform::_edit_files_with_filter(DirAccess *da, const Vector<S if (dir.begins_with(".")) { continue; } + + if (EditorFileSystem::_should_skip_directory(cur_dir + dir)) { + continue; + } + da->change_dir(dir); _edit_files_with_filter(da, p_filters, r_list, exclude); da->change_dir(".."); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 3c6649a66a..dce022e86e 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -669,10 +669,7 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess continue; } - if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) { // skip if another project inside this - continue; - } - if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) { // skip if another project inside this + if (_should_skip_directory(cd.plus_file(f))) { continue; } @@ -874,10 +871,7 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const int idx = p_dir->find_dir_index(f); if (idx == -1) { - if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) { // skip if another project inside this - continue; - } - if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) { // skip if another project inside this + if (_should_skip_directory(cd.plus_file(f))) { continue; } @@ -1979,6 +1973,20 @@ Error EditorFileSystem::_resource_import(const String &p_path) { return OK; } +bool EditorFileSystem::_should_skip_directory(const String &p_path) { + if (FileAccess::exists(p_path.plus_file("project.godot"))) { + // skip if another project inside this + return true; + } + + if (FileAccess::exists(p_path.plus_file(".gdignore"))) { + // skip if a `.gdignore` file is inside this + return true; + } + + return false; +} + bool EditorFileSystem::is_group_file(const String &p_path) const { return group_file_cache.has(p_path); } diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index dec2330256..59bde238a8 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -262,6 +262,8 @@ public: bool is_group_file(const String &p_path) const; void move_group_file(const String &p_path, const String &p_new_path); + static bool _should_skip_directory(const String &p_path); + EditorFileSystem(); ~EditorFileSystem(); }; diff --git a/editor/icons/NodeDisabled.svg b/editor/icons/NodeDisabled.svg new file mode 100644 index 0000000000..b2d51fc4fb --- /dev/null +++ b/editor/icons/NodeDisabled.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 2a6 6 0 0 0 -6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0 -6-6zm0 2a4 4 0 0 1 4 4 4 4 0 0 1 -4 4 4 4 0 0 1 -4-4 4 4 0 0 1 4-4z" fill="#999"/></svg> diff --git a/editor/import_defaults_editor.cpp b/editor/import_defaults_editor.cpp new file mode 100644 index 0000000000..43b97eb910 --- /dev/null +++ b/editor/import_defaults_editor.cpp @@ -0,0 +1,216 @@ +/*************************************************************************/ +/* import_defaults_editor.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "import_defaults_editor.h" + +class ImportDefaultsEditorSettings : public Object { + GDCLASS(ImportDefaultsEditorSettings, Object) + friend class ImportDefaultsEditor; + List<PropertyInfo> properties; + Map<StringName, Variant> values; + Map<StringName, Variant> default_values; + + Ref<ResourceImporter> importer; + +protected: + bool _set(const StringName &p_name, const Variant &p_value) { + if (values.has(p_name)) { + values[p_name] = p_value; + return true; + } else { + return false; + } + } + bool _get(const StringName &p_name, Variant &r_ret) const { + if (values.has(p_name)) { + r_ret = values[p_name]; + return true; + } else { + r_ret = Variant(); + return false; + } + } + void _get_property_list(List<PropertyInfo> *p_list) const { + if (importer.is_null()) { + return; + } + for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + if (importer->get_option_visibility(E->get().name, values)) { + p_list->push_back(E->get()); + } + } + } +}; + +void ImportDefaultsEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_PREDELETE) { + inspector->edit(nullptr); + } +} + +void ImportDefaultsEditor::_reset() { + if (settings->importer.is_valid()) { + settings->values = settings->default_values; + settings->notify_property_list_changed(); + } +} + +void ImportDefaultsEditor::_save() { + if (settings->importer.is_valid()) { + Dictionary modified; + + for (Map<StringName, Variant>::Element *E = settings->values.front(); E; E = E->next()) { + if (E->get() != settings->default_values[E->key()]) { + modified[E->key()] = E->get(); + } + } + + if (modified.size()) { + ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), modified); + } else { + ProjectSettings::get_singleton()->set("importer_defaults/" + settings->importer->get_importer_name(), Variant()); + } + + emit_signal("project_settings_changed"); + } +} + +void ImportDefaultsEditor::_update_importer() { + List<Ref<ResourceImporter>> importer_list; + ResourceFormatImporter::get_singleton()->get_importers(&importer_list); + Ref<ResourceImporter> importer; + for (List<Ref<ResourceImporter>>::Element *E = importer_list.front(); E; E = E->next()) { + if (E->get()->get_visible_name() == importers->get_item_text(importers->get_selected())) { + importer = E->get(); + break; + } + } + + settings->properties.clear(); + settings->values.clear(); + settings->importer = importer; + + if (importer.is_valid()) { + List<ResourceImporter::ImportOption> options; + importer->get_import_options(&options); + Dictionary d; + if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + importer->get_importer_name())) { + d = ProjectSettings::get_singleton()->get("importer_defaults/" + importer->get_importer_name()); + } + + for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { + settings->properties.push_back(E->get().option); + if (d.has(E->get().option.name)) { + settings->values[E->get().option.name] = d[E->get().option.name]; + } else { + settings->values[E->get().option.name] = E->get().default_value; + } + settings->default_values[E->get().option.name] = E->get().default_value; + } + + save_defaults->set_disabled(false); + reset_defaults->set_disabled(false); + + } else { + save_defaults->set_disabled(true); + reset_defaults->set_disabled(true); + } + + settings->notify_property_list_changed(); + + inspector->edit(settings); +} + +void ImportDefaultsEditor::_importer_selected(int p_index) { + _update_importer(); +} + +void ImportDefaultsEditor::clear() { + String last_selected; + if (importers->get_selected() > 0) { + last_selected = importers->get_item_text(importers->get_selected()); + } + + importers->clear(); + + importers->add_item("<" + TTR("Select Importer") + ">"); + importers->set_item_disabled(0, true); + + List<Ref<ResourceImporter>> importer_list; + ResourceFormatImporter::get_singleton()->get_importers(&importer_list); + Vector<String> names; + for (List<Ref<ResourceImporter>>::Element *E = importer_list.front(); E; E = E->next()) { + String vn = E->get()->get_visible_name(); + names.push_back(vn); + } + names.sort(); + + for (int i = 0; i < names.size(); i++) { + importers->add_item(names[i]); + + if (names[i] == last_selected) { + importers->select(i + 1); + } + } +} + +void ImportDefaultsEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("project_settings_changed")); +} + +ImportDefaultsEditor::ImportDefaultsEditor() { + HBoxContainer *hb = memnew(HBoxContainer); + hb->add_child(memnew(Label(TTR("Importer:")))); + importers = memnew(OptionButton); + hb->add_child(importers); + hb->add_spacer(); + importers->connect("item_selected", callable_mp(this, &ImportDefaultsEditor::_importer_selected)); + reset_defaults = memnew(Button); + reset_defaults->set_text(TTR("Reset to Defaults")); + reset_defaults->set_disabled(true); + reset_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_reset)); + hb->add_child(reset_defaults); + add_child(hb); + inspector = memnew(EditorInspector); + add_child(inspector); + inspector->set_v_size_flags(SIZE_EXPAND_FILL); + CenterContainer *cc = memnew(CenterContainer); + save_defaults = memnew(Button); + save_defaults->set_text(TTR("Save")); + save_defaults->connect("pressed", callable_mp(this, &ImportDefaultsEditor::_save)); + cc->add_child(save_defaults); + add_child(cc); + + settings = memnew(ImportDefaultsEditorSettings); +} + +ImportDefaultsEditor::~ImportDefaultsEditor() { + memdelete(settings); +} diff --git a/editor/import_defaults_editor.h b/editor/import_defaults_editor.h new file mode 100644 index 0000000000..c1becac5e9 --- /dev/null +++ b/editor/import_defaults_editor.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* import_defaults_editor.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IMPORT_DEFAULTS_EDITOR_H +#define IMPORT_DEFAULTS_EDITOR_H + +#include "core/object/undo_redo.h" +#include "editor/action_map_editor.h" +#include "editor/editor_data.h" +#include "editor/editor_plugin_settings.h" +#include "editor/editor_sectioned_inspector.h" +#include "editor/localization_editor.h" +#include "editor/shader_globals_editor.h" +#include "editor_autoload_settings.h" +#include "scene/gui/center_container.h" +#include "scene/gui/option_button.h" + +class ImportDefaultsEditorSettings; + +class ImportDefaultsEditor : public VBoxContainer { + GDCLASS(ImportDefaultsEditor, VBoxContainer) + + OptionButton *importers; + Button *save_defaults; + Button *reset_defaults; + + EditorInspector *inspector; + + ImportDefaultsEditorSettings *settings; + + void _update_importer(); + void _importer_selected(int p_index); + + void _reset(); + void _save(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void clear(); + + ImportDefaultsEditor(); + ~ImportDefaultsEditor(); +}; + +#endif // IMPORT_DEFAULTS_EDITOR_H diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp index 9e2fb01bb8..e6b99fa63f 100644 --- a/editor/node_3d_editor_gizmos.cpp +++ b/editor/node_3d_editor_gizmos.cpp @@ -3505,6 +3505,57 @@ void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { //// +CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<CollisionObject3D>(p_spatial) != nullptr; +} + +String CollisionObject3DGizmoPlugin::get_gizmo_name() const { + return "CollisionObject3D"; +} + +int CollisionObject3DGizmoPlugin::get_priority() const { + return -1; +} + +void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + List<uint32_t> owners; + co->get_shape_owners(&owners); + for (List<uint32_t>::Element *E = owners.front(); E; E = E->next()) { + uint32_t owner_id = E->get(); + Transform xform = co->shape_owner_get_transform(owner_id); + Object *owner = co->shape_owner_get_owner(owner_id); + // Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo. + if (!Object::cast_to<CollisionShape3D>(owner) && !Object::cast_to<CollisionPolygon3D>(owner)) { + Ref<Material> material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo); + for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) { + Ref<Shape3D> s = co->shape_owner_get_shape(owner_id, shape_id); + if (s.is_null()) { + continue; + } + SurfaceTool st; + st.append_from(s->get_debug_mesh(), 0, xform); + + p_gizmo->add_mesh(st.commit(), false, Ref<SkinReference>(), material); + p_gizmo->add_collision_segments(s->get_debug_mesh_lines()); + } + } + } +} + +//// + CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() { const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h index df4ed15a8e..6f98d3a08c 100644 --- a/editor/node_3d_editor_gizmos.h +++ b/editor/node_3d_editor_gizmos.h @@ -355,6 +355,18 @@ public: LightmapProbeGizmoPlugin(); }; +class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + CollisionObject3DGizmoPlugin(); +}; + class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 1d3835add9..66c4890c45 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -6243,12 +6243,14 @@ void Node3DEditor::_notification(int p_what) { sun_button->set_icon(get_theme_icon("DirectionalLight3D", "EditorIcons")); environ_button->set_icon(get_theme_icon("WorldEnvironment", "EditorIcons")); - sun_environ_settings->set_icon(get_theme_icon("GuiTabMenu", "EditorIcons")); + sun_environ_settings->set_icon(get_theme_icon("GuiTabMenuHl", "EditorIcons")); _update_preview_environment(); sun_title->add_theme_font_override("font", get_theme_font("title_font", "Window")); environ_title->add_theme_font_override("font", get_theme_font("title_font", "Window")); + sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size()); + environ_state->set_custom_minimum_size(environ_vb->get_combined_minimum_size()); } else if (p_what == NOTIFICATION_ENTER_TREE) { _register_all_gizmos(); _update_gizmos_menu(); @@ -6450,6 +6452,7 @@ void Node3DEditor::_register_all_gizmos() { add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin))); add_gizmo_plugin(Ref<BakedLightmapGizmoPlugin>(memnew(BakedLightmapGizmoPlugin))); add_gizmo_plugin(Ref<LightmapProbeGizmoPlugin>(memnew(LightmapProbeGizmoPlugin))); + add_gizmo_plugin(Ref<CollisionObject3DGizmoPlugin>(memnew(CollisionObject3DGizmoPlugin))); add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin))); add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin))); add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin))); @@ -6563,9 +6566,9 @@ void Node3DEditor::_update_preview_environment() { } if (directional_light_count > 0) { - sun_state->set_text(TTR("Scene contains\nDirectionalLight3D.\nPreview Disabled.")); + sun_state->set_text(TTR("Scene contains\nDirectionalLight3D.\nPreview disabled.")); } else { - sun_state->set_text(TTR("Preview Disabled.")); + sun_state->set_text(TTR("Preview disabled.")); } } else { @@ -6587,9 +6590,9 @@ void Node3DEditor::_update_preview_environment() { environ_vb->hide(); } if (world_env_count > 0) { - environ_state->set_text(TTR("Scene contains\nWorldEnvironment.\nPreview Disabled.")); + environ_state->set_text(TTR("Scene contains\nWorldEnvironment.\nPreview disabled.")); } else { - environ_state->set_text(TTR("Preview Disabled.")); + environ_state->set_text(TTR("Preview disabled.")); } } else { @@ -7027,10 +7030,6 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { sun_title->set_text(TTR("Preview Sun")); sun_title->set_align(Label::ALIGN_CENTER); - sun_state = memnew(Label); - sun_environ_hb->add_child(sun_state); - sun_state->show(); - CenterContainer *sun_direction_center = memnew(CenterContainer); sun_direction = memnew(Control); sun_direction->set_custom_minimum_size(Size2i(128, 128) * EDSCALE); @@ -7071,6 +7070,12 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { sun_vb->add_spacer(); sun_vb->add_child(sun_add_to_scene); + sun_state = memnew(Label); + sun_environ_hb->add_child(sun_state); + sun_state->set_align(Label::ALIGN_CENTER); + sun_state->set_valign(Label::VALIGN_CENTER); + sun_state->set_h_size_flags(SIZE_EXPAND_FILL); + VSeparator *sc = memnew(VSeparator); sc->set_custom_minimum_size(Size2(50 * EDSCALE, 0)); sc->set_v_size_flags(SIZE_EXPAND_FILL); @@ -7078,13 +7083,8 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { environ_vb = memnew(VBoxContainer); sun_environ_hb->add_child(environ_vb); - environ_vb->hide(); - environ_vb->set_custom_minimum_size(Size2(200 * EDSCALE, 0)); - - environ_state = memnew(Label); - sun_environ_hb->add_child(environ_state); - environ_state->show(); + environ_vb->hide(); environ_title = memnew(Label); environ_vb->add_child(environ_title); @@ -7134,6 +7134,12 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { environ_vb->add_spacer(); environ_vb->add_child(environ_add_to_scene); + environ_state = memnew(Label); + sun_environ_hb->add_child(environ_state); + environ_state->set_align(Label::ALIGN_CENTER); + environ_state->set_valign(Label::VALIGN_CENTER); + environ_state->set_h_size_flags(SIZE_EXPAND_FILL); + preview_sun = memnew(DirectionalLight3D); preview_sun->set_shadow(true); preview_sun->set_shadow_mode(DirectionalLight3D::SHADOW_PARALLEL_4_SPLITS); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 6e2cd72796..3960d155b6 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -52,6 +52,7 @@ void ProjectSettingsEditor::popup_project_settings() { localization_editor->update_translations(); autoload_settings->update_autoload(); plugin_settings->update_plugins(); + import_defaults_editor->clear(); } void ProjectSettingsEditor::queue_save() { @@ -648,7 +649,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { action_map->connect("action_removed", callable_mp(this, &ProjectSettingsEditor::_action_removed)); action_map->connect("action_renamed", callable_mp(this, &ProjectSettingsEditor::_action_renamed)); action_map->connect("action_reordered", callable_mp(this, &ProjectSettingsEditor::_action_reordered)); - action_map->set_toggle_editable_label(TTR("Show built-in Actions")); + action_map->set_toggle_editable_label(TTR("Show Built-in Actions")); action_map->set_show_uneditable(false); tab_container->add_child(action_map); @@ -692,4 +693,9 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { } inspector->set_restrict_to_basic_settings(!use_advanced); + + import_defaults_editor = memnew(ImportDefaultsEditor); + import_defaults_editor->set_name(TTR("Import Defaults")); + tab_container->add_child(import_defaults_editor); + import_defaults_editor->connect("project_settings_changed", callable_mp(this, &ProjectSettingsEditor::queue_save)); } diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index c28785bb27..cde46ac4c4 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -36,6 +36,7 @@ #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" #include "editor/editor_sectioned_inspector.h" +#include "editor/import_defaults_editor.h" #include "editor/localization_editor.h" #include "editor/shader_globals_editor.h" #include "editor_autoload_settings.h" @@ -75,6 +76,7 @@ class ProjectSettingsEditor : public AcceptDialog { PanelContainer *restart_container; Button *restart_close_button; + ImportDefaultsEditor *import_defaults_editor; EditorData *data; UndoRedo *undo_redo; diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index a61b4aa3b9..ebef5be9ed 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -483,8 +483,5 @@ ShaderGlobalsEditor::ShaderGlobalsEditor() { } ShaderGlobalsEditor::~ShaderGlobalsEditor() { - if (is_visible_in_tree()) { - inspector->edit(nullptr); - } memdelete(interface); } diff --git a/main/main.cpp b/main/main.cpp index ce9d8342c2..e9b06f6b07 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1992,7 +1992,7 @@ bool Main::start() { if (check_only) { if (!script_res->is_valid()) { - OS::get_singleton()->set_exit_code(1); + OS::get_singleton()->set_exit_code(EXIT_FAILURE); } return false; } @@ -2576,8 +2576,10 @@ void Main::force_redraw() { * so that the engine closes cleanly without leaking memory or crashing. * The order matters as some of those steps are linked with each other. */ -void Main::cleanup() { - ERR_FAIL_COND(!_start_success); +void Main::cleanup(bool p_force) { + if (!p_force) { + ERR_FAIL_COND(!_start_success); + } EngineDebugger::deinitialize(); diff --git a/main/main.h b/main/main.h index 9e606c188d..f4fff6b97e 100644 --- a/main/main.h +++ b/main/main.h @@ -59,7 +59,7 @@ public: static bool is_iterating(); - static void cleanup(); + static void cleanup(bool p_force = false); }; // Test main override is for the testing behaviour. diff --git a/methods.py b/methods.py index 45cfe00959..9b29eadc16 100644 --- a/methods.py +++ b/methods.py @@ -6,9 +6,11 @@ from collections import OrderedDict # We need to define our own `Action` method to control the verbosity of output # and whenever we need to run those commands in a subprocess on some platforms. -from SCons.Script import Action from SCons import Node +from SCons.Script import Action +from SCons.Script import ARGUMENTS from SCons.Script import Glob +from SCons.Variables.BoolVariable import _text2bool from platform_methods import run_in_subprocess @@ -145,6 +147,17 @@ def parse_cg_file(fname, uniforms, sizes, conditionals): fs.close() +def get_cmdline_bool(option, default): + """We use `ARGUMENTS.get()` to check if options were manually overridden on the command line, + and SCons' _text2bool helper to convert them to booleans, otherwise they're handled as strings. + """ + cmdline_val = ARGUMENTS.get(option) + if cmdline_val is not None: + return _text2bool(cmdline_val) + else: + return default + + def detect_modules(search_path, recursive=False): """Detects and collects a list of C++ modules at specified path diff --git a/misc/dist/html/editor.html b/misc/dist/html/editor.html index b4a8c69cc6..b0b906270b 100644 --- a/misc/dist/html/editor.html +++ b/misc/dist/html/editor.html @@ -5,8 +5,7 @@ <meta name='viewport' content='width=device-width, user-scalable=no' /> <link id='-gd-engine-icon' rel='icon' type='image/png' href='favicon.png' /> <title>Godot Engine Web Editor ($GODOT_VERSION)</title> - <style type='text/css'> - + <style> *:focus { /* More visible outline for better keyboard navigation. */ outline: 0.125rem solid hsl(220, 100%, 62.5%); @@ -40,13 +39,30 @@ filter: brightness(82.5%); } + #tabs-buttons { + /* Match the default background color of the editor window for a seamless appearance. */ + background-color: #202531; + } + + #tab-game { + /* Use a pure black background to better distinguish the running project */ + /* from the editor window, and to use a more neutral background color (no tint). */ + background-color: black; + /* Make the background span the entire page height. */ + min-height: 100vh; + } + #canvas, #gameCanvas { display: block; margin: 0; color: white; } - #canvas:focus, #gameCanvas:focus { + /* Don't show distracting focus outlines for the main tabs' contents. */ + #tab-editor canvas:focus, + #tab-game canvas:focus, + #canvas:focus, + #gameCanvas:focus { outline: none; } @@ -200,9 +216,11 @@ <a href="demo.zip">(Try this for example)</a> <br /> <br /> - <button id="startButton" class="btn" style="margin-bottom: 4rem">Start Godot editor</button> + <button id="startButton" class="btn" style="margin-bottom: 4rem; font-weight: 700">Start Godot editor</button> + <br /> + <button class="btn" onclick="clearPersistence()" style="margin-bottom: 1.5rem">Clear persistent data</button> <br /> - <button class="btn" onclick="clearPersistence()">Clear persistent data</button> + <a href="https://docs.godotengine.org/en/latest/tutorials/editor/using_the_web_editor.html">Web editor documentation</a> </div> </div> <div id='tab-editor' style="display: none;"> @@ -233,8 +251,8 @@ </div> </div> - <script type='text/javascript' src='godot.tools.js'></script> - <script type='text/javascript'>//<![CDATA[ + <script src='godot.tools.js'></script> + <script>//<![CDATA[ var editor = null; var game = null; @@ -258,7 +276,7 @@ }); } - if (!window.confirm("Are you sure you want to delete all the locally stored files?")) { + if (!window.confirm("Are you sure you want to delete all the locally stored files?\nClicking \"OK\" will permanently remove your projects and editor settings!")) { return; } Promise.all([ diff --git a/misc/dist/linux/org.godotengine.Godot.xml b/misc/dist/linux/org.godotengine.Godot.xml new file mode 100644 index 0000000000..2f647f71a6 --- /dev/null +++ b/misc/dist/linux/org.godotengine.Godot.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-godot-project"> + <comment>Godot Engine project</comment> + <icon name="x-godot-project" /> + <glob pattern="*.godot"/> + </mime-type> + + <mime-type type="application/x-godot-resource"> + <comment>Godot Engine resource</comment> + <icon name="x-godot-resource" /> + <glob pattern="*.res"/> + <glob pattern="*.tres"/> + </mime-type> + + <mime-type type="application/x-godot-scene"> + <comment>Godot Engine scene</comment> + <icon name="x-godot-scene" /> + <glob pattern="*.scn"/> + <glob pattern="*.tscn"/> + <glob pattern="*.escn"/> + </mime-type> + + <mime-type type="application/x-gdscript"> + <comment>GDScript script</comment> + <icon name="x-gdscript" /> + <glob pattern="*.gd"/> + </mime-type> +</mime-info> diff --git a/misc/dist/linux/x-godot-project.xml b/misc/dist/linux/x-godot-project.xml deleted file mode 100644 index 9f28bab2ae..0000000000 --- a/misc/dist/linux/x-godot-project.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0"?> -<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> - <mime-type type="application/x-godot-project"> - <comment>Godot Engine project</comment> - <icon name="godot" /> - <glob pattern="*.godot" weight="100" /> - </mime-type> -</mime-info> diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h index ef60633a3d..86fc745134 100644 --- a/modules/gdnative/include/text/godot_text.h +++ b/modules/gdnative/include/text/godot_text.h @@ -74,6 +74,10 @@ typedef struct { godot_rid (*create_font_system)(void *, const godot_string *, int); godot_rid (*create_font_resource)(void *, const godot_string *, int); godot_rid (*create_font_memory)(void *, const uint8_t *, size_t, godot_string *, int); + godot_rid (*create_font_bitmap)(void *, float, float, int); + void (*font_bitmap_add_texture)(void *, godot_rid *, const godot_object *); + void (*font_bitmap_add_char)(void *, godot_rid *, char32_t, int, const godot_rect2 *, const godot_vector2 *, float); + void (*font_bitmap_add_kerning_pair)(void *, godot_rid *, char32_t, char32_t, int); float (*font_get_height)(void *, godot_rid *, int); float (*font_get_ascent)(void *, godot_rid *, int); float (*font_get_descent)(void *, godot_rid *, int); diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp index 7584568dd8..7cd8de5f2e 100644 --- a/modules/gdnative/text/text_server_gdnative.cpp +++ b/modules/gdnative/text/text_server_gdnative.cpp @@ -113,6 +113,28 @@ RID TextServerGDNative::create_font_memory(const uint8_t *p_data, size_t p_size, return rid; } +RID TextServerGDNative::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { + ERR_FAIL_COND_V(interface == nullptr, RID()); + godot_rid result = interface->create_font_bitmap(data, p_height, p_ascent, p_base_size); + RID rid = *(RID *)&result; + return rid; +} + +void TextServerGDNative::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { + ERR_FAIL_COND(interface == nullptr); + interface->font_bitmap_add_texture(data, (godot_rid *)&p_font, (const godot_object *)p_texture.ptr()); +} + +void TextServerGDNative::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + ERR_FAIL_COND(interface == nullptr); + interface->font_bitmap_add_char(data, (godot_rid *)&p_font, p_char, p_texture_idx, (const godot_rect2 *)&p_rect, (const godot_vector2 *)&p_align, p_advance); +} + +void TextServerGDNative::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { + ERR_FAIL_COND(interface == nullptr); + interface->font_bitmap_add_kerning_pair(data, (godot_rid *)&p_font, p_A, p_B, p_kerning); +} + float TextServerGDNative::font_get_height(RID p_font, int p_size) const { ERR_FAIL_COND_V(interface == nullptr, 0.f); return interface->font_get_height(data, (godot_rid *)&p_font, p_size); diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h index 9783b65431..931bb44885 100644 --- a/modules/gdnative/text/text_server_gdnative.h +++ b/modules/gdnative/text/text_server_gdnative.h @@ -64,6 +64,11 @@ public: virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; + virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + + virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; + virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; + virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; virtual float font_get_height(RID p_font, int p_size) const override; virtual float font_get_ascent(RID p_font, int p_size) const override; diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp index 96744a15d7..6635098be2 100644 --- a/modules/gdscript/language_server/lsp.hpp +++ b/modules/gdscript/language_server/lsp.hpp @@ -1661,7 +1661,7 @@ struct ServerCapabilities { signatureHelpProvider.triggerCharacters.push_back(","); signatureHelpProvider.triggerCharacters.push_back("("); dict["signatureHelpProvider"] = signatureHelpProvider.to_json(); - dict["codeLensProvider"] = false; // codeLensProvider.to_json(); + //dict["codeLensProvider"] = codeLensProvider.to_json(); dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json(); dict["renameProvider"] = renameProvider.to_json(); dict["documentLinkProvider"] = documentLinkProvider.to_json(); diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 134aadb75e..e28cc57f9b 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -41,6 +41,7 @@ <return type="Array"> </return> <description> + Returns an array of [ArrayMesh]es and [Transform] references of all bake meshes that exist within the current GridMap. </description> </method> <method name="get_cell_item" qualifiers="const"> diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 167e9d75a1..b1875aec3f 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -39,6 +39,7 @@ #include "core/os/file_access.h" #include "core/os/os.h" #include "core/string/ucaps.h" +#include "main/main.h" #include "../glue/cs_glue_version.gen.h" #include "../godotsharp_defs.h" @@ -3035,9 +3036,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar } break; case Variant::FLOAT: -#ifndef REAL_T_IS_DOUBLE - r_iarg.default_argument += "f"; -#endif + if (r_iarg.type.cname == name_cache.type_float) { + r_iarg.default_argument += "f"; + } break; case Variant::STRING: case Variant::STRING_NAME: @@ -3050,23 +3051,32 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.default_argument = "\"" + r_iarg.default_argument + "\""; } break; - case Variant::TRANSFORM: - if (p_val.operator Transform() == Transform()) { - r_iarg.default_argument.clear(); - } - r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")"; + case Variant::PLANE: { + Plane plane = p_val.operator Plane(); + r_iarg.default_argument = "new Plane(new Vector3(" + plane.normal.operator String() + "), " + rtos(plane.d) + ")"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; - break; - case Variant::PLANE: - case Variant::AABB: + } break; + case Variant::AABB: { + AABB aabb = p_val.operator ::AABB(); + r_iarg.default_argument = "new AABB(new Vector3(" + aabb.position.operator String() + "), new Vector3(" + aabb.position.operator String() + "))"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; + case Variant::RECT2: { + Rect2 rect = p_val.operator Rect2(); + r_iarg.default_argument = "new Rect2(new Vector2(" + rect.position.operator String() + "), new Vector2(" + rect.position.operator String() + "))"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; + case Variant::RECT2I: { + Rect2i rect = p_val.operator Rect2i(); + r_iarg.default_argument = "new Rect2i(new Vector2i(" + rect.position.operator String() + "), new Vector2i(" + rect.position.operator String() + "))"; + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; case Variant::COLOR: - r_iarg.default_argument = "new Color(1, 1, 1, 1)"; + r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; break; case Variant::VECTOR2: case Variant::VECTOR2I: - case Variant::RECT2: - case Variant::RECT2I: case Variant::VECTOR3: case Variant::VECTOR3I: r_iarg.default_argument = "new %s" + r_iarg.default_argument; @@ -3104,12 +3114,43 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar r_iarg.default_argument = "new %s {}"; r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; break; - case Variant::TRANSFORM2D: - case Variant::BASIS: - case Variant::QUAT: - r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity"; + case Variant::TRANSFORM2D: { + Transform2D transform = p_val.operator Transform2D(); + if (transform == Transform2D()) { + r_iarg.default_argument = "Transform2D.Identity"; + } else { + r_iarg.default_argument = "new Transform2D(new Vector2" + transform.elements[0].operator String() + ", new Vector2" + transform.elements[1].operator String() + ", new Vector2" + transform.elements[2].operator String() + ")"; + } r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; - break; + } break; + case Variant::TRANSFORM: { + Transform transform = p_val.operator Transform(); + if (transform == Transform()) { + r_iarg.default_argument = "Transform.Identity"; + } else { + Basis basis = transform.basis; + r_iarg.default_argument = "new Transform(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ", new Vector3" + transform.origin.operator String() + ")"; + } + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; + case Variant::BASIS: { + Basis basis = p_val.operator Basis(); + if (basis == Basis()) { + r_iarg.default_argument = "Basis.Identity"; + } else { + r_iarg.default_argument = "new Basis(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ")"; + } + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; + case Variant::QUAT: { + Quat quat = p_val.operator Quat(); + if (quat == Quat()) { + r_iarg.default_argument = "Quat.Identity"; + } else { + r_iarg.default_argument = "new Quat" + quat.operator String(); + } + r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL; + } break; case Variant::CALLABLE: case Variant::SIGNAL: CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value."); @@ -3649,6 +3690,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) if (!bindings_generator.initialized) { ERR_PRINT("Failed to initialize the bindings generator"); + Main::cleanup(true); ::exit(0); } @@ -3675,6 +3717,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) } // Exit once done + Main::cleanup(true); ::exit(0); } } diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml index 7df261d2ba..86e7f9cc08 100644 --- a/modules/opensimplex/doc_classes/NoiseTexture.xml +++ b/modules/opensimplex/doc_classes/NoiseTexture.xml @@ -32,6 +32,7 @@ </member> <member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false"> Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate. + [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise. </member> <member name="width" type="int" setter="set_width" getter="get_width" default="512"> Width of the generated texture. diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml index dcda5c2324..ad82f87213 100644 --- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml +++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml @@ -109,6 +109,7 @@ </argument> <description> Generate a tileable noise image in [constant Image.FORMAT_L8] format, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]). + [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise. </description> </method> </methods> diff --git a/modules/text_server_adv/bitmap_font_adv.cpp b/modules/text_server_adv/bitmap_font_adv.cpp index df771301e6..e33556b232 100644 --- a/modules/text_server_adv/bitmap_font_adv.cpp +++ b/modules/text_server_adv/bitmap_font_adv.cpp @@ -367,61 +367,63 @@ Error BitmapFontDataAdvanced::load_from_file(const String &p_filename, int p_bas return OK; } -Error BitmapFontDataAdvanced::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE); - ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE); +Error BitmapFontDataAdvanced::bitmap_new(float p_height, float p_ascent, int p_base_size) { + height = p_height; + ascent = p_ascent; - const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data; + base_size = p_base_size; + if (base_size == 0) { + base_size = height; + } - if (RenderingServer::get_singleton() != nullptr) { - Ref<Image> image = memnew(Image(data->img)); - Ref<ImageTexture> tex = memnew(ImageTexture); - tex->create_from_image(image); + char_map.clear(); + textures.clear(); + kerning_map.clear(); - textures.push_back(tex); + for (Map<float, hb_font_t *>::Element *E = cache.front(); E; E = E->next()) { + hb_font_destroy(E->get()); } + cache.clear(); - for (int i = 0; i < data->charcount; i++) { - const int *c = &data->char_rects[i * 8]; + valid = true; - Character chr; - chr.rect.position.x = c[1]; - chr.rect.position.y = c[2]; - chr.rect.size.x = c[3]; - chr.rect.size.y = c[4]; - if (c[7] < 0) { - chr.advance.x = c[3]; - } else { - chr.advance.x = c[7]; - } - chr.align = Vector2(c[6], c[5]); - char_map[c[0]] = chr; - } + return OK; +} - for (int i = 0; i < data->kerning_count; i++) { - KerningPairKey kpk; - kpk.A = data->kernings[i * 3 + 0]; - kpk.B = data->kernings[i * 3 + 1]; +void BitmapFontDataAdvanced::bitmap_add_texture(const Ref<Texture> &p_texture) { + ERR_FAIL_COND(!valid); + ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = data->kernings[i * 3 + 2]; - } - } + textures.push_back(p_texture); +} - height = data->height; - ascent = data->ascent; +void BitmapFontDataAdvanced::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + ERR_FAIL_COND(!valid); - base_size = p_base_size; - if (base_size == 0) { - base_size = height; + Character chr; + chr.rect = p_rect; + chr.texture_idx = p_texture_idx; + if (p_advance < 0) { + chr.advance.x = chr.rect.size.x; + } else { + chr.advance.x = p_advance; } + chr.align = p_align; + char_map[p_char] = chr; +} - valid = true; +void BitmapFontDataAdvanced::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { + ERR_FAIL_COND(!valid); - return OK; + KerningPairKey kpk; + kpk.A = p_A; + kpk.B = p_B; + + if (p_kerning == 0 && kerning_map.has(kpk)) { + kerning_map.erase(kpk); + } else { + kerning_map[kpk] = p_kerning; + } } float BitmapFontDataAdvanced::get_height(int p_size) const { diff --git a/modules/text_server_adv/bitmap_font_adv.h b/modules/text_server_adv/bitmap_font_adv.h index c314f1b087..da7c2b00ac 100644 --- a/modules/text_server_adv/bitmap_font_adv.h +++ b/modules/text_server_adv/bitmap_font_adv.h @@ -74,7 +74,11 @@ public: virtual void clear_cache() override{}; virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override; + virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override; + + virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; + virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; + virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override; virtual float get_height(int p_size) const override; virtual float get_ascent(int p_size) const override; diff --git a/modules/text_server_adv/font_adv.h b/modules/text_server_adv/font_adv.h index 2bb09c11ad..2b6d977451 100644 --- a/modules/text_server_adv/font_adv.h +++ b/modules/text_server_adv/font_adv.h @@ -44,8 +44,13 @@ struct FontDataAdvanced { virtual void clear_cache() = 0; - virtual Error load_from_file(const String &p_filename, int p_base_size) = 0; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0; + virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; + virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; + virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; + + virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; + virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; + virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; virtual float get_height(int p_size) const = 0; virtual float get_ascent(int p_size) const = 0; diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 2af31f4043..2e3c2d1cab 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -570,6 +570,39 @@ RID TextServerAdvanced::create_font_memory(const uint8_t *p_data, size_t p_size, return font_owner.make_rid(fd); } +RID TextServerAdvanced::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = memnew(BitmapFontDataAdvanced); + Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); + if (err != OK) { + memdelete(fd); + return RID(); + } + + return font_owner.make_rid(fd); +} + +void TextServerAdvanced::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_texture(p_texture); +} + +void TextServerAdvanced::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); +} + +void TextServerAdvanced::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { + _THREAD_SAFE_METHOD_ + FontDataAdvanced *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); +} + float TextServerAdvanced::font_get_height(RID p_font, int p_size) const { _THREAD_SAFE_METHOD_ const FontDataAdvanced *fd = font_owner.getornull(p_font); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index c5ebe61bc3..b53b5716e5 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -126,6 +126,11 @@ public: virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; + virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + + virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; + virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; + virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; virtual float font_get_height(RID p_font, int p_size) const override; virtual float font_get_ascent(RID p_font, int p_size) const override; diff --git a/modules/text_server_fb/bitmap_font_fb.cpp b/modules/text_server_fb/bitmap_font_fb.cpp index c9a9cc6eba..c58f8cbba1 100644 --- a/modules/text_server_fb/bitmap_font_fb.cpp +++ b/modules/text_server_fb/bitmap_font_fb.cpp @@ -175,61 +175,58 @@ Error BitmapFontDataFallback::load_from_file(const String &p_filename, int p_bas return OK; } -Error BitmapFontDataFallback::load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(p_data == nullptr, ERR_CANT_CREATE); - ERR_FAIL_COND_V(p_size != sizeof(TextServer::BitmapFontData), ERR_CANT_CREATE); +Error BitmapFontDataFallback::bitmap_new(float p_height, float p_ascent, int p_base_size) { + height = p_height; + ascent = p_ascent; - const TextServer::BitmapFontData *data = (const TextServer::BitmapFontData *)p_data; + base_size = p_base_size; + if (base_size == 0) { + base_size = height; + } - if (RenderingServer::get_singleton() != nullptr) { - Ref<Image> image = memnew(Image(data->img)); - Ref<ImageTexture> tex = memnew(ImageTexture); - tex->create_from_image(image); + char_map.clear(); + textures.clear(); + kerning_map.clear(); - textures.push_back(tex); - } + valid = true; - for (int i = 0; i < data->charcount; i++) { - const int *c = &data->char_rects[i * 8]; - - Character chr; - chr.rect.position.x = c[1]; - chr.rect.position.y = c[2]; - chr.rect.size.x = c[3]; - chr.rect.size.y = c[4]; - if (c[7] < 0) { - chr.advance.x = c[3]; - } else { - chr.advance.x = c[7]; - } - chr.align = Vector2(c[6], c[5]); - char_map[c[0]] = chr; - } + return OK; +} - for (int i = 0; i < data->kerning_count; i++) { - KerningPairKey kpk; - kpk.A = data->kernings[i * 3 + 0]; - kpk.B = data->kernings[i * 3 + 1]; +void BitmapFontDataFallback::bitmap_add_texture(const Ref<Texture> &p_texture) { + ERR_FAIL_COND(!valid); + ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object."); - if (data->kernings[i * 3 + 2] == 0 && kerning_map.has(kpk)) { - kerning_map.erase(kpk); - } else { - kerning_map[kpk] = data->kernings[i * 3 + 2]; - } - } + textures.push_back(p_texture); +} - height = data->height; - ascent = data->ascent; +void BitmapFontDataFallback::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + ERR_FAIL_COND(!valid); - base_size = p_base_size; - if (base_size == 0) { - base_size = height; + Character chr; + chr.rect = p_rect; + chr.texture_idx = p_texture_idx; + if (p_advance < 0) { + chr.advance.x = chr.rect.size.x; + } else { + chr.advance.x = p_advance; } + chr.align = p_align; + char_map[p_char] = chr; +} - valid = true; +void BitmapFontDataFallback::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { + ERR_FAIL_COND(!valid); - return OK; + KerningPairKey kpk; + kpk.A = p_A; + kpk.B = p_B; + + if (p_kerning == 0 && kerning_map.has(kpk)) { + kerning_map.erase(kpk); + } else { + kerning_map[kpk] = p_kerning; + } } float BitmapFontDataFallback::get_height(int p_size) const { diff --git a/modules/text_server_fb/bitmap_font_fb.h b/modules/text_server_fb/bitmap_font_fb.h index 33401b85fa..7cd7507ebc 100644 --- a/modules/text_server_fb/bitmap_font_fb.h +++ b/modules/text_server_fb/bitmap_font_fb.h @@ -70,7 +70,11 @@ public: virtual void clear_cache() override{}; virtual Error load_from_file(const String &p_filename, int p_base_size) override; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) override; + virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) override; + + virtual void bitmap_add_texture(const Ref<Texture> &p_texture) override; + virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; + virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) override; virtual float get_height(int p_size) const override; virtual float get_ascent(int p_size) const override; diff --git a/modules/text_server_fb/font_fb.h b/modules/text_server_fb/font_fb.h index 7fccbe06b5..218f3df03a 100644 --- a/modules/text_server_fb/font_fb.h +++ b/modules/text_server_fb/font_fb.h @@ -42,8 +42,13 @@ struct FontDataFallback { virtual void clear_cache() = 0; - virtual Error load_from_file(const String &p_filename, int p_base_size) = 0; - virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) = 0; + virtual Error load_from_file(const String &p_filename, int p_base_size) { return ERR_CANT_CREATE; }; + virtual Error load_from_memory(const uint8_t *p_data, size_t p_size, int p_base_size) { return ERR_CANT_CREATE; }; + virtual Error bitmap_new(float p_height, float p_ascent, int p_base_size) { return ERR_CANT_CREATE; }; + + virtual void bitmap_add_texture(const Ref<Texture> &p_texture) { ERR_FAIL_MSG("Not supported."); }; + virtual void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { ERR_FAIL_MSG("Not supported."); }; + virtual void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { ERR_FAIL_MSG("Not supported."); }; virtual float get_height(int p_size) const = 0; virtual float get_ascent(int p_size) const = 0; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index a732c6184c..60ab14738a 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -148,6 +148,39 @@ RID TextServerFallback::create_font_memory(const uint8_t *p_data, size_t p_size, return font_owner.make_rid(fd); } +RID TextServerFallback::create_font_bitmap(float p_height, float p_ascent, int p_base_size) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = memnew(BitmapFontDataFallback); + Error err = fd->bitmap_new(p_height, p_ascent, p_base_size); + if (err != OK) { + memdelete(fd); + return RID(); + } + + return font_owner.make_rid(fd); +} + +void TextServerFallback::font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_texture(p_texture); +} + +void TextServerFallback::font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_char(p_char, p_texture_idx, p_rect, p_align, p_advance); +} + +void TextServerFallback::font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) { + _THREAD_SAFE_METHOD_ + FontDataFallback *fd = font_owner.getornull(p_font); + ERR_FAIL_COND(!fd); + fd->bitmap_add_kerning_pair(p_A, p_B, p_kerning); +} + float TextServerFallback::font_get_height(RID p_font, int p_size) const { _THREAD_SAFE_METHOD_ const FontDataFallback *fd = font_owner.getornull(p_font); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index c14a444872..b10369d172 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -81,6 +81,11 @@ public: virtual RID create_font_system(const String &p_name, int p_base_size = 16) override; virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) override; virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) override; + virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) override; + + virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) override; + virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) override; + virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) override; virtual float font_get_height(RID p_font, int p_size) const override; virtual float font_get_ascent(RID p_font, int p_size) const override; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 888b1546e4..088bb35f62 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -2414,6 +2414,9 @@ public: print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); print_verbose("- enabled abis: " + String(",").join(enabled_abis)); + print_verbose("- export filter: " + itos(p_preset->get_export_filter())); + print_verbose("- include filter: " + p_preset->get_include_filter()); + print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); Ref<Image> splash_image; Ref<Image> splash_bg_color_image; @@ -2553,6 +2556,7 @@ public: cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. + cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); // NOTE: The release keystore is not included in the verbose logging // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting. diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index e94681659c..cd2f1d367e 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -22,6 +22,11 @@ tools:ignore="GoogleAppIndexingWarning" android:icon="@mipmap/icon" > + <!-- Records the version of the Godot editor used for building --> + <meta-data + android:name="org.godotengine.editor.version" + android:value="${godotEditorVersion}" /> + <!-- The following metadata values are replaced when Godot exports, modifying them here has no effect. --> <!-- Do these changes in the export preset. Adding new ones is fine. --> diff --git a/platform/android/java/app/build.gradle b/platform/android/java/app/build.gradle index 814cc30613..934c4bf441 100644 --- a/platform/android/java/app/build.gradle +++ b/platform/android/java/app/build.gradle @@ -85,6 +85,8 @@ android { abiFilters export_abi_list } + manifestPlaceholders = [godotEditorVersion: getGodotEditorVersion()] + // Feel free to modify the application id to your own. applicationId getExportPackageName() versionCode getExportVersionCode() diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index 202b3c35c0..06d1f4064e 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -50,6 +50,55 @@ ext.getExportVersionName = { -> return versionName } +ext.getGodotEditorVersion = { -> + String editorVersion = project.hasProperty("godot_editor_version") ? project.property("godot_editor_version") : "" + if (editorVersion == null || editorVersion.isEmpty()) { + // Try the library version first + editorVersion = getGodotLibraryVersion() + + if (editorVersion.isEmpty()) { + // Fallback value. + editorVersion = "custom_build" + } + } + return editorVersion +} + +ext.getGodotLibraryVersion = { -> + // Attempt to read the version from the `version.py` file. + String libraryVersion = "" + + File versionFile = new File("../../../version.py") + if (versionFile.isFile()) { + List<String> requiredKeys = ["major", "minor", "patch", "status", "module_config"] + def map = [:] + + List<String> lines = versionFile.readLines() + for (String line in lines) { + String[] keyValue = line.split("=") + String key = keyValue[0].trim() + String value = keyValue[1].trim().replaceAll("\"", "") + + if (requiredKeys.contains(key)) { + if (!value.isEmpty()) { + map[key] = value + } + requiredKeys.remove(key) + } + } + + if (requiredKeys.empty) { + libraryVersion = map.values().join(".") + } + } + + if (libraryVersion.isEmpty()) { + // Fallback value in case we're unable to read the file. + libraryVersion = "custom_build" + } + return libraryVersion +} + final String PLUGIN_VALUE_SEPARATOR_REGEX = "\\|" // get the list of ABIs the project should be exported to diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 73c136ed0e..ec02b0fc7a 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -165,12 +165,6 @@ task cleanGodotTemplates(type: Delete) { // Delete the library generated AAR files delete("lib/build/outputs/aar") - // Delete the godotpayment libs directory contents - delete("plugins/godotpayment/libs") - - // Delete the generated godotpayment aar - delete("plugins/godotpayment/build/outputs/aar") - // Delete the app libs directory contents delete("app/libs") diff --git a/platform/android/java/lib/AndroidManifest.xml b/platform/android/java/lib/AndroidManifest.xml index fa39bc0f1d..3034794d69 100644 --- a/platform/android/java/lib/AndroidManifest.xml +++ b/platform/android/java/lib/AndroidManifest.xml @@ -6,6 +6,11 @@ <application> + <!-- Records the version of the Godot library --> + <meta-data + android:name="org.godotengine.library.version" + android:value="${godotLibraryVersion}" /> + <service android:name=".GodotDownloaderService" /> </application> diff --git a/platform/android/java/lib/build.gradle b/platform/android/java/lib/build.gradle index ca5153f7f6..6fc9a11a08 100644 --- a/platform/android/java/lib/build.gradle +++ b/platform/android/java/lib/build.gradle @@ -18,6 +18,8 @@ android { defaultConfig { minSdkVersion versions.minSdk targetSdkVersion versions.targetSdk + + manifestPlaceholders = [godotLibraryVersion: getGodotLibraryVersion()] } compileOptions { diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 7d396b402e..0891904dff 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -464,7 +464,9 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) { + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + final Activity activity = getActivity(); Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON); @@ -572,24 +574,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download - // progress (next step) + // progress (next step in onCreateView) mDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this, GodotDownloaderService.class); - View downloadingExpansionView = - inflater.inflate(R.layout.downloading_expansion, container, false); - mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar); - mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText); - mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction); - mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage); - mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed); - mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining); - mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard); - mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular); - mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton); - mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton); - - return downloadingExpansionView; + return; } } catch (NameNotFoundException e) { // TODO Auto-generated catch block @@ -600,6 +589,27 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC mCurrentIntent = activity.getIntent(); initializeGodot(); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle) { + if (mDownloaderClientStub != null) { + View downloadingExpansionView = + inflater.inflate(R.layout.downloading_expansion, container, false); + mPB = (ProgressBar)downloadingExpansionView.findViewById(R.id.progressBar); + mStatusText = (TextView)downloadingExpansionView.findViewById(R.id.statusText); + mProgressFraction = (TextView)downloadingExpansionView.findViewById(R.id.progressAsFraction); + mProgressPercent = (TextView)downloadingExpansionView.findViewById(R.id.progressAsPercentage); + mAverageSpeed = (TextView)downloadingExpansionView.findViewById(R.id.progressAverageSpeed); + mTimeRemaining = (TextView)downloadingExpansionView.findViewById(R.id.progressTimeRemaining); + mDashboard = downloadingExpansionView.findViewById(R.id.downloaderDashboard); + mCellMessage = downloadingExpansionView.findViewById(R.id.approveCellular); + mPauseButton = (Button)downloadingExpansionView.findViewById(R.id.pauseButton); + mWiFiSettingsButton = (Button)downloadingExpansionView.findViewById(R.id.wifiSettingsButton); + + return downloadingExpansionView; + } + return containerLayout; } diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py index ff521e4e7f..8f1afe0e66 100644 --- a/platform/linuxbsd/detect.py +++ b/platform/linuxbsd/detect.py @@ -127,7 +127,6 @@ def configure(env): env["CC"] = "clang" env["CXX"] = "clang++" env.extra_suffix = ".llvm" + env.extra_suffix - env.Append(LIBS=["atomic"]) if env["use_lld"]: if env["use_llvm"]: @@ -394,3 +393,9 @@ def configure(env): # That doesn't make any sense but it's likely a Ubuntu bug? if is64 or env["bits"] == "64": env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) + if env["use_llvm"]: + env["LINKCOM"] = env["LINKCOM"] + " -l:libatomic.a" + + else: + if env["use_llvm"]: + env.Append(LIBS=["atomic"]) diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis index d85dfbc3d3..857c6a88f1 100644 --- a/platform/windows/godot.natvis +++ b/platform/windows/godot.natvis @@ -10,6 +10,16 @@ </Expand> </Type> + <Type Name="LocalVector<*>"> + <Expand> + <Item Name="[size]">count</Item> + <ArrayItems> + <Size>count</Size> + <ValuePointer>data</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <Type Name="List<*>"> <Expand> <Item Name="[size]">_data ? (_data->size_cache) : 0</Item> diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 68d5b4b540..49d1654e3f 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -590,10 +590,10 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("_body_inout"), &Area2D::_body_inout); ClassDB::bind_method(D_METHOD("_area_inout"), &Area2D::_area_inout); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node2D"))); ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area2D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 99c5276636..23eda379be 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -640,10 +640,10 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_reverb_uniformity", "amount"), &Area3D::set_reverb_uniformity); ClassDB::bind_method(D_METHOD("get_reverb_uniformity"), &Area3D::get_reverb_uniformity); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index b7da4822e2..849ef7a2bf 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -30,6 +30,7 @@ #include "collision_object_3d.h" +#include "mesh_instance_3d.h" #include "scene/scene_string_names.h" #include "servers/physics_server_3d.h" @@ -110,6 +111,42 @@ void CollisionObject3D::_update_pickable() { } } +void CollisionObject3D::_update_debug_shapes() { + for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { + if (shapes.has(shapedata_idx->get())) { + ShapeData &shapedata = shapes[shapedata_idx->get()]; + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapedata.shapes.write[i]; + if (s.debug_shape) { + s.debug_shape->queue_delete(); + s.debug_shape = nullptr; + } + if (s.shape.is_null() || shapedata.disabled) { + continue; + } + + Ref<Mesh> mesh = s.shape->get_debug_mesh(); + MeshInstance3D *mi = memnew(MeshInstance3D); + mi->set_transform(shapedata.xform); + mi->set_mesh(mesh); + add_child(mi); + mi->force_update_transform(); + s.debug_shape = mi; + } + } + } + debug_shapes_to_update.clear(); +} + +void CollisionObject3D::_update_shape_data(uint32_t p_owner) { + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + if (debug_shapes_to_update.is_empty()) { + call_deferred("_update_debug_shapes"); + } + debug_shapes_to_update.insert(p_owner); + } +} + void CollisionObject3D::set_ray_pickable(bool p_ray_pickable) { ray_pickable = p_ray_pickable; _update_pickable(); @@ -141,6 +178,8 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes); ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); + ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject3D::_update_debug_shapes); + BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); @@ -188,6 +227,7 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl PhysicsServer3D::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); } } + _update_shape_data(p_owner); } bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const { @@ -223,6 +263,8 @@ void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transf PhysicsServer3D::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform); } } + + _update_shape_data(p_owner); } Transform CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const { @@ -245,6 +287,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3 ShapeData::ShapeBase s; s.index = total_subshapes; s.shape = p_shape; + if (area) { PhysicsServer3D::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled); } else { @@ -253,6 +296,8 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3 sd.shapes.push_back(s); total_subshapes++; + + _update_shape_data(p_owner); } int CollisionObject3D::shape_owner_get_shape_count(uint32_t p_owner) const { @@ -279,13 +324,19 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) ERR_FAIL_COND(!shapes.has(p_owner)); ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); - int index_to_remove = shapes[p_owner].shapes[p_shape].index; + const ShapeData::ShapeBase &s = shapes[p_owner].shapes[p_shape]; + int index_to_remove = s.index; + if (area) { PhysicsServer3D::get_singleton()->area_remove_shape(rid, index_to_remove); } else { PhysicsServer3D::get_singleton()->body_remove_shape(rid, index_to_remove); } + if (s.debug_shape) { + s.debug_shape->queue_delete(); + } + shapes[p_owner].shapes.remove(p_shape); for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index b7473ca12a..fe20176984 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -45,6 +45,7 @@ class CollisionObject3D : public Node3D { Object *owner = nullptr; Transform xform; struct ShapeBase { + Node *debug_shape = nullptr; Ref<Shape3D> shape; int index = 0; }; @@ -60,8 +61,12 @@ class CollisionObject3D : public Node3D { bool capture_input_on_drag = false; bool ray_pickable = true; + Set<uint32_t> debug_shapes_to_update; + void _update_pickable(); + void _update_shape_data(uint32_t p_owner); + protected: CollisionObject3D(RID p_rid, bool p_area); @@ -72,6 +77,8 @@ protected: virtual void _mouse_enter(); virtual void _mouse_exit(); + void _update_debug_shapes(); + public: uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index 914c8eab7a..242d82ab4c 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -100,9 +100,6 @@ void CollisionShape3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(); } - if (get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape(); - } } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { @@ -163,8 +160,6 @@ void CollisionShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings); ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); - ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape3D::_update_debug_shape); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); } @@ -224,34 +219,9 @@ CollisionShape3D::~CollisionShape3D() { //RenderingServer::get_singleton()->free(indicator); } -void CollisionShape3D::_update_debug_shape() { - debug_shape_dirty = false; - - if (debug_shape) { - debug_shape->queue_delete(); - debug_shape = nullptr; - } - - Ref<Shape3D> s = get_shape(); - if (s.is_null()) { - return; - } - - Ref<Mesh> mesh = s->get_debug_mesh(); - MeshInstance3D *mi = memnew(MeshInstance3D); - mi->set_mesh(mesh); - add_child(mi); - debug_shape = mi; -} - void CollisionShape3D::_shape_changed() { // If this is a heightfield shape our center may have changed if (parent) { _update_in_shape_owner(true); } - - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) { - debug_shape_dirty = true; - call_deferred("_update_debug_shape"); - } } diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index f55c09ffaa..5512417f75 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -43,14 +43,10 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *parent = nullptr; - Node *debug_shape = nullptr; - bool debug_shape_dirty; - void resource_changed(RES res); bool disabled = false; protected: - void _update_debug_shape(); void _shape_changed(); void _update_in_shape_owner(bool p_xform_only = false); diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 62d03ea80c..9b98f3d031 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -1350,6 +1350,9 @@ void Tween::interpolate_property(Object *p_object, NodePath p_property, Variant return; } + // Check that the target object is valid + ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name())); + // Get the property from the node path p_property = p_property.get_as_property_path(); @@ -1378,6 +1381,9 @@ void Tween::interpolate_method(Object *p_object, StringName p_method, Variant p_ return; } + // Check that the target object is valid + ERR_FAIL_COND_MSG(p_object == nullptr, vformat("The Tween \"%s\"'s target node is `null`. Is the node reference correct?", get_name())); + // Convert any integers into REALs as they are better for interpolation if (p_initial_val.get_type() == Variant::INT) { p_initial_val = p_initial_val.operator real_t(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index a79c633502..682584d73f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1578,11 +1578,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { if (k->is_pressed()) { bool handled = false; - if (k->is_action("ui_pageup") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_page_up") && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() - vscroll->get_page()); handled = true; } - if (k->is_action("ui_pagedown") && vscroll->is_visible_in_tree()) { + if (k->is_action("ui_page_down") && vscroll->is_visible_in_tree()) { vscroll->set_value(vscroll->get_value() + vscroll->get_page()); handled = true; } diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 8b07299e30..2239226c78 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -73,8 +73,10 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { } } else if (scrollable) { if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) { + grab_focus(); set_value(get_value() + get_step()); } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_DOWN) { + grab_focus(); set_value(get_value() - get_step()); } } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index b10e23ac07..9aaddfd373 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -54,6 +54,7 @@ #include "window.h" #include <stdio.h> +#include <stdlib.h> void SceneTreeTimer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_time_left", "time"), &SceneTreeTimer::set_time_left); @@ -534,12 +535,7 @@ void SceneTree::finalize() { } void SceneTree::quit(int p_exit_code) { - if (p_exit_code >= 0) { - // Override the exit code if a positive argument is given (the default is `-1`). - // This is a shorthand for calling `set_exit_code()` on the OS singleton then quitting. - OS::get_singleton()->set_exit_code(p_exit_code); - } - + OS::get_singleton()->set_exit_code(p_exit_code); _quit = true; } @@ -1205,7 +1201,7 @@ void SceneTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_node_count"), &SceneTree::get_node_count); ClassDB::bind_method(D_METHOD("get_frame"), &SceneTree::get_frame); - ClassDB::bind_method(D_METHOD("quit", "exit_code"), &SceneTree::quit, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("quit", "exit_code"), &SceneTree::quit, DEFVAL(EXIT_SUCCESS)); ClassDB::bind_method(D_METHOD("queue_delete", "obj"), &SceneTree::queue_delete); @@ -1350,6 +1346,8 @@ SceneTree::SceneTree() { collision_debug_contacts = GLOBAL_DEF("debug/shapes/collision/max_contacts_displayed", 10000); ProjectSettings::get_singleton()->set_custom_property_info("debug/shapes/collision/max_contacts_displayed", PropertyInfo(Variant::INT, "debug/shapes/collision/max_contacts_displayed", PROPERTY_HINT_RANGE, "0,20000,1")); // No negative + GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true); + // Create with mainloop. root = memnew(Window); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index c2280c747b..a2f2adb8f8 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -245,7 +245,7 @@ public: void set_auto_accept_quit(bool p_enable); void set_quit_on_go_back(bool p_enable); - void quit(int p_exit_code = -1); + void quit(int p_exit_code = EXIT_SUCCESS); _FORCE_INLINE_ float get_physics_process_time() const { return physics_process_time; } _FORCE_INLINE_ float get_process_time() const { return process_time; } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 54b670df6c..40b85e6d7b 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1770,19 +1770,22 @@ Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_ } } - if (!c) { + if (!c || c->data.mouse_filter == Control::MOUSE_FILTER_IGNORE) { return nullptr; } matrix.affine_invert(); + if (!c->has_point(matrix.xform(p_global))) { + return nullptr; + } - //conditions for considering this as a valid control for return - if (c->data.mouse_filter != Control::MOUSE_FILTER_IGNORE && c->has_point(matrix.xform(p_global)) && (!gui.drag_preview || (c != gui.drag_preview && !gui.drag_preview->is_a_parent_of(c)))) { + Control *drag_preview = _gui_get_drag_preview(); + if (!drag_preview || (c != drag_preview && !drag_preview->is_a_parent_of(c))) { r_inv_xform = matrix; return c; - } else { - return nullptr; } + + return nullptr; } bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) { @@ -1920,9 +1923,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.drag_data = Variant(); gui.dragging = false; - if (gui.drag_preview) { - memdelete(gui.drag_preview); - gui.drag_preview = nullptr; + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + memdelete(drag_preview); + gui.drag_preview_id = ObjectID(); } _propagate_viewport_notification(this, NOTIFICATION_DRAG_END); //change mouse accordingly @@ -1935,9 +1939,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, false); } - if (gui.drag_preview && mb->get_button_index() == BUTTON_LEFT) { - memdelete(gui.drag_preview); - gui.drag_preview = nullptr; + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + memdelete(drag_preview); + gui.drag_preview_id = ObjectID(); } gui.drag_data = Variant(); @@ -2034,10 +2039,11 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.mouse_focus_mask = 0; break; } else { - if (gui.drag_preview != nullptr) { + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored."); - memdelete(gui.drag_preview); - gui.drag_preview = nullptr; + memdelete(drag_preview); + gui.drag_preview_id = ObjectID(); } gui.dragging = false; } @@ -2177,8 +2183,9 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (gui.drag_data.get_type() != Variant::NIL) { //handle dragandrop - if (gui.drag_preview) { - gui.drag_preview->set_position(mpos); + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + drag_preview->set_position(mpos); } gui.drag_mouse_over = over; @@ -2453,15 +2460,29 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) { ERR_FAIL_COND(p_control->is_inside_tree()); ERR_FAIL_COND(p_control->get_parent() != nullptr); - if (gui.drag_preview) { - memdelete(gui.drag_preview); + Control *drag_preview = _gui_get_drag_preview(); + if (drag_preview) { + memdelete(drag_preview); } p_control->set_as_top_level(true); p_control->set_position(gui.last_mouse_pos); p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport p_control->raise(); - gui.drag_preview = p_control; + gui.drag_preview_id = p_control->get_instance_id(); +} + +Control *Viewport::_gui_get_drag_preview() { + if (gui.drag_preview_id.is_null()) { + return nullptr; + } else { + Control *drag_preview = Object::cast_to<Control>(ObjectDB::get_instance(gui.drag_preview_id)); + if (!drag_preview) { + ERR_PRINT("Don't free the control set as drag preview."); + gui.drag_preview_id = ObjectID(); + } + return drag_preview; + } } void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 2a0026a561..0f11e6fb19 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -357,7 +357,7 @@ private: Point2 drag_accum; bool drag_attempted = false; Variant drag_data; - Control *drag_preview = nullptr; + ObjectID drag_preview_id; float tooltip_timer = -1.0; float tooltip_delay = 0.0; Transform2D focus_inv_xform; @@ -415,6 +415,7 @@ private: void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control); void _gui_set_drag_preview(Control *p_base, Control *p_control); + Control *_gui_get_drag_preview(); void _gui_remove_focus_for_window(Node *p_window); void _gui_remove_focus(); diff --git a/scene/resources/capsule_shape_2d.cpp b/scene/resources/capsule_shape_2d.cpp index acf7319339..e5edba8a67 100644 --- a/scene/resources/capsule_shape_2d.cpp +++ b/scene/resources/capsule_shape_2d.cpp @@ -85,9 +85,11 @@ void CapsuleShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector<Color> col; col.push_back(p_color); RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); - RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); - // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + if (is_collision_outline_enabled()) { + RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); + // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + } } Rect2 CapsuleShape2D::get_rect() const { diff --git a/scene/resources/circle_shape_2d.cpp b/scene/resources/circle_shape_2d.cpp index a8a9c42fbd..f06bc4248d 100644 --- a/scene/resources/circle_shape_2d.cpp +++ b/scene/resources/circle_shape_2d.cpp @@ -79,9 +79,11 @@ void CircleShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector<Color> col; col.push_back(p_color); RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); - RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); - // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + if (is_collision_outline_enabled()) { + RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); + // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + } } CircleShape2D::CircleShape2D() : diff --git a/scene/resources/convex_polygon_shape_2d.cpp b/scene/resources/convex_polygon_shape_2d.cpp index 7271614995..d331f83daf 100644 --- a/scene/resources/convex_polygon_shape_2d.cpp +++ b/scene/resources/convex_polygon_shape_2d.cpp @@ -75,9 +75,11 @@ void ConvexPolygonShape2D::draw(const RID &p_to_rid, const Color &p_color) { Vector<Color> col; col.push_back(p_color); RenderingServer::get_singleton()->canvas_item_add_polygon(p_to_rid, points, col); - RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); - // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. - RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + if (is_collision_outline_enabled()) { + RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, points, col); + // Draw the last segment as it's not drawn by `canvas_item_add_polyline()`. + RenderingServer::get_singleton()->canvas_item_add_line(p_to_rid, points[points.size() - 1], points[0], p_color); + } } Rect2 ConvexPolygonShape2D::get_rect() const { diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 943176537b..a94209c75f 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -128,6 +128,38 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, return texture; } +static Ref<FontData> make_font(int p_height, int p_ascent, int p_charcount, const int *p_char_rects, int p_kerning_count, const int *p_kernings, int p_w, int p_h, const unsigned char *p_img) { + Ref<FontData> font(memnew(FontData)); + font->new_bitmap(p_height, p_ascent, p_height); + + Ref<Image> image = memnew(Image(p_img)); + Ref<ImageTexture> tex = memnew(ImageTexture); + tex->create_from_image(image); + + font->bitmap_add_texture(tex); + + for (int i = 0; i < p_charcount; i++) { + const int *c = &p_char_rects[i * 8]; + + int chr = c[0]; + Rect2 frect; + frect.position.x = c[1]; + frect.position.y = c[2]; + frect.size.x = c[3]; + frect.size.y = c[4]; + Point2 align(c[6], c[5]); + int advance = c[7]; + + font->bitmap_add_char(chr, 0, frect, align, advance); + } + + for (int i = 0; i < p_kerning_count; i++) { + font->bitmap_add_kerning_pair(p_kernings[i * 3 + 0], p_kernings[i * 3 + 1], p_kernings[i * 3 + 2]); + } + + return font; +} + static Ref<StyleBox> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { Ref<StyleBox> style(memnew(StyleBoxEmpty)); @@ -989,41 +1021,11 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) { if (p_font.is_valid()) { default_font = p_font; } else if (p_hidpi) { - TextServer::BitmapFontData data; - data.height = _hidpi_font_height; - data.ascent = _hidpi_font_ascent; - data.charcount = _hidpi_font_charcount; - data.char_rects = &_hidpi_font_charrects[0][0]; - data.kerning_count = _hidpi_font_kerning_pair_count; - data.kernings = &_hidpi_font_kerning_pairs[0][0]; - data.w = _hidpi_font_img_width; - data.h = _hidpi_font_img_height; - data.img = _hidpi_font_img_data; - - Ref<FontData> font_data; - font_data.instance(); - font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt"); - default_font_size = font_data->get_base_size(); - + Ref<FontData> font_data = make_font(_hidpi_font_height, _hidpi_font_ascent, _hidpi_font_charcount, &_hidpi_font_charrects[0][0], _hidpi_font_kerning_pair_count, &_hidpi_font_kerning_pairs[0][0], _hidpi_font_img_width, _hidpi_font_img_height, _hidpi_font_img_data); default_font.instance(); default_font->add_data(font_data); } else { - TextServer::BitmapFontData data; - data.height = _lodpi_font_height; - data.ascent = _lodpi_font_ascent; - data.charcount = _lodpi_font_charcount; - data.char_rects = &_lodpi_font_charrects[0][0]; - data.kerning_count = _lodpi_font_kerning_pair_count; - data.kernings = &_lodpi_font_kerning_pairs[0][0]; - data.w = _lodpi_font_img_width; - data.h = _lodpi_font_img_height; - data.img = _lodpi_font_img_data; - - Ref<FontData> font_data; - font_data.instance(); - font_data->load_memory((const uint8_t *)&data, sizeof(data), "fnt"); - default_font_size = font_data->get_base_size(); - + Ref<FontData> font_data = make_font(_lodpi_font_height, _lodpi_font_ascent, _lodpi_font_charcount, &_lodpi_font_charrects[0][0], _lodpi_font_kerning_pair_count, &_lodpi_font_kerning_pairs[0][0], _lodpi_font_img_width, _lodpi_font_img_height, _lodpi_font_img_data); default_font.instance(); default_font->add_data(font_data); } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 702f2ed1c8..6f87c524d8 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -39,6 +39,11 @@ void FontData::_bind_methods() { ClassDB::bind_method(D_METHOD("load_resource", "filename", "base_size"), &FontData::load_resource, DEFVAL(16)); ClassDB::bind_method(D_METHOD("load_memory", "data", "type", "base_size"), &FontData::_load_memory, DEFVAL(16)); + ClassDB::bind_method(D_METHOD("new_bitmap", "height", "ascent", "base_size"), &FontData::new_bitmap); + + ClassDB::bind_method(D_METHOD("bitmap_add_texture", "texture"), &FontData::bitmap_add_texture); + ClassDB::bind_method(D_METHOD("bitmap_add_char", "char", "texture_idx", "rect", "align", "advance"), &FontData::bitmap_add_char); + ClassDB::bind_method(D_METHOD("bitmap_add_kerning_pair", "A", "B", "kerning"), &FontData::bitmap_add_kerning_pair); ClassDB::bind_method(D_METHOD("set_data_path", "path"), &FontData::set_data_path); ClassDB::bind_method(D_METHOD("get_data_path"), &FontData::get_data_path); @@ -229,6 +234,34 @@ void FontData::load_memory(const uint8_t *p_data, size_t p_size, const String &p emit_changed(); } +void FontData::new_bitmap(float p_height, float p_ascent, int p_base_size) { + if (rid != RID()) { + TS->free(rid); + } + rid = TS->create_font_bitmap(p_height, p_ascent, p_base_size); + path = TTR("(Bitmap: " + String::num_int64(rid.get_id(), 16, true) + ")"); + base_size = TS->font_get_base_size(rid); + emit_changed(); +} + +void FontData::bitmap_add_texture(const Ref<Texture> &p_texture) { + if (rid != RID()) { + TS->font_bitmap_add_texture(rid, p_texture); + } +} + +void FontData::bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) { + if (rid != RID()) { + TS->font_bitmap_add_char(rid, p_char, p_texture_idx, p_rect, p_align, p_advance); + } +} + +void FontData::bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning) { + if (rid != RID()) { + TS->font_bitmap_add_kerning_pair(rid, p_A, p_B, p_kerning); + } +} + void FontData::set_data_path(const String &p_path) { load_resource(p_path, base_size); } diff --git a/scene/resources/font.h b/scene/resources/font.h index 56b5acde1a..200373aa8c 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -69,6 +69,12 @@ public: void load_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16); void _load_memory(const PackedByteArray &p_data, const String &p_type, int p_base_size = 16); + void new_bitmap(float p_height, float p_ascent, int p_base_size = 16); + + void bitmap_add_texture(const Ref<Texture> &p_texture); + void bitmap_add_char(char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance); + void bitmap_add_kerning_pair(char32_t p_A, char32_t p_B, int p_kerning); + void set_data_path(const String &p_path); String get_data_path() const; diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 1b82aca386..3fb4f8f211 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -50,6 +50,28 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf int vcount = varray.size(); ERR_FAIL_COND_V(vcount == 0, ERR_INVALID_PARAMETER); + Vector<int> indices; + + if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) { + indices = arrays[Mesh::ARRAY_INDEX]; + } else { + //make code simpler + indices.resize(vcount); + int *iw = indices.ptrw(); + for (int i = 0; i < vcount; i++) { + iw[i] = i; + } + } + + int icount = indices.size(); + const int *r = indices.ptr(); + + ERR_FAIL_COND_V(icount == 0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(icount % 3, ERR_INVALID_PARAMETER); + for (int i = 0; i < icount; i++) { + ERR_FAIL_INDEX_V(r[i], vcount, ERR_INVALID_PARAMETER); + } + clear(); format = p_mesh->surface_get_format(p_surface); material = p_mesh->surface_get_material(p_surface); @@ -128,22 +150,6 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf vertices.write[i] = v; } - Vector<int> indices; - - if (arrays[Mesh::ARRAY_INDEX].get_type() != Variant::NIL) { - indices = arrays[Mesh::ARRAY_INDEX]; - } else { - //make code simpler - indices.resize(vcount); - int *iw = indices.ptrw(); - for (int i = 0; i < vcount; i++) { - iw[i] = i; - } - } - - int icount = indices.size(); - const int *r = indices.ptr(); - Map<Point2i, int> edge_indices; for (int i = 0; i < icount; i += 3) { diff --git a/scene/resources/rectangle_shape_2d.cpp b/scene/resources/rectangle_shape_2d.cpp index 0fd65d8c72..dc4c6dc2d7 100644 --- a/scene/resources/rectangle_shape_2d.cpp +++ b/scene/resources/rectangle_shape_2d.cpp @@ -47,23 +47,25 @@ Vector2 RectangleShape2D::get_size() const { } void RectangleShape2D::draw(const RID &p_to_rid, const Color &p_color) { - // Draw an outlined rectangle to make individual shapes easier to distinguish. - Vector<Vector2> stroke_points; - stroke_points.resize(5); - stroke_points.write[0] = -size * 0.5; - stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5; - stroke_points.write[2] = size * 0.5; - stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5; - stroke_points.write[4] = -size * 0.5; + RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size * 0.5, size), p_color); + if (is_collision_outline_enabled()) { + // Draw an outlined rectangle to make individual shapes easier to distinguish. + Vector<Vector2> stroke_points; + stroke_points.resize(5); + stroke_points.write[0] = -size * 0.5; + stroke_points.write[1] = Vector2(size.x, -size.y) * 0.5; + stroke_points.write[2] = size * 0.5; + stroke_points.write[3] = Vector2(-size.x, size.y) * 0.5; + stroke_points.write[4] = -size * 0.5; - Vector<Color> stroke_colors; - stroke_colors.resize(5); - for (int i = 0; i < 5; i++) { - stroke_colors.write[i] = (p_color); - } + Vector<Color> stroke_colors; + stroke_colors.resize(5); + for (int i = 0; i < 5; i++) { + stroke_colors.write[i] = (p_color); + } - RenderingServer::get_singleton()->canvas_item_add_rect(p_to_rid, Rect2(-size * 0.5, size), p_color); - RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors); + RenderingServer::get_singleton()->canvas_item_add_polyline(p_to_rid, stroke_points, stroke_colors); + } } Rect2 RectangleShape2D::get_rect() const { diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index f8a5855d33..013b1ef1a9 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -29,7 +29,11 @@ /*************************************************************************/ #include "shape_2d.h" + +#include "core/config/engine.h" +#include "core/config/project_settings.h" #include "servers/physics_server_2d.h" + RID Shape2D::get_rid() const { return shape; } @@ -105,6 +109,15 @@ void Shape2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias"); } +bool Shape2D::is_collision_outline_enabled() { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + return true; + } +#endif + return GLOBAL_DEF("debug/shapes/collision/draw_2d_outlines", true); +} + Shape2D::Shape2D(const RID &p_rid) { shape = p_rid; } diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h index 7b00e7e426..14bdd60e4b 100644 --- a/scene/resources/shape_2d.h +++ b/scene/resources/shape_2d.h @@ -61,6 +61,9 @@ public: /// Returns the radius of a circle that fully enclose this shape virtual real_t get_enclosing_radius() const = 0; virtual RID get_rid() const override; + + static bool is_collision_outline_enabled(); + Shape2D(); ~Shape2D(); }; diff --git a/servers/audio/effects/audio_effect_chorus.cpp b/servers/audio/effects/audio_effect_chorus.cpp index 76a995eb37..eb2268aa2e 100644 --- a/servers/audio/effects/audio_effect_chorus.cpp +++ b/servers/audio/effects/audio_effect_chorus.cpp @@ -309,7 +309,7 @@ void AudioEffectChorus::_bind_methods() { ClassDB::bind_method(D_METHOD("set_dry", "amount"), &AudioEffectChorus::set_dry); ClassDB::bind_method(D_METHOD("get_dry"), &AudioEffectChorus::get_dry); - ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1"), "set_voice_count", "get_voice_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "voice_count", PROPERTY_HINT_RANGE, "1,4,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_voice_count", "get_voice_count"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dry", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_dry", "get_dry"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wet", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_wet", "get_wet"); diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index 9cbc01d1d3..4f12248c3e 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -84,6 +84,10 @@ int PhysicsDirectSpaceState2DSW::_intersect_point_impl(const Vector2 &p_point, S int shape_idx = space->intersection_query_subindex_results[i]; + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + Shape2DSW *shape = col_obj->get_shape(shape_idx); Vector2 local_point = (col_obj->get_transform() * col_obj->get_shape_transform(shape_idx)).affine_inverse().xform(p_point); @@ -229,6 +233,10 @@ int PhysicsDirectSpaceState2DSW::intersect_shape(const RID &p_shape, const Trans const CollisionObject2DSW *col_obj = space->intersection_query_results[i]; int shape_idx = space->intersection_query_subindex_results[i]; + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + if (!CollisionSolver2DSW::solve(shape, p_xform, p_motion, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), Vector2(), nullptr, nullptr, nullptr, p_margin)) { continue; } @@ -272,6 +280,10 @@ bool PhysicsDirectSpaceState2DSW::cast_motion(const RID &p_shape, const Transfor const CollisionObject2DSW *col_obj = space->intersection_query_results[i]; int shape_idx = space->intersection_query_subindex_results[i]; + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + Transform2D col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx); //test initial overlap, does it collide if going all the way? if (!CollisionSolver2DSW::solve(shape, p_xform, p_motion, col_obj->get_shape(shape_idx), col_obj_xform, Vector2(), nullptr, nullptr, nullptr, p_margin)) { @@ -346,12 +358,17 @@ bool PhysicsDirectSpaceState2DSW::collide_shape(RID p_shape, const Transform2D & } const CollisionObject2DSW *col_obj = space->intersection_query_results[i]; - int shape_idx = space->intersection_query_subindex_results[i]; if (p_exclude.has(col_obj->get_self())) { continue; } + int shape_idx = space->intersection_query_subindex_results[i]; + + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + cbk.valid_dir = Vector2(); cbk.valid_depth = 0; @@ -436,12 +453,17 @@ bool PhysicsDirectSpaceState2DSW::rest_info(RID p_shape, const Transform2D &p_sh } const CollisionObject2DSW *col_obj = space->intersection_query_results[i]; - int shape_idx = space->intersection_query_subindex_results[i]; if (p_exclude.has(col_obj->get_self())) { continue; } + int shape_idx = space->intersection_query_subindex_results[i]; + + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + rcd.valid_dir = Vector2(); rcd.object = col_obj; rcd.shape = shape_idx; diff --git a/servers/physics_3d/space_3d_sw.cpp b/servers/physics_3d/space_3d_sw.cpp index dd5754b9ac..c8741dc930 100644 --- a/servers/physics_3d/space_3d_sw.cpp +++ b/servers/physics_3d/space_3d_sw.cpp @@ -210,6 +210,10 @@ int PhysicsDirectSpaceState3DSW::intersect_shape(const RID &p_shape, const Trans const CollisionObject3DSW *col_obj = space->intersection_query_results[i]; int shape_idx = space->intersection_query_subindex_results[i]; + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + if (!CollisionSolver3DSW::solve_static(shape, p_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), nullptr, nullptr, nullptr, p_margin, 0)) { continue; } @@ -265,6 +269,10 @@ bool PhysicsDirectSpaceState3DSW::cast_motion(const RID &p_shape, const Transfor const CollisionObject3DSW *col_obj = space->intersection_query_results[i]; int shape_idx = space->intersection_query_subindex_results[i]; + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + Vector3 point_A, point_B; Vector3 sep_axis = p_motion.normalized(); @@ -365,12 +373,17 @@ bool PhysicsDirectSpaceState3DSW::collide_shape(RID p_shape, const Transform &p_ } const CollisionObject3DSW *col_obj = space->intersection_query_results[i]; - int shape_idx = space->intersection_query_subindex_results[i]; if (p_exclude.has(col_obj->get_self())) { continue; } + int shape_idx = space->intersection_query_subindex_results[i]; + + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + if (CollisionSolver3DSW::solve_static(shape, p_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, nullptr, p_margin)) { collided = true; } @@ -435,12 +448,17 @@ bool PhysicsDirectSpaceState3DSW::rest_info(RID p_shape, const Transform &p_shap } const CollisionObject3DSW *col_obj = space->intersection_query_results[i]; - int shape_idx = space->intersection_query_subindex_results[i]; if (p_exclude.has(col_obj->get_self())) { continue; } + int shape_idx = space->intersection_query_subindex_results[i]; + + if (col_obj->is_shape_set_as_disabled(shape_idx)) { + continue; + } + rcd.object = col_obj; rcd.shape = shape_idx; bool sc = CollisionSolver3DSW::solve_static(shape, p_shape_xform, col_obj->get_shape(shape_idx), col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), _rest_cbk_result, &rcd, nullptr, p_margin); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 27fdd090f1..d6d3c11cfb 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -219,6 +219,11 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("create_font_system", "name", "base_size"), &TextServer::create_font_system, DEFVAL(16)); ClassDB::bind_method(D_METHOD("create_font_resource", "filename", "base_size"), &TextServer::create_font_resource, DEFVAL(16)); ClassDB::bind_method(D_METHOD("create_font_memory", "data", "type", "base_size"), &TextServer::_create_font_memory, DEFVAL(16)); + ClassDB::bind_method(D_METHOD("create_font_bitmap", "height", "ascent", "base_size"), &TextServer::create_font_bitmap); + + ClassDB::bind_method(D_METHOD("font_bitmap_add_texture", "font", "texture"), &TextServer::font_bitmap_add_texture); + ClassDB::bind_method(D_METHOD("font_bitmap_add_char", "font", "char", "texture_idx", "rect", "align", "advance"), &TextServer::font_bitmap_add_char); + ClassDB::bind_method(D_METHOD("font_bitmap_add_kerning_pair", "font", "A", "B", "kerning"), &TextServer::font_bitmap_add_kerning_pair); ClassDB::bind_method(D_METHOD("font_get_height", "font", "size"), &TextServer::font_get_height); ClassDB::bind_method(D_METHOD("font_get_ascent", "font", "size"), &TextServer::font_get_ascent); diff --git a/servers/text_server.h b/servers/text_server.h index 3268741a74..e1429da1d1 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -192,18 +192,6 @@ public: Vector<TextServer::Glyph> glyphs_logical; }; - struct BitmapFontData { - int height = 0; - int ascent = 0; - int charcount = 0; - const int *char_rects = nullptr; - int kerning_count = 0; - const int *kernings = nullptr; - int w = 0; - int h = 0; - const unsigned char *img = nullptr; - }; - protected: static void _bind_methods(); @@ -236,6 +224,11 @@ public: virtual RID create_font_system(const String &p_name, int p_base_size = 16) = 0; virtual RID create_font_resource(const String &p_filename, int p_base_size = 16) = 0; virtual RID create_font_memory(const uint8_t *p_data, size_t p_size, const String &p_type, int p_base_size = 16) = 0; + virtual RID create_font_bitmap(float p_height, float p_ascent, int p_base_size = 16) = 0; + + virtual void font_bitmap_add_texture(RID p_font, const Ref<Texture> &p_texture) = 0; + virtual void font_bitmap_add_char(RID p_font, char32_t p_char, int p_texture_idx, const Rect2 &p_rect, const Size2 &p_align, float p_advance) = 0; + virtual void font_bitmap_add_kerning_pair(RID p_font, char32_t p_A, char32_t p_B, int p_kerning) = 0; virtual float font_get_height(RID p_font, int p_size) const = 0; virtual float font_get_ascent(RID p_font, int p_size) const = 0; diff --git a/tests/test_main.cpp b/tests/test_main.cpp index df43b7424f..9d9d5a66db 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -72,6 +72,7 @@ #include "test_text_server.h" #include "test_validate_testing.h" #include "test_variant.h" +#include "test_xml_parser.h" #include "modules/modules_tests.gen.h" diff --git a/tests/test_path_follow_3d.h b/tests/test_path_follow_3d.h new file mode 100644 index 0000000000..b6b4c88222 --- /dev/null +++ b/tests/test_path_follow_3d.h @@ -0,0 +1,220 @@ +/*************************************************************************/ +/* test_path_follow_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_PATH_FOLLOW_3D_H +#define TEST_PATH_FOLLOW_3D_H + +#include "scene/3d/path_3d.h" +#include "scene/resources/curve.h" + +#include "tests/test_macros.h" + +namespace TestPathFollow3D { + +TEST_CASE("[PathFollow3D] Sampling with unit offset") { + const Ref<Curve3D> &curve = memnew(Curve3D()); + curve->add_point(Vector3(0, 0, 0)); + curve->add_point(Vector3(100, 0, 0)); + curve->add_point(Vector3(100, 100, 0)); + curve->add_point(Vector3(100, 100, 100)); + curve->add_point(Vector3(100, 0, 100)); + const Path3D *path = memnew(Path3D); + path->set_curve(curve); + const PathFollow3D *path_follow_3d = memnew(PathFollow3D); + path->add_child(path_follow_3d); + + path_follow_3d->set_unit_offset(0); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0)); + + path_follow_3d->set_unit_offset(0.125); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0)); + + path_follow_3d->set_unit_offset(0.25); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0); + + path_follow_3d->set_unit_offset(0.375); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0))); + + path_follow_3d->set_unit_offset(0.5); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0))); + + path_follow_3d->set_unit_offset(0.625); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50))); + + path_follow_3d->set_unit_offset(0.75); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100))); + + path_follow_3d->set_unit_offset(0.875); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100))); + + path_follow_3d->set_unit_offset(1); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100))); + + memdelete(path); +} + +TEST_CASE("[PathFollow3D] Sampling with offset") { + const Ref<Curve3D> &curve = memnew(Curve3D()); + curve->add_point(Vector3(0, 0, 0)); + curve->add_point(Vector3(100, 0, 0)); + curve->add_point(Vector3(100, 100, 0)); + curve->add_point(Vector3(100, 100, 100)); + curve->add_point(Vector3(100, 0, 100)); + const Path3D *path = memnew(Path3D); + path->set_curve(curve); + const PathFollow3D *path_follow_3d = memnew(PathFollow3D); + path->add_child(path_follow_3d); + + path_follow_3d->set_offset(0); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(0, 0, 0)); + + path_follow_3d->set_offset(50); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(50, 0, 0)); + + path_follow_3d->set_offset(100); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 0); + + path_follow_3d->set_offset(150); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 0))); + + path_follow_3d->set_offset(200); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 0))); + + path_follow_3d->set_offset(250); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 50))); + + path_follow_3d->set_offset(300); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 100, 100))); + + path_follow_3d->set_offset(350); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 50, 100))); + + path_follow_3d->set_offset(400); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector3(100, 0, 100))); + + memdelete(path); +} + +TEST_CASE("[PathFollow3D] Removal of a point in curve") { + const Ref<Curve3D> &curve = memnew(Curve3D()); + curve->add_point(Vector3(0, 0, 0)); + curve->add_point(Vector3(100, 0, 0)); + curve->add_point(Vector3(100, 100, 0)); + const Path3D *path = memnew(Path3D); + path->set_curve(curve); + const PathFollow3D *path_follow_3d = memnew(PathFollow3D); + path->add_child(path_follow_3d); + + path_follow_3d->set_unit_offset(0.5); + CHECK(path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(100, 0, 0))); + + curve->remove_point(1); + + CHECK_MESSAGE( + path_follow_3d->get_transform().get_origin().is_equal_approx(Vector2(50, 50, 0)), + "Path follow's position should be updated after removing a point from the curve"); + + memdelete(path); +} + +TEST_CASE("[PathFollow3D] Unit offset out of range") { + const Ref<Curve3D> &curve = memnew(Curve3D()); + curve->add_point(Vector3(0, 0, 0)); + curve->add_point(Vector3(100, 0, 0)); + const Path3D *path = memnew(Path3D); + path->set_curve(curve); + const PathFollow3D *path_follow_3d = memnew(PathFollow3D); + path->add_child(path_follow_3d); + + path_follow_3d->set_loop(true); + + path_follow_3d->set_unit_offset(-0.3); + CHECK_MESSAGE( + path_follow_3d->get_unit_offset() == 0.7, + "Unit Offset should loop back from the end in the opposite direction"); + + path_follow_3d->set_unit_offset(1.3); + CHECK_MESSAGE( + path_follow_3d->get_unit_offset() == 0.3, + "Unit Offset should loop back from the end in the opposite direction"); + + path_follow_3d->set_loop(false); + + path_follow_3d->set_unit_offset(-0.3); + CHECK_MESSAGE( + path_follow_3d->get_unit_offset() == 0, + "Unit Offset should be clamped at 0"); + + path_follow_3d->set_unit_offset(1.3); + CHECK_MESSAGE( + path_follow_3d->get_unit_offset() == 1, + "Unit Offset should be clamped at 1"); + + memdelete(path); +} + +TEST_CASE("[PathFollow3D] Offset out of range") { + const Ref<Curve3D> &curve = memnew(Curve3D()); + curve->add_point(Vector3(0, 0, 0)); + curve->add_point(Vector3(100, 0, 0)); + const Path3D *path = memnew(Path3D); + path->set_curve(curve); + const PathFollow3D *path_follow_3d = memnew(PathFollow3D); + path->add_child(path_follow_3d); + + path_follow_3d->set_loop(true); + + path_follow_3d->set_offset(-50); + CHECK_MESSAGE( + path_follow_3d->get_offset() == 50, + "Offset should loop back from the end in the opposite direction"); + + path_follow_3d->set_offset(150); + CHECK_MESSAGE( + path_follow_3d->get_offset() == 50, + "Offset should loop back from the end in the opposite direction"); + + path_follow_3d->set_loop(false); + + path_follow_3d->set_offset(-50); + CHECK_MESSAGE( + path_follow_3d->get_offset() == 0, + "Offset should be clamped at 0"); + + path_follow_3d->set_offset(150); + CHECK_MESSAGE( + path_follow_3d->get_offset() == 100, + "Offset should be clamped at max value of curve"); + + memdelete(path); +} +} // namespace TestPathFollow3D + +#endif // TEST_PATH_FOLLOW_3D_H diff --git a/tests/test_string.h b/tests/test_string.h index cc3152203e..17f24fb0d8 100644 --- a/tests/test_string.h +++ b/tests/test_string.h @@ -1166,6 +1166,52 @@ TEST_CASE("[String] xml_escape/unescape") { CHECK(s.xml_escape(false).xml_unescape() == s); } +TEST_CASE("[String] xml_unescape") { + // Named entities + String input = ""&'<>"; + CHECK(input.xml_unescape() == "\"&\'<>"); + + // Numeric entities + input = "AB"; + CHECK(input.xml_unescape() == "AB"); + + input = "�&x#0;More text"; + String result = input.xml_unescape(); + // Didn't put in a leading NUL and terminate the string + CHECK(input.length() > 0); + CHECK(input[0] != '\0'); + // Entity should be left as-is if invalid + CHECK(input.xml_unescape() == input); + + // Check near char32_t range + input = "�"; + result = input.xml_unescape(); + CHECK(result.length() == 1); + CHECK(result[0] == 0xFFFFFFFF); + input = "�"; + result = input.xml_unescape(); + CHECK(result.length() == 1); + CHECK(result[0] == 0xFFFFFFFF); + + // Check out of range of char32_t + input = "�"; + CHECK(input.xml_unescape() == input); + input = "�"; + CHECK(input.xml_unescape() == input); + + // Shouldn't consume without ending in a ';' + input = "B"; + CHECK(input.xml_unescape() == input); + input = "A"; + CHECK(input.xml_unescape() == input); + + // Invalid characters should make the entity ignored + input = "ASomeIrrelevantText;"; + CHECK(input.xml_unescape() == input); + input = "BSomeIrrelevantText;"; + CHECK(input.xml_unescape() == input); +} + TEST_CASE("[String] Strip escapes") { String s = "\t\tTest Test\r\n Test"; CHECK(s.strip_escapes() == "Test Test Test"); diff --git a/tests/test_xml_parser.h b/tests/test_xml_parser.h new file mode 100644 index 0000000000..55de048d6a --- /dev/null +++ b/tests/test_xml_parser.h @@ -0,0 +1,74 @@ +/*************************************************************************/ +/* test_xml_parser.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_XML_PARSER_H +#define TEST_XML_PARSER_H + +#include <inttypes.h> + +#include "core/io/xml_parser.h" +#include "core/string/ustring.h" + +#include "tests/test_macros.h" + +namespace TestXMLParser { +TEST_CASE("[XMLParser] End-to-end") { + String source = "<?xml version = \"1.0\" encoding=\"UTF-8\" ?>\ +<top attr=\"attr value\">\ + Text<AB>\ +</top>"; + Vector<uint8_t> buff = source.to_utf8_buffer(); + + XMLParser parser; + parser.open_buffer(buff); + + // <?xml ...?> gets parsed as NODE_UNKNOWN + CHECK(parser.read() == OK); + CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_UNKNOWN); + + CHECK(parser.read() == OK); + CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT); + CHECK(parser.get_node_name() == "top"); + CHECK(parser.has_attribute("attr")); + CHECK(parser.get_attribute_value("attr") == "attr value"); + + CHECK(parser.read() == OK); + CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_TEXT); + CHECK(parser.get_node_data().lstrip(" \t") == "Text<AB>"); + + CHECK(parser.read() == OK); + CHECK(parser.get_node_type() == XMLParser::NodeType::NODE_ELEMENT_END); + CHECK(parser.get_node_name() == "top"); + + parser.close(); +} +} // namespace TestXMLParser + +#endif // TEST_XML_PARSER_H |