diff options
78 files changed, 1104 insertions, 654 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 38db7f9190..5c4bcc687a 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1198,6 +1198,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("display/window/size/window_height_override", 0); custom_prop_info["display/window/size/window_height_override"] = PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"); // 8K resolution + GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); + GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false); + GLOBAL_DEF_BASIC("audio/buses/default_bus_layout", "res://default_bus_layout.tres"); custom_prop_info["audio/buses/default_bus_layout"] = PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"); GLOBAL_DEF_RST("audio/general/2d_panning_strength", 1.0f); diff --git a/core/io/image.cpp b/core/io/image.cpp index 0f20aabd7e..2d87523ca4 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -416,8 +416,8 @@ int Image::get_height() const { return height; } -Vector2 Image::get_size() const { - return Vector2(width, height); +Vector2i Image::get_size() const { + return Vector2i(width, height); } bool Image::has_mipmaps() const { diff --git a/core/io/image.h b/core/io/image.h index 46820a4c08..9d415423be 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -209,7 +209,7 @@ private: public: int get_width() const; ///< Get image width int get_height() const; ///< Get image height - Vector2 get_size() const; + Vector2i get_size() const; bool has_mipmaps() const; int get_mipmap_count() const; diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp index a456318148..2dcb86cbc6 100644 --- a/core/io/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -353,7 +353,7 @@ Variant PackedDataContainer::_iter_get(const Variant &p_iter) { } void PackedDataContainer::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_data"), &PackedDataContainer::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &PackedDataContainer::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PackedDataContainer::_get_data); ClassDB::bind_method(D_METHOD("_iter_init"), &PackedDataContainer::_iter_init); ClassDB::bind_method(D_METHOD("_iter_get"), &PackedDataContainer::_iter_get); diff --git a/core/object/method_bind.cpp b/core/object/method_bind.cpp index a4474ea53b..0edc7a90c2 100644 --- a/core/object/method_bind.cpp +++ b/core/object/method_bind.cpp @@ -63,7 +63,7 @@ PropertyInfo MethodBind::get_argument_info(int p_argument) const { PropertyInfo info = _gen_argument_type_info(p_argument); #ifdef DEBUG_METHODS_ENABLED - info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("arg" + itos(p_argument)); + info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("_unnamed_arg" + itos(p_argument)); #endif return info; } diff --git a/core/string/translation.cpp b/core/string/translation.cpp index cba2f09022..b83b7c786f 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -141,7 +141,7 @@ void Translation::_bind_methods() { ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list); ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count); - ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages); + ClassDB::bind_method(D_METHOD("_set_messages", "messages"), &Translation::_set_messages); ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages); GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context"); diff --git a/doc/classes/GPUParticlesCollisionSDF3D.xml b/doc/classes/GPUParticlesCollisionSDF3D.xml index c9af07288e..bc8b3c7c11 100644 --- a/doc/classes/GPUParticlesCollisionSDF3D.xml +++ b/doc/classes/GPUParticlesCollisionSDF3D.xml @@ -13,7 +13,27 @@ </description> <tutorials> </tutorials> + <methods> + <method name="get_bake_mask_value" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <description> + Returns whether or not the specified layer of the [member bake_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + <method name="set_bake_mask_value"> + <return type="void" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> + <description> + Based on [code]value[/code], enables or disables the specified layer in the [member bake_mask], given a [code]layer_number[/code] between 1 and 32. + </description> + </method> + </methods> <members> + <member name="bake_mask" type="int" setter="set_bake_mask" getter="get_bake_mask" default="4294967295"> + The visual layers to account for when baking the particle collision SDF. Only [MeshInstance3D]s whose [member VisualInstance3D.layers] match with this [member bake_mask] will be included in the generated particle collision SDF. By default, all objects are taken into account for the particle collision SDF baking. + </member> <member name="extents" type="Vector3" setter="set_extents" getter="get_extents" default="Vector3(1, 1, 1)"> The collision SDF's extents in 3D units. To improve SDF quality, the [member extents] should be set as small as possible while covering the parts of the scene you need. </member> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index a927345e79..754d1ae68a 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -250,7 +250,7 @@ </description> </method> <method name="get_size" qualifiers="const"> - <return type="Vector2" /> + <return type="Vector2i" /> <description> Returns the image's size (width and height). </description> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 9d8f1e1e5d..3431a3347b 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -54,7 +54,7 @@ <argument index="1" name="color" type="Color" /> <description> Sets the color of a specific instance by [i]multiplying[/i] the mesh's existing vertex colors. - For the color to take effect, ensure that [member use_colors] is [code]true[/code] on the [MultiMesh] and [member BaseMaterial3D.vertex_color_use_as_albedo] is [code]true[/code] on the material. + For the color to take effect, ensure that [member use_colors] is [code]true[/code] on the [MultiMesh] and [member BaseMaterial3D.vertex_color_use_as_albedo] is [code]true[/code] on the material. If the color doesn't look as expected, make sure the material's albedo color is set to pure white ([code]Color(1, 1, 1)[/code]). </description> </method> <method name="set_instance_custom_data"> diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml index abd99dd3aa..08d12341cd 100644 --- a/doc/classes/OccluderInstance3D.xml +++ b/doc/classes/OccluderInstance3D.xml @@ -31,7 +31,7 @@ </methods> <members> <member name="bake_mask" type="int" setter="set_bake_mask" getter="get_bake_mask" default="4294967295"> - The visual layers to account for when baking for occluders. Only [MeshInstance3D]s whose [member VisualInstance3D.layers] match with this [member bake_mask] will be included in the generated occluder mesh. By default, all objects are taken into account for the occluder baking. + The visual layers to account for when baking for occluders. Only [MeshInstance3D]s whose [member VisualInstance3D.layers] match with this [member bake_mask] will be included in the generated occluder mesh. By default, all objects with [i]opaque[/i] materials are taken into account for the occluder baking. To improve performance and avoid artifacts, it is recommended to exclude dynamic objects, small objects and fixtures from the baking process by moving them to a separate visual layer and excluding this layer in [member bake_mask]. </member> <member name="bake_simplification_distance" type="float" setter="set_bake_simplification_distance" getter="get_bake_simplification_distance" default="0.1"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 40477d27d4..9df8d2df80 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -549,6 +549,9 @@ <member name="display/window/energy_saving/keep_screen_on" type="bool" setter="" getter="" default="true"> If [code]true[/code], keeps the screen on (even in case of inactivity), so the screensaver does not take over. Works on desktop and mobile platforms. </member> + <member name="display/window/energy_saving/keep_screen_on.editor" type="bool" setter="" getter="" default="false"> + Editor-only override for [member display/window/energy_saving/keep_screen_on]. Does not affect exported projects in debug or release mode. + </member> <member name="display/window/handheld/orientation" type="int" setter="" getter="" default="0"> The default screen orientation to use on mobile devices. See [enum DisplayServer.ScreenOrientation] for possible values. [b]Note:[/b] When set to a portrait orientation, this project setting does not flip the project resolution's width and height automatically. Instead, you have to set [member display/window/size/viewport_width] and [member display/window/size/viewport_height] accordingly. diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index f4eaaac2e1..84f3b6a4b1 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -344,9 +344,11 @@ </member> <member name="max_size" type="Vector2i" setter="set_max_size" getter="get_max_size" default="Vector2i(0, 0)"> If non-zero, the [Window] can't be resized to be bigger than this size. + [b]Note:[/b] This property will be ignored if the value is lower than [member min_size]. </member> <member name="min_size" type="Vector2i" setter="set_min_size" getter="get_min_size" default="Vector2i(0, 0)"> If non-zero, the [Window] can't be resized to be smaller than this size. + [b]Note:[/b] This property will be ignored in favor of [method get_contents_minimum_size] if [member wrap_controls] is enabled and if its size is bigger. </member> <member name="mode" type="int" setter="set_mode" getter="get_mode" enum="Window.Mode" default="0"> Set's the window's current mode. @@ -388,7 +390,7 @@ If [code]true[/code], the window is visible. </member> <member name="wrap_controls" type="bool" setter="set_wrap_controls" getter="is_wrapping_controls" default="false"> - If [code]true[/code], the window's size will automatically update when a child node is added or removed. + If [code]true[/code], the window's size will automatically update when a child node is added or removed, ignoring [member min_size] if the new size is bigger. If [code]false[/code], you need to call [method child_controls_changed] manually. </member> </members> diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 196a26ef77..cf87990a11 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -72,6 +72,7 @@ class State: def parse_class(self, class_root: ET.Element, filepath: str) -> None: class_name = class_root.attrib["name"] + self.current_class = class_name class_def = ClassDef(class_name) self.classes[class_name] = class_def @@ -126,7 +127,7 @@ class State: else: return_type = TypeName("void") - params = parse_arguments(constructor) + params = self.parse_arguments(constructor, "constructor") desc_element = constructor.find("description") method_desc = None @@ -154,7 +155,7 @@ class State: else: return_type = TypeName("void") - params = parse_arguments(method) + params = self.parse_arguments(method, "method") desc_element = method.find("description") method_desc = None @@ -182,7 +183,7 @@ class State: else: return_type = TypeName("void") - params = parse_arguments(operator) + params = self.parse_arguments(operator, "operator") desc_element = operator.find("description") method_desc = None @@ -230,7 +231,7 @@ class State: annotation_name = annotation.attrib["name"] qualifiers = annotation.get("qualifiers") - params = parse_arguments(annotation) + params = self.parse_arguments(annotation, "annotation") desc_element = annotation.find("description") annotation_desc = None @@ -254,7 +255,7 @@ class State: print_error('{}.xml: Duplicate signal "{}".'.format(class_name, signal_name), self) continue - params = parse_arguments(signal) + params = self.parse_arguments(signal, "signal") desc_element = signal.find("description") signal_desc = None @@ -302,6 +303,32 @@ class State: if link.text is not None: class_def.tutorials.append((link.text.strip(), link.get("title", ""))) + self.current_class = "" + + def parse_arguments(self, root: ET.Element, context: str) -> List["ParameterDef"]: + param_elements = root.findall("argument") + params: Any = [None] * len(param_elements) + + for param_index, param_element in enumerate(param_elements): + param_name = param_element.attrib["name"] + index = int(param_element.attrib["index"]) + type_name = TypeName.from_element(param_element) + default = param_element.get("default") + + if param_name.strip() == "" or param_name.startswith("_unnamed_arg"): + print_error( + '{}.xml: Empty argument name in {} "{}" at position {}.'.format( + self.current_class, context, root.attrib["name"], param_index + ), + self, + ) + + params[index] = ParameterDef(param_name, type_name, default) + + cast: List[ParameterDef] = params + + return cast + def sort_classes(self) -> None: self.classes = OrderedDict(sorted(self.classes.items(), key=lambda t: t[0])) @@ -440,22 +467,6 @@ def print_error(error: str, state: State) -> None: state.num_errors += 1 -def parse_arguments(root: ET.Element) -> List[ParameterDef]: - param_elements = root.findall("argument") - params: Any = [None] * len(param_elements) - for param_element in param_elements: - param_name = param_element.attrib["name"] - index = int(param_element.attrib["index"]) - type_name = TypeName.from_element(param_element) - default = param_element.get("default") - - params[index] = ParameterDef(param_name, type_name, default) - - cast: List[ParameterDef] = params - - return cast - - def main() -> None: # Enable ANSI escape code support on Windows 10 and later (for colored console output). # <https://bugs.python.org/issue29059> diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 2550b3a8a1..84daf839e9 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1052,6 +1052,7 @@ void main() { #else vec3 ref_vec = reflect(-view, normal); #endif + ref_vec = mix(ref_vec, normal, roughness * roughness); float horizon = min(1.0 + dot(ref_vec, normal), 1.0); ref_vec = scene_data.radiance_inverse_xform * ref_vec; specular_light = textureLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).rgb; diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index a819458417..46a2a0719a 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -337,311 +337,318 @@ static Variant get_documentation_default_value(const StringName &p_class_name, c } void DocTools::generate(bool p_basic_types) { - List<StringName> classes; - ClassDB::get_class_list(&classes); - classes.sort_custom<StringName::AlphCompare>(); - // Move ProjectSettings, so that other classes can register properties there. - classes.move_to_back(classes.find("ProjectSettings")); - - bool skip_setter_getter_methods = true; - - while (classes.size()) { - HashSet<StringName> setters_getters; - - String name = classes.front()->get(); - if (!ClassDB::is_class_exposed(name)) { - print_verbose(vformat("Class '%s' is not exposed, skipping.", name)); - classes.pop_front(); - continue; - } - - String cname = name; - - class_list[cname] = DocData::ClassDoc(); - DocData::ClassDoc &c = class_list[cname]; - c.name = cname; - c.inherits = ClassDB::get_parent_class(name); + // Add ClassDB-exposed classes. + { + List<StringName> classes; + ClassDB::get_class_list(&classes); + classes.sort_custom<StringName::AlphCompare>(); + // Move ProjectSettings, so that other classes can register properties there. + classes.move_to_back(classes.find("ProjectSettings")); + + bool skip_setter_getter_methods = true; + + // Populate documentation data for each exposed class. + while (classes.size()) { + String name = classes.front()->get(); + if (!ClassDB::is_class_exposed(name)) { + print_verbose(vformat("Class '%s' is not exposed, skipping.", name)); + classes.pop_front(); + continue; + } - List<PropertyInfo> properties; - List<PropertyInfo> own_properties; - - // Special case for editor and project settings, so they can be documented. - if (name == "EditorSettings") { - // We don't create the full blown EditorSettings (+ config file) with `create()`, - // instead we just make a local instance to get default values. - Ref<EditorSettings> edset = memnew(EditorSettings); - edset->get_property_list(&properties); - own_properties = properties; - } else if (name == "ProjectSettings") { - ProjectSettings::get_singleton()->get_property_list(&properties); - own_properties = properties; - } else { - ClassDB::get_property_list(name, &properties); - ClassDB::get_property_list(name, &own_properties, true); - } + String cname = name; + // Property setters and getters do not get exposed as individual methods. + HashSet<StringName> setters_getters; - properties.sort(); - own_properties.sort(); + class_list[cname] = DocData::ClassDoc(); + DocData::ClassDoc &c = class_list[cname]; + c.name = cname; + c.inherits = ClassDB::get_parent_class(name); - List<PropertyInfo>::Element *EO = own_properties.front(); - for (const PropertyInfo &E : properties) { - bool inherited = true; - if (EO && EO->get() == E) { - inherited = false; - EO = EO->next(); - } + List<PropertyInfo> properties; + List<PropertyInfo> own_properties; - if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) { - continue; + // Special case for editor and project settings, so they can be documented. + if (name == "EditorSettings") { + // We don't create the full blown EditorSettings (+ config file) with `create()`, + // instead we just make a local instance to get default values. + Ref<EditorSettings> edset = memnew(EditorSettings); + edset->get_property_list(&properties); + own_properties = properties; + } else if (name == "ProjectSettings") { + ProjectSettings::get_singleton()->get_property_list(&properties); + own_properties = properties; + } else { + ClassDB::get_property_list(name, &properties); + ClassDB::get_property_list(name, &own_properties, true); } - DocData::PropertyDoc prop; - prop.name = E.name; - prop.overridden = inherited; + properties.sort(); + own_properties.sort(); - if (inherited) { - String parent = ClassDB::get_parent_class(c.name); - while (!ClassDB::has_property(parent, prop.name, true)) { - parent = ClassDB::get_parent_class(parent); + List<PropertyInfo>::Element *EO = own_properties.front(); + for (const PropertyInfo &E : properties) { + bool inherited = true; + if (EO && EO->get() == E) { + inherited = false; + EO = EO->next(); } - prop.overrides = parent; - } - - bool default_value_valid = false; - Variant default_value; - if (name == "EditorSettings") { - if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") { - // Don't include spurious properties in the generated EditorSettings class reference. + if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) { continue; } - } - if (name == "ProjectSettings") { - // Special case for project settings, so that settings are not taken from the current project's settings - if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) { - continue; - } - if (E.usage & PROPERTY_USAGE_EDITOR) { - if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) { - default_value = ProjectSettings::get_singleton()->property_get_revert(E.name); - default_value_valid = true; + DocData::PropertyDoc prop; + prop.name = E.name; + prop.overridden = inherited; + + if (inherited) { + String parent = ClassDB::get_parent_class(c.name); + while (!ClassDB::has_property(parent, prop.name, true)) { + parent = ClassDB::get_parent_class(parent); } + prop.overrides = parent; } - } else { - default_value = get_documentation_default_value(name, E.name, default_value_valid); - if (inherited) { - bool base_default_value_valid = false; - Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid); - if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) { + + bool default_value_valid = false; + Variant default_value; + + if (name == "EditorSettings") { + if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") { + // Don't include spurious properties in the generated EditorSettings class reference. continue; } } - } - if (default_value_valid && default_value.get_type() != Variant::OBJECT) { - prop.default_value = default_value.get_construct_string().replace("\n", " "); - } - - StringName setter = ClassDB::get_property_setter(name, E.name); - StringName getter = ClassDB::get_property_getter(name, E.name); - - prop.setter = setter; - prop.getter = getter; - - bool found_type = false; - if (getter != StringName()) { - MethodBind *mb = ClassDB::get_method(name, getter); - if (mb) { - PropertyInfo retinfo = mb->get_return_info(); - - found_type = true; - if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { - prop.enumeration = retinfo.class_name; - prop.type = "int"; - } else if (retinfo.class_name != StringName()) { - prop.type = retinfo.class_name; - } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { - prop.type = retinfo.hint_string + "[]"; - } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { - prop.type = retinfo.hint_string; - } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - prop.type = "Variant"; - } else if (retinfo.type == Variant::NIL) { - prop.type = "void"; - } else { - prop.type = Variant::get_type_name(retinfo.type); + if (name == "ProjectSettings") { + // Special case for project settings, so that settings are not taken from the current project's settings + if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) { + continue; + } + if (E.usage & PROPERTY_USAGE_EDITOR) { + if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) { + default_value = ProjectSettings::get_singleton()->property_get_revert(E.name); + default_value_valid = true; + } + } + } else { + default_value = get_documentation_default_value(name, E.name, default_value_valid); + if (inherited) { + bool base_default_value_valid = false; + Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid); + if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) { + continue; + } } } - setters_getters.insert(getter); - } + if (default_value_valid && default_value.get_type() != Variant::OBJECT) { + prop.default_value = default_value.get_construct_string().replace("\n", " "); + } - if (setter != StringName()) { - setters_getters.insert(setter); - } + StringName setter = ClassDB::get_property_setter(name, E.name); + StringName getter = ClassDB::get_property_getter(name, E.name); + + prop.setter = setter; + prop.getter = getter; + + bool found_type = false; + if (getter != StringName()) { + MethodBind *mb = ClassDB::get_method(name, getter); + if (mb) { + PropertyInfo retinfo = mb->get_return_info(); + + found_type = true; + if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { + prop.enumeration = retinfo.class_name; + prop.type = "int"; + } else if (retinfo.class_name != StringName()) { + prop.type = retinfo.class_name; + } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { + prop.type = retinfo.hint_string + "[]"; + } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { + prop.type = retinfo.hint_string; + } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { + prop.type = "Variant"; + } else if (retinfo.type == Variant::NIL) { + prop.type = "void"; + } else { + prop.type = Variant::get_type_name(retinfo.type); + } + } - if (!found_type) { - if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { - prop.type = E.hint_string; - } else { - prop.type = Variant::get_type_name(E.type); + setters_getters.insert(getter); } - } - c.properties.push_back(prop); - } + if (setter != StringName()) { + setters_getters.insert(setter); + } - List<MethodInfo> method_list; - ClassDB::get_method_list(name, &method_list, true); - method_list.sort(); + if (!found_type) { + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { + prop.type = E.hint_string; + } else { + prop.type = Variant::get_type_name(E.type); + } + } - for (const MethodInfo &E : method_list) { - if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) { - continue; //hidden, don't count + c.properties.push_back(prop); } - if (skip_setter_getter_methods && setters_getters.has(E.name)) { - // Don't skip parametric setters and getters, i.e. method which require - // one or more parameters to define what property should be set or retrieved. - // E.g. CPUParticles3D::set_param(Parameter param, float value). - if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) { - continue; - } - } + List<MethodInfo> method_list; + ClassDB::get_method_list(name, &method_list, true); + method_list.sort(); - DocData::MethodDoc method; - DocData::method_doc_from_methodinfo(method, E, ""); + for (const MethodInfo &E : method_list) { + if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) { + continue; //hidden, don't count + } - Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); - if (errs.size()) { - if (!errs.has(OK)) { - errs.insert(0, OK); + if (skip_setter_getter_methods && setters_getters.has(E.name)) { + // Don't skip parametric setters and getters, i.e. method which require + // one or more parameters to define what property should be set or retrieved. + // E.g. CPUParticles3D::set_param(Parameter param, float value). + if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) { + continue; + } } - for (int i = 0; i < errs.size(); i++) { - if (!method.errors_returned.has(errs[i])) { - method.errors_returned.push_back(errs[i]); + + DocData::MethodDoc method; + DocData::method_doc_from_methodinfo(method, E, ""); + + Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); + if (errs.size()) { + if (!errs.has(OK)) { + errs.insert(0, OK); + } + for (int i = 0; i < errs.size(); i++) { + if (!method.errors_returned.has(errs[i])) { + method.errors_returned.push_back(errs[i]); + } } } + + c.methods.push_back(method); } - c.methods.push_back(method); - } + List<MethodInfo> signal_list; + ClassDB::get_signal_list(name, &signal_list, true); - List<MethodInfo> signal_list; - ClassDB::get_signal_list(name, &signal_list, true); + if (signal_list.size()) { + for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) { + DocData::MethodDoc signal; + signal.name = EV->get().name; + for (int i = 0; i < EV->get().arguments.size(); i++) { + const PropertyInfo &arginfo = EV->get().arguments[i]; + DocData::ArgumentDoc argument; + DocData::argument_doc_from_arginfo(argument, arginfo); - if (signal_list.size()) { - for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) { - DocData::MethodDoc signal; - signal.name = EV->get().name; - for (int i = 0; i < EV->get().arguments.size(); i++) { - const PropertyInfo &arginfo = EV->get().arguments[i]; - DocData::ArgumentDoc argument; - DocData::argument_doc_from_arginfo(argument, arginfo); + signal.arguments.push_back(argument); + } - signal.arguments.push_back(argument); + c.signals.push_back(signal); } - - c.signals.push_back(signal); } - } - List<String> constant_list; - ClassDB::get_integer_constant_list(name, &constant_list, true); + List<String> constant_list; + ClassDB::get_integer_constant_list(name, &constant_list, true); + + for (const String &E : constant_list) { + DocData::ConstantDoc constant; + constant.name = E; + constant.value = itos(ClassDB::get_integer_constant(name, E)); + constant.is_value_valid = true; + constant.enumeration = ClassDB::get_integer_constant_enum(name, E); + constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration); + c.constants.push_back(constant); + } - for (const String &E : constant_list) { - DocData::ConstantDoc constant; - constant.name = E; - constant.value = itos(ClassDB::get_integer_constant(name, E)); - constant.is_value_valid = true; - constant.enumeration = ClassDB::get_integer_constant_enum(name, E); - constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration); - c.constants.push_back(constant); - } + // Theme items. + { + List<StringName> l; + + Theme::get_default()->get_color_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Color"; + tid.data_type = "color"; + tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " "); + c.theme_properties.push_back(tid); + } - // Theme items. - { - List<StringName> l; - - Theme::get_default()->get_color_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Color"; - tid.data_type = "color"; - tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " "); - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_constant_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "int"; + tid.data_type = "constant"; + tid.default_value = itos(Theme::get_default()->get_constant(E, cname)); + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_constant_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "int"; - tid.data_type = "constant"; - tid.default_value = itos(Theme::get_default()->get_constant(E, cname)); - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_font_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Font"; + tid.data_type = "font"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_font_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Font"; - tid.data_type = "font"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_font_size_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "int"; + tid.data_type = "font_size"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_font_size_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "int"; - tid.data_type = "font_size"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_icon_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "Texture2D"; + tid.data_type = "icon"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_icon_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "Texture2D"; - tid.data_type = "icon"; - c.theme_properties.push_back(tid); - } + l.clear(); + Theme::get_default()->get_stylebox_list(cname, &l); + for (const StringName &E : l) { + DocData::ThemeItemDoc tid; + tid.name = E; + tid.type = "StyleBox"; + tid.data_type = "style"; + c.theme_properties.push_back(tid); + } - l.clear(); - Theme::get_default()->get_stylebox_list(cname, &l); - for (const StringName &E : l) { - DocData::ThemeItemDoc tid; - tid.name = E; - tid.type = "StyleBox"; - tid.data_type = "style"; - c.theme_properties.push_back(tid); + c.theme_properties.sort(); } - c.theme_properties.sort(); + classes.pop_front(); } - - classes.pop_front(); } + // Add a dummy Variant entry. { - // So we can document the concept of Variant even if it's not a usable class per se. + // This allows us to document the concept of Variant even though + // it's not a ClassDB-exposed class. class_list["Variant"] = DocData::ClassDoc(); class_list["Variant"].name = "Variant"; } + // If we don't want to populate basic types, break here. if (!p_basic_types) { return; } - // Add Variant types. + // Add Variant data types. for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (i == Variant::NIL) { continue; // Not exposed outside of 'null', should not be in class list. @@ -809,14 +816,14 @@ void DocTools::generate(bool p_basic_types) { } } - //built in constants and functions - + // Add global API (servers, engine singletons, global constants) and Variant utility functions. { String cname = "@GlobalScope"; class_list[cname] = DocData::ClassDoc(); DocData::ClassDoc &c = class_list[cname]; c.name = cname; + // Global constants. for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) { DocData::ConstantDoc cd; cd.name = CoreConstants::get_global_constant_name(i); @@ -830,10 +837,11 @@ void DocTools::generate(bool p_basic_types) { c.constants.push_back(cd); } + // Servers/engine singletons. List<Engine::Singleton> singletons; Engine::get_singleton()->get_singletons(&singletons); - //servers (this is kind of hackish) + // FIXME: this is kind of hackish... for (const Engine::Singleton &s : singletons) { DocData::PropertyDoc pd; if (!s.ptr) { @@ -847,13 +855,14 @@ void DocTools::generate(bool p_basic_types) { c.properties.push_back(pd); } + // Variant utility functions. List<StringName> utility_functions; Variant::get_utility_function_list(&utility_functions); utility_functions.sort_custom<StringName::AlphCompare>(); for (const StringName &E : utility_functions) { DocData::MethodDoc md; md.name = E; - //return + // Utility function's return type. if (Variant::has_utility_function_return_value(E)) { PropertyInfo pi; pi.type = Variant::get_utility_function_return_type(E); @@ -865,6 +874,7 @@ void DocTools::generate(bool p_basic_types) { md.return_type = ad.type; } + // Utility function's arguments. if (Variant::is_utility_function_vararg(E)) { md.qualifiers = "vararg"; } else { @@ -885,11 +895,10 @@ void DocTools::generate(bool p_basic_types) { } } - // Built-in script reference. - // We only add a doc entry for languages which actually define any built-in - // methods or constants. - + // Add scripting language built-ins. { + // We only add a doc entry for languages which actually define any built-in + // methods, constants, or annotations. for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); String cname = "@" + lang->get_name(); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 3b37adb830..2064b6f3a1 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1613,12 +1613,11 @@ void EditorInspectorArray::_rmb_popup_id_pressed(int p_id) { _clear_array(); break; case OPTION_RESIZE_ARRAY: - new_size = count; - new_size_line_edit->set_text(Variant(new_size)); + new_size_spin_box->set_value(count); resize_dialog->get_ok_button()->set_disabled(true); - resize_dialog->popup_centered(); - new_size_line_edit->grab_focus(); - new_size_line_edit->select_all(); + resize_dialog->popup_centered(Size2i(250, 0) * EDSCALE); + new_size_spin_box->get_line_edit()->grab_focus(); + new_size_spin_box->get_line_edit()->select_all(); break; default: break; @@ -2013,36 +2012,21 @@ int EditorInspectorArray::_drop_position() const { return -1; } -void EditorInspectorArray::_new_size_line_edit_text_changed(String p_text) { - bool valid = false; - if (p_text.is_valid_int()) { - int val = p_text.to_int(); - if (val > 0 && val != count) { - valid = true; - } +void EditorInspectorArray::_resize_dialog_confirmed() { + if (int(new_size_spin_box->get_value()) == count) { + return; } - resize_dialog->get_ok_button()->set_disabled(!valid); + + resize_dialog->hide(); + _resize_array(int(new_size_spin_box->get_value())); } -void EditorInspectorArray::_new_size_line_edit_text_submitted(String p_text) { - bool valid = false; - if (p_text.is_valid_int()) { - int val = p_text.to_int(); - if (val > 0 && val != count) { - new_size = val; - valid = true; - } - } - if (valid) { - resize_dialog->hide(); - _resize_array(new_size); - } else { - new_size_line_edit->set_text(Variant(new_size)); - } +void EditorInspectorArray::_new_size_spin_box_value_changed(float p_value) { + resize_dialog->get_ok_button()->set_disabled(int(p_value) == count); } -void EditorInspectorArray::_resize_dialog_confirmed() { - _new_size_line_edit_text_submitted(new_size_line_edit->get_text()); +void EditorInspectorArray::_new_size_spin_box_text_submitted(String p_text) { + _resize_dialog_confirmed(); } void EditorInspectorArray::_setup() { @@ -2342,10 +2326,11 @@ EditorInspectorArray::EditorInspectorArray() { VBoxContainer *resize_dialog_vbox = memnew(VBoxContainer); resize_dialog->add_child(resize_dialog_vbox); - new_size_line_edit = memnew(LineEdit); - new_size_line_edit->connect("text_changed", callable_mp(this, &EditorInspectorArray::_new_size_line_edit_text_changed)); - new_size_line_edit->connect("text_submitted", callable_mp(this, &EditorInspectorArray::_new_size_line_edit_text_submitted)); - resize_dialog_vbox->add_margin_child(TTRC("New Size:"), new_size_line_edit); + new_size_spin_box = memnew(SpinBox); + new_size_spin_box->set_max(16384); + new_size_spin_box->connect("value_changed", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_value_changed)); + new_size_spin_box->get_line_edit()->connect("text_submitted", callable_mp(this, &EditorInspectorArray::_new_size_spin_box_text_submitted)); + resize_dialog_vbox->add_margin_child(TTRC("New Size:"), new_size_spin_box); vbox->connect("visibility_changed", callable_mp(this, &EditorInspectorArray::_vbox_visibility_changed)); } diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 551f6f6f86..baba9ec1f4 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -39,6 +39,7 @@ #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/spin_box.h" #include "scene/gui/texture_rect.h" class UndoRedo; @@ -333,8 +334,7 @@ class EditorInspectorArray : public EditorInspectorSection { Button *add_button = nullptr; AcceptDialog *resize_dialog = nullptr; - int new_size = 0; - LineEdit *new_size_line_edit = nullptr; + SpinBox *new_size_spin_box = nullptr; // Pagination int page_length = 5; @@ -390,8 +390,8 @@ class EditorInspectorArray : public EditorInspectorSection { Array _extract_properties_as_array(const List<PropertyInfo> &p_list); int _drop_position() const; - void _new_size_line_edit_text_changed(String p_text); - void _new_size_line_edit_text_submitted(String p_text); + void _new_size_spin_box_value_changed(float p_value); + void _new_size_spin_box_text_submitted(String p_text); void _resize_dialog_confirmed(); void _update_elements_visibility(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 5e66d436fc..f434df3a1e 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1147,6 +1147,17 @@ void EditorPropertyLayersGrid::_bind_methods() { ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name"))); } +void EditorPropertyLayers::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + button->set_normal_texture(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + button->set_pressed_texture(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + button->set_disabled_texture(get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons"))); + } break; + } +} + void EditorPropertyLayers::_set_read_only(bool p_read_only) { button->set_disabled(p_read_only); grid->set_read_only(p_read_only); @@ -1283,9 +1294,9 @@ EditorPropertyLayers::EditorPropertyLayers() { grid->set_h_size_flags(SIZE_EXPAND_FILL); hb->add_child(grid); - button = memnew(Button); + button = memnew(TextureButton); + button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED); button->set_toggle_mode(true); - button->set_text("..."); button->connect("pressed", callable_mp(this, &EditorPropertyLayers::_button_pressed)); hb->add_child(button); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 5c73a53184..c1dfb5cb1e 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -342,13 +342,14 @@ private: String basename; LayerType layer_type; PopupMenu *layers = nullptr; - Button *button = nullptr; + TextureButton *button = nullptr; void _button_pressed(); void _menu_pressed(int p_menu); void _refresh_names(); protected: + void _notification(int p_what); virtual void _set_read_only(bool p_read_only) override; static void _bind_methods(); diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index cb77cd78bf..a7d7c0145a 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -294,9 +294,9 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) _commit_action(); return true; } else { - pre_move_edit = vertices; edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos)); vertices.insert(edited_point.vertex, edited_point.pos); + pre_move_edit = vertices; selected_point = Vertex(edited_point.polygon, edited_point.vertex); edge_point = PosVertex(); diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp index 643a470425..b54cb515e4 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp @@ -140,7 +140,7 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const Strin if (col_sdf) { Ref<Image> bake_img = col_sdf->bake(); if (bake_img.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("Bake Error.")); + EditorNode::get_singleton()->show_warning(TTR("No faces detected during GPUParticlesCollisionSDF3D bake.\nCheck whether there are visible meshes matching the bake mask within its extents.")); return; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 005d5fc951..6afc6798d0 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1386,25 +1386,17 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { const real_t zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor(); switch (b->get_button_index()) { case MouseButton::WHEEL_UP: { - if (b->is_alt_pressed()) { - scale_fov(-0.05); + if (is_freelook_active()) { + scale_freelook_speed(zoom_factor); } else { - if (is_freelook_active()) { - scale_freelook_speed(zoom_factor); - } else { - scale_cursor_distance(1.0 / zoom_factor); - } + scale_cursor_distance(1.0 / zoom_factor); } } break; case MouseButton::WHEEL_DOWN: { - if (b->is_alt_pressed()) { - scale_fov(0.05); + if (is_freelook_active()) { + scale_freelook_speed(1.0 / zoom_factor); } else { - if (is_freelook_active()) { - scale_freelook_speed(1.0 / zoom_factor); - } else { - scale_cursor_distance(zoom_factor); - } + scale_cursor_distance(zoom_factor); } } break; case MouseButton::RIGHT: { diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 3323d865c2..7d350fd46f 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -160,6 +160,11 @@ void Sprite2DEditor::_menu_option(int p_option) { } void Sprite2DEditor::_update_mesh_data() { + if (node->get_owner() != get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("Can't convert a Sprite2D from a foreign scene.")); + err_dialog->popup_centered(); + } + Ref<Texture2D> texture = node->get_texture(); if (texture.is_null()) { err_dialog->set_text(TTR("Sprite2D is empty!")); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 421e56f9a1..6c544e4437 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -358,6 +358,7 @@ static const char *gdscript_function_renames[][2] = { { "get_scancode_with_modifiers", "get_keycode_with_modifiers" }, // InputEventKey { "get_shift", "is_shift_pressed" }, // InputEventWithModifiers { "get_size_override", "get_size_2d_override" }, // SubViewport + { "get_slide_count", "get_slide_collision_count" }, // CharacterBody2D, CharacterBody3D { "get_slips_on_slope", "get_slide_on_slope" }, // SeparationRayShape2D, SeparationRayShape3D { "get_space_override_mode", "get_gravity_space_override_mode" }, // Area2D { "get_speed", "get_velocity" }, // InputEventMouseMotion @@ -2024,8 +2025,8 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer ®_container) valid = valid & test_conversion_single_additional_builtin("OS.window_fullscreen = Settings.fullscreen", "ProjectSettings.set(\\\"display/window/size/fullscreen\\\", Settings.fullscreen)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true); valid = valid & test_conversion_single_additional_builtin("OS.get_window_safe_area()", "DisplayServer.get_display_safe_area()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("list_dir_begin( a )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -2071,7 +2072,7 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer ®_container) valid = valid & test_conversion_single_additional_builtin("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("yield(this, \"timeout\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); - valid = valid & test_conversion_single_additional_builtin("yield(this, \"timeout\")", "await this.\"timeout\"", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true); + valid = valid & test_conversion_single_additional_builtin("yield(this, \\\"timeout\\\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true); valid = valid & test_conversion_single_additional_builtin(" Transform.xform(Vector3(a,b,c)) ", " Transform * Vector3(a,b,c) ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin(" Transform.xform_inv(Vector3(a,b,c)) ", " Vector3(a,b,c) * Transform ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -2105,11 +2106,17 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer ®_container) valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,D,E) != OK):", "(connect(A,Callable(B,C).bind(D),E) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(start(A,B) != OK):", "(start(Callable(A,B)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("func start(A,B):", "func start(A,B):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(start(A,B,C,D,E,F,G) != OK):", "(start(Callable(A,B).bind(C),D,E,F,G) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("disconnect(A,B,C) != OK):", "disconnect(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("is_connected(A,B,C) != OK):", "is_connected(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("is_connected(A,B,C))", "is_connected(A,Callable(B,C)))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(tween_method(A,B,C,D,E).foo())", "(tween_method(Callable(A,B),C,D,E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(tween_method(A,B,C,D,E,[F,G]).foo())", "(tween_method(Callable(A,B).bind(F,G),C,D,E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(tween_callback(A,B).foo())", "(tween_callback(Callable(A,B)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(tween_callback(A,B,[C,D]).foo())", "(tween_callback(Callable(A,B).bind(C,D)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("func _init(p_x:int)->void:", "func _init(p_x:int):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -2768,7 +2775,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai line = reg_container.reg_os_fullscreen.sub(line, "ProjectSettings.set(\"display/window/size/fullscreen\", $1)", true); } - // -- r.move_and_slide( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody + // -- r.move_and_slide( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody if (line.find("move_and_slide(") != -1) { int start = line.find("move_and_slide("); int end = get_end_parenthess(line.substr(start)) + 1; @@ -2781,7 +2788,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai String line_new; // motion_velocity - line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n"; + line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n"; // up_direction if (parts.size() >= 2) { @@ -2808,12 +2815,13 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n"; } - line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start); + line_new += starting_space + base_obj + "move_and_slide()\n"; + line = line_new + line.substr(0, start) + "velocity" + line.substr(end + start); } } } - // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody + // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody if (line.find("move_and_slide_with_snap(") != -1) { int start = line.find("move_and_slide_with_snap("); int end = get_end_parenthess(line.substr(start)) + 1; @@ -2826,7 +2834,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai String line_new; // motion_velocity - line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n"; + line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n"; // snap if (parts.size() >= 2) { @@ -2858,7 +2866,8 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n"; } - line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start); + line_new += starting_space + base_obj + "move_and_slide()\n"; + line = line_new + line.substr(0, start) + "velocity" + line.substr(end + start); // move_and_slide used to return velocity } } } @@ -2923,7 +2932,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai Vector<String> parts = parse_arguments(line.substr(start, end)); if (parts.size() == 2) { if (builtin) { - line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace(" ", "") + line.substr(end + start); + line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").replace(" ", "") + line.substr(end + start); } else { line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start); } @@ -3007,17 +3016,47 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai } } } - // -- start(a,b) -> start(Callable(a,b)) Thread - // -- start(a,b,c,d) -> start(Callable(a,b).bind(c),d) Thread - if (line.find("start(") != -1) { - int start = line.find("start("); + // -- "(tween_method(A,B,C,D,E) != OK):", "(tween_method(Callable(A,B),C,D,E) Object + // -- "(tween_method(A,B,C,D,E,[F,G]) != OK):", "(tween_method(Callable(A,B).bind(F,G),C,D,E) Object + if (line.find("tween_method(") != -1) { + int start = line.find("tween_method("); + int end = get_end_parenthess(line.substr(start)) + 1; + if (end > -1) { + Vector<String> parts = parse_arguments(line.substr(start, end)); + if (parts.size() == 5) { + line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + "," + parts[1] + ")," + parts[2] + "," + parts[3] + "," + parts[4] + ")" + line.substr(end + start); + } else if (parts.size() >= 6) { + line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + "," + parts[1] + ").bind(" + connect_arguments(parts, 5).substr(1).lstrip("[").rstrip("]") + ")," + parts[2] + "," + parts[3] + "," + parts[4] + ")" + line.substr(end + start); + } + } + } + // -- "(tween_callback(A,B,[C,D]) != OK):", "(connect(Callable(A,B).bind(C,D)) Object + if (line.find("tween_callback(") != -1) { + int start = line.find("tween_callback("); int end = get_end_parenthess(line.substr(start)) + 1; if (end > -1) { Vector<String> parts = parse_arguments(line.substr(start, end)); if (parts.size() == 2) { - line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start); + line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start); } else if (parts.size() >= 3) { - line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start); + line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + "," + parts[1] + ").bind(" + connect_arguments(parts, 2).substr(1).lstrip("[").rstrip("]") + "))" + line.substr(end + start); + } + } + } + // -- start(a,b) -> start(Callable(a,b)) Thread + // -- start(a,b,c,d) -> start(Callable(a,b).bind(c),d) Thread + if (line.find("start(") != -1) { + int start = line.find("start("); + int end = get_end_parenthess(line.substr(start)) + 1; + // Protection from 'func start' + if (!line.begins_with("func ")) { + if (end > -1) { + Vector<String> parts = parse_arguments(line.substr(start, end)); + if (parts.size() == 2) { + line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start); + } else if (parts.size() >= 3) { + line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start); + } } } } diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 282cfa80e8..ad83db9b60 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -400,7 +400,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } _update_visibility_color(p_node, item); - } else if (p_node->is_class("CanvasLayer")) { + } else if (p_node->is_class("CanvasLayer") || p_node->is_class("Window")) { bool v = p_node->call("is_visible"); if (v) { item->add_button(0, get_theme_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility")); @@ -490,10 +490,7 @@ void SceneTreeEditor::_node_visibility_changed(Node *p_node) { bool visible = false; - if (p_node->is_class("CanvasItem")) { - visible = p_node->call("is_visible"); - CanvasItemEditor::get_singleton()->get_viewport_control()->update(); - } else if (p_node->is_class("CanvasLayer")) { + if (p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) { visible = p_node->call("is_visible"); CanvasItemEditor::get_singleton()->get_viewport_control()->update(); } else if (p_node->is_class("Node3D")) { @@ -539,7 +536,7 @@ void SceneTreeEditor::_node_removed(Node *p_node) { p_node->disconnect("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed)); } - if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer")) { + if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) { if (p_node->is_connected("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed))) { p_node->disconnect("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed)); } diff --git a/main/main.cpp b/main/main.cpp index 4c4377a174..965fcc66c6 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1481,7 +1481,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph OS::get_singleton()->_allow_layered = false; } - OS::get_singleton()->_keep_screen_on = GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); if (rtm == -1) { rtm = GLOBAL_DEF("rendering/driver/threads/thread_model", OS::RENDER_THREAD_SAFE); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 7ed440929c..a07d4855f3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3238,12 +3238,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid); if (!valid) { push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index); + result_type.kind = GDScriptParser::DataType::VARIANT; } else { p_subscript->is_constant = true; p_subscript->reduced_value = value; result_type = type_from_variant(value, p_subscript); } - result_type.kind = GDScriptParser::DataType::VARIANT; } else { GDScriptParser::DataType base_type = p_subscript->base->get_datatype(); diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index 0722fb800e..90f6d7f7e8 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -69,6 +69,40 @@ func _init(): CHECK_MESSAGE(int(ref_counted->get_meta("result")) == 42, "The script should assign object metadata successfully."); } +TEST_CASE("[Modules][GDScript] Validate built-in API") { + GDScriptLanguage *lang = GDScriptLanguage::get_singleton(); + + // Validate methods. + List<MethodInfo> builtin_methods; + lang->get_public_functions(&builtin_methods); + + SUBCASE("[Modules][GDScript] Validate built-in methods") { + for (const MethodInfo &mi : builtin_methods) { + for (int j = 0; j < mi.arguments.size(); j++) { + PropertyInfo arg = mi.arguments[j]; + + TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")), + vformat("Unnamed argument in position %d of built-in method '%s'.", j, mi.name)); + } + } + } + + // Validate annotations. + List<MethodInfo> builtin_annotations; + lang->get_public_annotations(&builtin_annotations); + + SUBCASE("[Modules][GDScript] Validate built-in annotations") { + for (const MethodInfo &ai : builtin_annotations) { + for (int j = 0; j < ai.arguments.size(); j++) { + PropertyInfo arg = ai.arguments[j]; + + TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")), + vformat("Unnamed argument in position %d of built-in annotation '%s'.", j, ai.name)); + } + } + } +} + } // namespace GDScriptTests #endif // GDSCRIPT_TEST_RUNNER_SUITE_H diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd index fb0ace6a90..ea744e3027 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd @@ -1,3 +1,5 @@ +const A := 42 + func test(): pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd new file mode 100644 index 0000000000..276875dd5a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.gd @@ -0,0 +1,6 @@ +const Constants = preload("gdscript_to_preload.gd") + +func test(): + var a := Constants.A + print(a) + diff --git a/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out new file mode 100644 index 0000000000..0982f3718c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/preload_constant_types_are_inferred.out @@ -0,0 +1,2 @@ +GDTEST_OK +42 diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index ac29efb716..3440eb701c 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -66,6 +66,9 @@ namespace GodotTools.Ides.Rider if (string.IsNullOrEmpty(path)) return false; + if (path.IndexOfAny(Path.GetInvalidPathChars()) != -1) + return false; + var fileInfo = new FileInfo(path); string filename = fileInfo.Name.ToLowerInvariant(); return filename.StartsWith("rider", StringComparison.Ordinal); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs index fc9d40ca48..a6324504fc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs @@ -916,7 +916,7 @@ namespace Godot /// <c>new Color(1 - c.r, 1 - c.g, 1 - c.b, 1 - c.a)</c>. /// </summary> /// <param name="color">The color to invert.</param> - /// <returns>The inverted color</returns> + /// <returns>The inverted color.</returns> public static Color operator -(Color color) { return Colors.White - color; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index bb076a9633..236d0666bc 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -239,11 +239,20 @@ namespace Godot } /// <summary> - /// Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output. - /// When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough. + /// Converts one or more arguments of any type to string in the best way possible + /// and prints them to the console. + /// The following BBCode tags are supported: b, i, u, s, indent, code, url, center, + /// right, color, bgcolor, fgcolor. + /// Color tags only support named colors such as <c>red</c>, not hexadecimal color codes. + /// Unsupported tags will be left as-is in standard output. + /// When printing to standard output, the supported subset of BBCode is converted to + /// ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes + /// is currently only supported on Linux and macOS. Support for ANSI escape codes may vary + /// across terminal emulators, especially for italic and strikethrough. /// /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/> - /// to print error and warning messages instead of <see cref="Print(object[])"/> or <see cref="PrintRich(object[])"/>. + /// to print error and warning messages instead of <see cref="Print(object[])"/> or + /// <see cref="PrintRich(object[])"/>. /// This distinguishes them from print messages used for debugging purposes, /// while also displaying a stack trace when an error or warning is printed. /// </summary> @@ -253,7 +262,6 @@ namespace Godot /// </code> /// </example> /// <param name="what">Arguments that will be printed.</param> - /// </summary> public static void PrintRich(params object[] what) { godot_icall_GD_print_rich(GetPrintParams(what)); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs index 5680c9d55a..da01300586 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs @@ -18,7 +18,7 @@ namespace Godot public StringName Name => _signalName; /// <summary> - /// Creates a new <see cref="Signal"/> with the name <paramref name="name"/> + /// Creates a new <see cref="SignalInfo"/> with the name <paramref name="name"/> /// in the specified <paramref name="owner"/>. /// </summary> /// <param name="owner">Object that contains the signal.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 9c80dd0217..67f70390dd 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -534,7 +534,7 @@ namespace Godot /// /// This method also handles interpolating the lengths if the input vectors /// have different lengths. For the special case of one or both input vectors - /// having zero length, this method behaves like <see cref="Lerp"/>. + /// having zero length, this method behaves like <see cref="Lerp(Vector2, real_t)"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index c3d5fbfdef..67a98efc2d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -574,7 +574,7 @@ namespace Godot /// /// This method also handles interpolating the lengths if the input vectors /// have different lengths. For the special case of one or both input vectors - /// having zero length, this method behaves like <see cref="Lerp"/>. + /// having zero length, this method behaves like <see cref="Lerp(Vector3, real_t)"/>. /// </summary> /// <param name="to">The destination vector for interpolation.</param> /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param> diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 6e8ac77f79..848e554fb0 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -42,6 +42,7 @@ #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/height_map_shape_3d.h" #include "scene/resources/primitive_meshes.h" #include "scene/resources/shape_3d.h" #include "scene/resources/sphere_shape_3d.h" @@ -275,6 +276,50 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans _add_faces(faces, transform, p_vertices, p_indices); } } + + HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s); + if (heightmap_shape) { + int heightmap_depth = heightmap_shape->get_map_depth(); + int heightmap_width = heightmap_shape->get_map_width(); + + if (heightmap_depth >= 2 && heightmap_width >= 2) { + const Vector<real_t> &map_data = heightmap_shape->get_map_data(); + + Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); + Vector2 start = heightmap_gridsize * -0.5; + + Vector<Vector3> vertex_array; + vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); + int map_data_current_index = 0; + + for (int d = 0; d < heightmap_depth - 1; d++) { + for (int w = 0; w < heightmap_width - 1; w++) { + if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { + float top_left_height = map_data[map_data_current_index]; + float top_right_height = map_data[map_data_current_index + 1]; + float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; + float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; + + Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); + Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); + Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); + Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); + + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_left); + vertex_array.push_back(top_left); + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_right); + vertex_array.push_back(bottom_left); + } + map_data_current_index += 1; + } + } + if (vertex_array.size() > 0) { + _add_faces(vertex_array, transform, p_vertices, p_indices); + } + } + } } } } @@ -362,6 +407,50 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans PackedVector3Array faces = Variant(dict["faces"]); _add_faces(faces, shapes[i], p_vertices, p_indices); } break; + case PhysicsServer3D::SHAPE_HEIGHTMAP: { + Dictionary dict = data; + ///< dict( int:"width", int:"depth",float:"cell_size", float_array:"heights" + int heightmap_depth = dict["depth"]; + int heightmap_width = dict["width"]; + + if (heightmap_depth >= 2 && heightmap_width >= 2) { + const Vector<real_t> &map_data = dict["heights"]; + + Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1); + Vector2 start = heightmap_gridsize * -0.5; + + Vector<Vector3> vertex_array; + vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6); + int map_data_current_index = 0; + + for (int d = 0; d < heightmap_depth - 1; d++) { + for (int w = 0; w < heightmap_width - 1; w++) { + if (map_data_current_index + 1 + heightmap_depth < map_data.size()) { + float top_left_height = map_data[map_data_current_index]; + float top_right_height = map_data[map_data_current_index + 1]; + float bottom_left_height = map_data[map_data_current_index + heightmap_depth]; + float bottom_right_height = map_data[map_data_current_index + 1 + heightmap_depth]; + + Vector3 top_left = Vector3(start.x + w, top_left_height, start.y + d); + Vector3 top_right = Vector3(start.x + w + 1.0, top_right_height, start.y + d); + Vector3 bottom_left = Vector3(start.x + w, bottom_left_height, start.y + d + 1.0); + Vector3 bottom_right = Vector3(start.x + w + 1.0, bottom_right_height, start.y + d + 1.0); + + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_left); + vertex_array.push_back(top_left); + vertex_array.push_back(top_right); + vertex_array.push_back(bottom_right); + vertex_array.push_back(bottom_left); + } + map_data_current_index += 1; + } + } + if (vertex_array.size() > 0) { + _add_faces(vertex_array, shapes[i], p_vertices, p_indices); + } + } + } break; default: { WARN_PRINT("Unsupported collision shape type."); } break; diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 81802298d9..6427346365 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -56,10 +56,10 @@ void AndroidInputHandler::_set_key_modifier_state(Ref<InputEventWithModifiers> e ev->set_ctrl_pressed(control_mem); } -void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed) { +void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed) { static char32_t prev_wc = 0; - char32_t unicode = p_unicode_char; - if ((p_unicode_char & 0xfffffc00) == 0xd800) { + char32_t unicode = p_unicode; + if ((p_unicode & 0xfffffc00) == 0xd800) { if (prev_wc != 0) { ERR_PRINT("invalid utf16 surrogate input"); } @@ -78,39 +78,38 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_scancode, int p Ref<InputEventKey> ev; ev.instantiate(); - int val = unicode; - Key keycode = android_get_keysym(p_keycode); - Key phy_keycode = android_get_keysym(p_scancode); - if (keycode == Key::SHIFT) { - shift_mem = p_pressed; + Key physical_keycode = godot_code_from_android_code(p_physical_keycode); + Key keycode = physical_keycode; + if (p_keycode != 0) { + keycode = godot_code_from_unicode(p_keycode); } - if (keycode == Key::ALT) { - alt_mem = p_pressed; - } - if (keycode == Key::CTRL) { - control_mem = p_pressed; - } - if (keycode == Key::META) { - meta_mem = p_pressed; + + switch (physical_keycode) { + case Key::SHIFT: { + shift_mem = p_pressed; + } break; + case Key::ALT: { + alt_mem = p_pressed; + } break; + case Key::CTRL: { + control_mem = p_pressed; + } break; + case Key::META: { + meta_mem = p_pressed; + } break; + default: + break; } ev->set_keycode(keycode); - ev->set_physical_keycode(phy_keycode); - ev->set_unicode(val); + ev->set_physical_keycode(physical_keycode); + ev->set_unicode(unicode); ev->set_pressed(p_pressed); _set_key_modifier_state(ev); - if (val == '\n') { - ev->set_keycode(Key::ENTER); - } else if (val == 61448) { - ev->set_keycode(Key::BACKSPACE); - ev->set_unicode((char32_t)Key::BACKSPACE); - } else if (val == 61453) { - ev->set_keycode(Key::ENTER); - ev->set_unicode((char32_t)Key::ENTER); - } else if (p_keycode == 4) { + if (p_physical_keycode == AKEYCODE_BACK) { if (DisplayServerAndroid *dsa = Object::cast_to<DisplayServerAndroid>(DisplayServer::get_singleton())) { dsa->send_window_event(DisplayServer::WINDOW_EVENT_GO_BACK_REQUEST, true); } diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index 1397ca59e4..6dfab7def8 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -83,7 +83,7 @@ public: void process_mouse_event(int input_device, int event_action, int event_android_buttons_mask, Point2 event_pos, float event_vertical_factor = 0, float event_horizontal_factor = 0); void process_double_tap(int event_android_button_mask, Point2 p_pos); void process_joy_event(JoypadEvent p_event); - void process_key_event(int p_keycode, int p_scancode, int p_unicode_char, bool p_pressed); + void process_key_event(int p_keycode, int p_physical_keycode, int p_unicode, bool p_pressed); }; #endif // ANDROID_INPUT_HANDLER_H diff --git a/platform/android/android_keys_utils.cpp b/platform/android/android_keys_utils.cpp index 885e4ff145..d2c5fdfd6c 100644 --- a/platform/android/android_keys_utils.cpp +++ b/platform/android/android_keys_utils.cpp @@ -30,12 +30,49 @@ #include "android_keys_utils.h" -Key android_get_keysym(unsigned int p_code) { - for (int i = 0; _ak_to_keycode[i].keysym != Key::UNKNOWN; i++) { - if (_ak_to_keycode[i].keycode == p_code) { - return _ak_to_keycode[i].keysym; +Key godot_code_from_android_code(unsigned int p_code) { + for (int i = 0; android_godot_code_pairs[i].android_code != AKEYCODE_MAX; i++) { + if (android_godot_code_pairs[i].android_code == p_code) { + return android_godot_code_pairs[i].godot_code; } } - return Key::UNKNOWN; } + +Key godot_code_from_unicode(unsigned int p_code) { + unsigned int code = p_code; + if (code > 0xFF) { + return Key::UNKNOWN; + } + // Known control codes. + if (code == '\b') { // 0x08 + return Key::BACKSPACE; + } + if (code == '\t') { // 0x09 + return Key::TAB; + } + if (code == '\n') { // 0x0A + return Key::ENTER; + } + if (code == 0x1B) { + return Key::ESCAPE; + } + if (code == 0x1F) { + return Key::KEY_DELETE; + } + // Unknown control codes. + if (code <= 0x1F || (code >= 0x80 && code <= 0x9F)) { + return Key::UNKNOWN; + } + // Convert to uppercase. + if (code >= 'a' && code <= 'z') { // 0x61 - 0x7A + code -= ('a' - 'A'); + } + if (code >= u'à ' && code <= u'ö') { // 0xE0 - 0xF6 + code -= (u'à ' - u'À'); // 0xE0 - 0xC0 + } + if (code >= u'ø' && code <= u'þ') { // 0xF8 - 0xFF + code -= (u'ø' - u'Ø'); // 0xF8 - 0xD8 + } + return Key(code); +} diff --git a/platform/android/android_keys_utils.h b/platform/android/android_keys_utils.h index 24a6589fba..5ec3ee17aa 100644 --- a/platform/android/android_keys_utils.h +++ b/platform/android/android_keys_utils.h @@ -34,129 +34,140 @@ #include <android/input.h> #include <core/os/keyboard.h> -struct _WinTranslatePair { - Key keysym = Key::NONE; - unsigned int keycode = 0; +#define AKEYCODE_MAX 0xFFFF + +struct AndroidGodotCodePair { + unsigned int android_code = 0; + Key godot_code = Key::NONE; }; -static _WinTranslatePair _ak_to_keycode[] = { - { Key::TAB, AKEYCODE_TAB }, - { Key::ENTER, AKEYCODE_ENTER }, - { Key::SHIFT, AKEYCODE_SHIFT_LEFT }, - { Key::SHIFT, AKEYCODE_SHIFT_RIGHT }, - { Key::ALT, AKEYCODE_ALT_LEFT }, - { Key::ALT, AKEYCODE_ALT_RIGHT }, - { Key::MENU, AKEYCODE_MENU }, - { Key::PAUSE, AKEYCODE_MEDIA_PLAY_PAUSE }, - { Key::ESCAPE, AKEYCODE_BACK }, - { Key::SPACE, AKEYCODE_SPACE }, - { Key::PAGEUP, AKEYCODE_PAGE_UP }, - { Key::PAGEDOWN, AKEYCODE_PAGE_DOWN }, - { Key::HOME, AKEYCODE_HOME }, //(0x24) - { Key::LEFT, AKEYCODE_DPAD_LEFT }, - { Key::UP, AKEYCODE_DPAD_UP }, - { Key::RIGHT, AKEYCODE_DPAD_RIGHT }, - { Key::DOWN, AKEYCODE_DPAD_DOWN }, - { Key::PERIODCENTERED, AKEYCODE_DPAD_CENTER }, - { Key::BACKSPACE, AKEYCODE_DEL }, - { Key::KEY_0, AKEYCODE_0 }, - { Key::KEY_1, AKEYCODE_1 }, - { Key::KEY_2, AKEYCODE_2 }, - { Key::KEY_3, AKEYCODE_3 }, - { Key::KEY_4, AKEYCODE_4 }, - { Key::KEY_5, AKEYCODE_5 }, - { Key::KEY_6, AKEYCODE_6 }, - { Key::KEY_7, AKEYCODE_7 }, - { Key::KEY_8, AKEYCODE_8 }, - { Key::KEY_9, AKEYCODE_9 }, - { Key::A, AKEYCODE_A }, - { Key::B, AKEYCODE_B }, - { Key::C, AKEYCODE_C }, - { Key::D, AKEYCODE_D }, - { Key::E, AKEYCODE_E }, - { Key::F, AKEYCODE_F }, - { Key::G, AKEYCODE_G }, - { Key::H, AKEYCODE_H }, - { Key::I, AKEYCODE_I }, - { Key::J, AKEYCODE_J }, - { Key::K, AKEYCODE_K }, - { Key::L, AKEYCODE_L }, - { Key::M, AKEYCODE_M }, - { Key::N, AKEYCODE_N }, - { Key::O, AKEYCODE_O }, - { Key::P, AKEYCODE_P }, - { Key::Q, AKEYCODE_Q }, - { Key::R, AKEYCODE_R }, - { Key::S, AKEYCODE_S }, - { Key::T, AKEYCODE_T }, - { Key::U, AKEYCODE_U }, - { Key::V, AKEYCODE_V }, - { Key::W, AKEYCODE_W }, - { Key::X, AKEYCODE_X }, - { Key::Y, AKEYCODE_Y }, - { Key::Z, AKEYCODE_Z }, - { Key::HOMEPAGE, AKEYCODE_EXPLORER }, - { Key::LAUNCH0, AKEYCODE_BUTTON_A }, - { Key::LAUNCH1, AKEYCODE_BUTTON_B }, - { Key::LAUNCH2, AKEYCODE_BUTTON_C }, - { Key::LAUNCH3, AKEYCODE_BUTTON_X }, - { Key::LAUNCH4, AKEYCODE_BUTTON_Y }, - { Key::LAUNCH5, AKEYCODE_BUTTON_Z }, - { Key::LAUNCH6, AKEYCODE_BUTTON_L1 }, - { Key::LAUNCH7, AKEYCODE_BUTTON_R1 }, - { Key::LAUNCH8, AKEYCODE_BUTTON_L2 }, - { Key::LAUNCH9, AKEYCODE_BUTTON_R2 }, - { Key::LAUNCHA, AKEYCODE_BUTTON_THUMBL }, - { Key::LAUNCHB, AKEYCODE_BUTTON_THUMBR }, - { Key::LAUNCHC, AKEYCODE_BUTTON_START }, - { Key::LAUNCHD, AKEYCODE_BUTTON_SELECT }, - { Key::LAUNCHE, AKEYCODE_BUTTON_MODE }, - { Key::VOLUMEMUTE, AKEYCODE_MUTE }, - { Key::VOLUMEDOWN, AKEYCODE_VOLUME_DOWN }, - { Key::VOLUMEUP, AKEYCODE_VOLUME_UP }, - { Key::BACK, AKEYCODE_MEDIA_REWIND }, - { Key::FORWARD, AKEYCODE_MEDIA_FAST_FORWARD }, - { Key::MEDIANEXT, AKEYCODE_MEDIA_NEXT }, - { Key::MEDIAPREVIOUS, AKEYCODE_MEDIA_PREVIOUS }, - { Key::MEDIASTOP, AKEYCODE_MEDIA_STOP }, - { Key::PLUS, AKEYCODE_PLUS }, - { Key::EQUAL, AKEYCODE_EQUALS }, // the '+' key - { Key::COMMA, AKEYCODE_COMMA }, // the ',' key - { Key::MINUS, AKEYCODE_MINUS }, // the '-' key - { Key::SLASH, AKEYCODE_SLASH }, // the '/?' key - { Key::BACKSLASH, AKEYCODE_BACKSLASH }, - { Key::BRACKETLEFT, AKEYCODE_LEFT_BRACKET }, - { Key::BRACKETRIGHT, AKEYCODE_RIGHT_BRACKET }, - { Key::CTRL, AKEYCODE_CTRL_LEFT }, - { Key::CTRL, AKEYCODE_CTRL_RIGHT }, - { Key::UNKNOWN, 0 } +static AndroidGodotCodePair android_godot_code_pairs[] = { + { AKEYCODE_UNKNOWN, Key::UNKNOWN }, // (0) Unknown key code. + { AKEYCODE_HOME, Key::HOME }, // (3) Home key. + { AKEYCODE_BACK, Key::BACK }, // (4) Back key. + { AKEYCODE_0, Key::KEY_0 }, // (7) '0' key. + { AKEYCODE_1, Key::KEY_1 }, // (8) '1' key. + { AKEYCODE_2, Key::KEY_2 }, // (9) '2' key. + { AKEYCODE_3, Key::KEY_3 }, // (10) '3' key. + { AKEYCODE_4, Key::KEY_4 }, // (11) '4' key. + { AKEYCODE_5, Key::KEY_5 }, // (12) '5' key. + { AKEYCODE_6, Key::KEY_6 }, // (13) '6' key. + { AKEYCODE_7, Key::KEY_7 }, // (14) '7' key. + { AKEYCODE_8, Key::KEY_8 }, // (15) '8' key. + { AKEYCODE_9, Key::KEY_9 }, // (16) '9' key. + { AKEYCODE_STAR, Key::ASTERISK }, // (17) '*' key. + { AKEYCODE_POUND, Key::NUMBERSIGN }, // (18) '#' key. + { AKEYCODE_DPAD_UP, Key::UP }, // (19) Directional Pad Up key. + { AKEYCODE_DPAD_DOWN, Key::DOWN }, // (20) Directional Pad Down key. + { AKEYCODE_DPAD_LEFT, Key::LEFT }, // (21) Directional Pad Left key. + { AKEYCODE_DPAD_RIGHT, Key::RIGHT }, // (22) Directional Pad Right key. + { AKEYCODE_VOLUME_UP, Key::VOLUMEUP }, // (24) Volume Up key. + { AKEYCODE_VOLUME_DOWN, Key::VOLUMEDOWN }, // (25) Volume Down key. + { AKEYCODE_CLEAR, Key::CLEAR }, // (28) Clear key. + { AKEYCODE_A, Key::A }, // (29) 'A' key. + { AKEYCODE_B, Key::B }, // (30) 'B' key. + { AKEYCODE_C, Key::C }, // (31) 'C' key. + { AKEYCODE_D, Key::D }, // (32) 'D' key. + { AKEYCODE_E, Key::E }, // (33) 'E' key. + { AKEYCODE_F, Key::F }, // (34) 'F' key. + { AKEYCODE_G, Key::G }, // (35) 'G' key. + { AKEYCODE_H, Key::H }, // (36) 'H' key. + { AKEYCODE_I, Key::I }, // (37) 'I' key. + { AKEYCODE_J, Key::J }, // (38) 'J' key. + { AKEYCODE_K, Key::K }, // (39) 'K' key. + { AKEYCODE_L, Key::L }, // (40) 'L' key. + { AKEYCODE_M, Key::M }, // (41) 'M' key. + { AKEYCODE_N, Key::N }, // (42) 'N' key. + { AKEYCODE_O, Key::O }, // (43) 'O' key. + { AKEYCODE_P, Key::P }, // (44) 'P' key. + { AKEYCODE_Q, Key::Q }, // (45) 'Q' key. + { AKEYCODE_R, Key::R }, // (46) 'R' key. + { AKEYCODE_S, Key::S }, // (47) 'S' key. + { AKEYCODE_T, Key::T }, // (48) 'T' key. + { AKEYCODE_U, Key::U }, // (49) 'U' key. + { AKEYCODE_V, Key::V }, // (50) 'V' key. + { AKEYCODE_W, Key::W }, // (51) 'W' key. + { AKEYCODE_X, Key::X }, // (52) 'X' key. + { AKEYCODE_Y, Key::Y }, // (53) 'Y' key. + { AKEYCODE_Z, Key::Z }, // (54) 'Z' key. + { AKEYCODE_COMMA, Key::COMMA }, // (55) ',’ key. + { AKEYCODE_PERIOD, Key::PERIOD }, // (56) '.' key. + { AKEYCODE_ALT_LEFT, Key::ALT }, // (57) Left Alt modifier key. + { AKEYCODE_ALT_RIGHT, Key::ALT }, // (58) Right Alt modifier key. + { AKEYCODE_SHIFT_LEFT, Key::SHIFT }, // (59) Left Shift modifier key. + { AKEYCODE_SHIFT_RIGHT, Key::SHIFT }, // (60) Right Shift modifier key. + { AKEYCODE_TAB, Key::TAB }, // (61) Tab key. + { AKEYCODE_SPACE, Key::SPACE }, // (62) Space key. + { AKEYCODE_ENTER, Key::ENTER }, // (66) Enter key. + { AKEYCODE_DEL, Key::BACKSPACE }, // (67) Backspace key. + { AKEYCODE_GRAVE, Key::QUOTELEFT }, // (68) '`' (backtick) key. + { AKEYCODE_MINUS, Key::MINUS }, // (69) '-'. + { AKEYCODE_EQUALS, Key::EQUAL }, // (70) '=' key. + { AKEYCODE_LEFT_BRACKET, Key::BRACKETLEFT }, // (71) '[' key. + { AKEYCODE_RIGHT_BRACKET, Key::BRACKETRIGHT }, // (72) ']' key. + { AKEYCODE_BACKSLASH, Key::BACKSLASH }, // (73) '\' key. + { AKEYCODE_SEMICOLON, Key::SEMICOLON }, // (74) ';' key. + { AKEYCODE_APOSTROPHE, Key::APOSTROPHE }, // (75) ''' (apostrophe) key. + { AKEYCODE_SLASH, Key::SLASH }, // (76) '/' key. + { AKEYCODE_AT, Key::AT }, // (77) '@' key. + { AKEYCODE_PLUS, Key::PLUS }, // (81) '+' key. + { AKEYCODE_MENU, Key::MENU }, // (82) Menu key. + { AKEYCODE_SEARCH, Key::SEARCH }, // (84) Search key. + { AKEYCODE_MEDIA_STOP, Key::MEDIASTOP }, // (86) Stop media key. + { AKEYCODE_MEDIA_PREVIOUS, Key::MEDIAPREVIOUS }, // (88) Play Previous media key. + { AKEYCODE_PAGE_UP, Key::PAGEUP }, // (92) Page Up key. + { AKEYCODE_PAGE_DOWN, Key::PAGEDOWN }, // (93) Page Down key. + { AKEYCODE_ESCAPE, Key::ESCAPE }, // (111) Escape key. + { AKEYCODE_FORWARD_DEL, Key::KEY_DELETE }, // (112) Forward Delete key. + { AKEYCODE_CTRL_LEFT, Key::CTRL }, // (113) Left Control modifier key. + { AKEYCODE_CTRL_RIGHT, Key::CTRL }, // (114) Right Control modifier key. + { AKEYCODE_CAPS_LOCK, Key::CAPSLOCK }, // (115) Caps Lock key. + { AKEYCODE_SCROLL_LOCK, Key::SCROLLLOCK }, // (116) Scroll Lock key. + { AKEYCODE_META_LEFT, Key::META }, // (117) Left Meta modifier key. + { AKEYCODE_META_RIGHT, Key::META }, // (118) Right Meta modifier key. + { AKEYCODE_SYSRQ, Key::PRINT }, // (120) System Request / Print Screen key. + { AKEYCODE_BREAK, Key::PAUSE }, // (121) Break / Pause key. + { AKEYCODE_INSERT, Key::INSERT }, // (124) Insert key. + { AKEYCODE_FORWARD, Key::FORWARD }, // (125) Forward key. + { AKEYCODE_MEDIA_PLAY, Key::MEDIAPLAY }, // (126) Play media key. + { AKEYCODE_MEDIA_RECORD, Key::MEDIARECORD }, // (130) Record media key. + { AKEYCODE_F1, Key::F1 }, // (131) F1 key. + { AKEYCODE_F2, Key::F2 }, // (132) F2 key. + { AKEYCODE_F3, Key::F3 }, // (133) F3 key. + { AKEYCODE_F4, Key::F4 }, // (134) F4 key. + { AKEYCODE_F5, Key::F5 }, // (135) F5 key. + { AKEYCODE_F6, Key::F6 }, // (136) F6 key. + { AKEYCODE_F7, Key::F7 }, // (137) F7 key. + { AKEYCODE_F8, Key::F8 }, // (138) F8 key. + { AKEYCODE_F9, Key::F9 }, // (139) F9 key. + { AKEYCODE_F10, Key::F10 }, // (140) F10 key. + { AKEYCODE_F11, Key::F11 }, // (141) F11 key. + { AKEYCODE_F12, Key::F12 }, // (142) F12 key. + { AKEYCODE_NUM_LOCK, Key::NUMLOCK }, // (143) Num Lock key. + { AKEYCODE_NUMPAD_0, Key::KP_0 }, // (144) Numeric keypad '0' key. + { AKEYCODE_NUMPAD_1, Key::KP_1 }, // (145) Numeric keypad '1' key. + { AKEYCODE_NUMPAD_2, Key::KP_2 }, // (146) Numeric keypad '2' key. + { AKEYCODE_NUMPAD_3, Key::KP_3 }, // (147) Numeric keypad '3' key. + { AKEYCODE_NUMPAD_4, Key::KP_4 }, // (148) Numeric keypad '4' key. + { AKEYCODE_NUMPAD_5, Key::KP_5 }, // (149) Numeric keypad '5' key. + { AKEYCODE_NUMPAD_6, Key::KP_6 }, // (150) Numeric keypad '6' key. + { AKEYCODE_NUMPAD_7, Key::KP_7 }, // (151) Numeric keypad '7' key. + { AKEYCODE_NUMPAD_8, Key::KP_8 }, // (152) Numeric keypad '8' key. + { AKEYCODE_NUMPAD_9, Key::KP_9 }, // (153) Numeric keypad '9' key. + { AKEYCODE_NUMPAD_DIVIDE, Key::KP_DIVIDE }, // (154) Numeric keypad '/' key (for division). + { AKEYCODE_NUMPAD_MULTIPLY, Key::KP_MULTIPLY }, // (155) Numeric keypad '*' key (for multiplication). + { AKEYCODE_NUMPAD_SUBTRACT, Key::KP_SUBTRACT }, // (156) Numeric keypad '-' key (for subtraction). + { AKEYCODE_NUMPAD_ADD, Key::KP_ADD }, // (157) Numeric keypad '+' key (for addition). + { AKEYCODE_NUMPAD_DOT, Key::KP_PERIOD }, // (158) Numeric keypad '.' key (for decimals or digit grouping). + { AKEYCODE_NUMPAD_ENTER, Key::KP_ENTER }, // (160) Numeric keypad Enter key. + { AKEYCODE_VOLUME_MUTE, Key::VOLUMEMUTE }, // (164) Volume Mute key. + { AKEYCODE_YEN, Key::YEN }, // (216) Japanese Yen key. + { AKEYCODE_HELP, Key::HELP }, // (259) Help key. + { AKEYCODE_REFRESH, Key::REFRESH }, // (285) Refresh key. + { AKEYCODE_MAX, Key::UNKNOWN } }; -/* -TODO: map these android key: - AKEYCODE_SOFT_LEFT = 1, - AKEYCODE_SOFT_RIGHT = 2, - AKEYCODE_CALL = 5, - AKEYCODE_ENDCALL = 6, - AKEYCODE_STAR = 17, - AKEYCODE_POUND = 18, - AKEYCODE_POWER = 26, - AKEYCODE_CAMERA = 27, - AKEYCODE_CLEAR = 28, - AKEYCODE_SYM = 63, - AKEYCODE_ENVELOPE = 65, - AKEYCODE_GRAVE = 68, - AKEYCODE_SEMICOLON = 74, - AKEYCODE_APOSTROPHE = 75, - AKEYCODE_AT = 77, - AKEYCODE_NUM = 78, - AKEYCODE_HEADSETHOOK = 79, - AKEYCODE_FOCUS = 80, // *Camera* focus - AKEYCODE_NOTIFICATION = 83, - AKEYCODE_SEARCH = 84, - AKEYCODE_PICTSYMBOLS = 94, - AKEYCODE_SWITCH_CHARSET = 95, -*/ -Key android_get_keysym(unsigned int p_code); +Key godot_code_from_android_code(unsigned int p_code); +Key godot_code_from_unicode(unsigned int p_code); #endif // ANDROID_KEYS_UTILS_H diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index e2ae62d9cf..f855fc6cf6 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -151,7 +151,7 @@ public class GodotLib { /** * Forward regular key events from the main thread to the GL thread. */ - public static native void key(int p_keycode, int p_scancode, int p_unicode_char, boolean p_pressed); + public static native void key(int p_keycode, int p_physical_keycode, int p_unicode, boolean p_pressed); /** * Forward game device's key events from the main thread to the GL thread. diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index ccfb865b1a..da15b2490c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -96,10 +96,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { GodotLib.joybutton(godotJoyId, button, false); } } else { - final int scanCode = event.getScanCode(); - final int chr = event.getUnicodeChar(0); - GodotLib.key(keyCode, scanCode, chr, false); - } + // getKeyCode(): The physical key that was pressed. + // Godot's keycodes match the ASCII codes, so for single byte unicode characters, + // we can use the unmodified unicode character to determine Godot's keycode. + final int keycode = event.getUnicodeChar(0); + final int physical_keycode = event.getKeyCode(); + final int unicode = event.getUnicodeChar(); + GodotLib.key(keycode, physical_keycode, unicode, false); + }; return true; } @@ -131,9 +135,10 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { GodotLib.joybutton(godotJoyId, button, true); } } else { - final int scanCode = event.getScanCode(); - final int chr = event.getUnicodeChar(0); - GodotLib.key(keyCode, scanCode, chr, true); + final int keycode = event.getUnicodeChar(0); + final int physical_keycode = event.getKeyCode(); + final int unicode = event.getUnicodeChar(); + GodotLib.key(keycode, physical_keycode, unicode, true); } return true; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index 7714703a21..c959b5f28c 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -92,11 +92,9 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public void beforeTextChanged(final CharSequence pCharSequence, final int start, final int count, final int after) { - //Log.d(TAG, "beforeTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",after: " + after); - for (int i = 0; i < count; ++i) { - GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, true); - GodotLib.key(KeyEvent.KEYCODE_DEL, KeyEvent.KEYCODE_DEL, 0, false); + GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, true); + GodotLib.key(0, KeyEvent.KEYCODE_DEL, 0, false); if (mHasSelection) { mHasSelection = false; @@ -107,8 +105,6 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene @Override public void onTextChanged(final CharSequence pCharSequence, final int start, final int before, final int count) { - //Log.d(TAG, "onTextChanged(" + pCharSequence + ")start: " + start + ",count: " + count + ",before: " + before); - final int[] newChars = new int[count]; for (int i = start; i < start + count; ++i) { newChars[i - start] = pCharSequence.charAt(i); @@ -119,8 +115,8 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene // Return keys are handled through action events continue; } - GodotLib.key(0, 0, key, true); - GodotLib.key(0, 0, key, false); + GodotLib.key(key, 0, key, true); + GodotLib.key(key, 0, key, false); } } @@ -131,16 +127,16 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for (int i = 0; i < characters.length(); i++) { final int ch = characters.codePointAt(i); - GodotLib.key(0, 0, ch, true); - GodotLib.key(0, 0, ch, false); + GodotLib.key(ch, 0, ch, true); + GodotLib.key(ch, 0, ch, false); } } if (pActionID == EditorInfo.IME_ACTION_DONE) { // Enter key has been pressed mRenderView.queueOnRenderThread(() -> { - GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, true); - GodotLib.key(KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_ENTER, 0, false); + GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, true); + GodotLib.key(0, KeyEvent.KEYCODE_ENTER, 0, false); }); mRenderView.getView().requestFocus(); return true; diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index f4de4acfad..422c05e5ce 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -376,12 +376,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged( } // Called on the UI thread -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed) { if (step.get() <= 0) { return; } - - input_handler->process_key_event(p_keycode, p_scancode, p_unicode_char, p_pressed); + input_handler->process_key_event(p_keycode, p_physical_keycode, p_unicode, p_pressed); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_accelerometer(JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z) { diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index de16f197b8..3c48ca0459 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -51,7 +51,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FI(JNIEn JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch__IIII_3FIFF(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask, jfloat vertical_factor, jfloat horizontal_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hover(JNIEnv *env, jclass clazz, jint p_type, jfloat p_x, jfloat p_y); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_doubleTap(JNIEnv *env, jclass clazz, jint p_button_mask, jint p_x, jint p_y); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_scancode, jint p_unicode_char, jboolean p_pressed); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jclass clazz, jint p_keycode, jint p_physical_keycode, jint p_unicode, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jclass clazz, jint p_device, jint p_button, jboolean p_pressed); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jclass clazz, jint p_device, jint p_axis, jfloat p_value); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jclass clazz, jint p_device, jint p_hat_x, jint p_hat_y); diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index 080ee5f3ab..6ce7e676a2 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -128,7 +128,7 @@ DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode } #endif - bool keep_screen_on = bool(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + bool keep_screen_on = bool(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); screen_set_keep_on(keep_screen_on); Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 9ca2948542..2ac44ccf8b 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1528,8 +1528,6 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p //write - file = file.replace_first("ios/", ""); - if (files_to_parse.has(file)) { _fix_config_file(p_preset, data, config_data, p_debug); } else if (file.begins_with("libgodot.ios")) { diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index 3409e43ba5..8efbd2e3c5 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -4991,7 +4991,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode #ifdef DBUS_ENABLED screensaver = memnew(FreeDesktopScreenSaver); - screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); #endif r_error = OK; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index e228007246..a49485154b 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -3286,7 +3286,7 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM } #endif - screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); } DisplayServerMacOS::~DisplayServerMacOS() { diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 7bc4c6047c..40f93bb18b 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -275,7 +275,7 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a display_request->RequestActive(); } - set_keep_screen_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + set_keep_screen_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); return OK; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index a975d09a9d..8c8dbef8a4 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -3653,7 +3653,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win tts = memnew(TTS_Windows); // Enforce default keep screen on value. - screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true)); + screen_set_keep_on(GLOBAL_GET("display/window/energy_saving/keep_screen_on")); // Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 6f189a57e8..cdf7c5afa9 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -422,7 +422,7 @@ real_t NavigationRegion2D::get_enter_cost() const { void NavigationRegion2D::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); travel_cost = MAX(p_travel_cost, 0.0); - NavigationServer2D::get_singleton()->region_set_enter_cost(region, travel_cost); + NavigationServer2D::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationRegion2D::get_travel_cost() const { diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 1fcd5160f6..1cfd889272 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -127,6 +127,10 @@ GPUParticlesCollisionBox3D::~GPUParticlesCollisionBox3D() { void GPUParticlesCollisionSDF3D::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->is_visible_in_tree()) { + if ((mi->get_layer_mask() & bake_mask) == 0) { + return; + } + Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); @@ -445,7 +449,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { //compute bvh - ERR_FAIL_COND_V(faces.size() <= 1, Ref<Image>()); + ERR_FAIL_COND_V_MSG(faces.size() <= 1, Ref<Image>(), "No faces detected during GPUParticlesCollisionSDF3D bake. Check whether there are visible meshes matching the bake mask within its extents."); LocalVector<FacePos> face_pos; @@ -499,6 +503,16 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { return ret; } +TypedArray<String> GPUParticlesCollisionSDF3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (bake_mask == 0) { + warnings.push_back(RTR("The Bake Mask has no bits enabled, which means baking will not produce any collision for this GPUParticlesCollisionSDF3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); + } + + return warnings; +} + void GPUParticlesCollisionSDF3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF3D::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF3D::get_extents); @@ -512,9 +526,15 @@ void GPUParticlesCollisionSDF3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF3D::set_thickness); ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF3D::get_thickness); + ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &GPUParticlesCollisionSDF3D::set_bake_mask); + ClassDB::bind_method(D_METHOD("get_bake_mask"), &GPUParticlesCollisionSDF3D::get_bake_mask); + ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &GPUParticlesCollisionSDF3D::set_bake_mask_value); + ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &GPUParticlesCollisionSDF3D::get_bake_mask_value); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater,suffix:m"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512,suffix:px"), "set_resolution", "get_resolution"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,suffix:m"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); BIND_ENUM_CONSTANT(RESOLUTION_16); @@ -553,6 +573,31 @@ GPUParticlesCollisionSDF3D::Resolution GPUParticlesCollisionSDF3D::get_resolutio return resolution; } +void GPUParticlesCollisionSDF3D::set_bake_mask(uint32_t p_mask) { + bake_mask = p_mask; + update_configuration_warnings(); +} + +uint32_t GPUParticlesCollisionSDF3D::get_bake_mask() const { + return bake_mask; +} + +void GPUParticlesCollisionSDF3D::set_bake_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1 || p_layer_number > 20, vformat("The render layer number (%d) must be between 1 and 20 (inclusive).", p_layer_number)); + uint32_t mask = get_bake_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_bake_mask(mask); +} + +bool GPUParticlesCollisionSDF3D::get_bake_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1 || p_layer_number > 20, false, vformat("The render layer number (%d) must be between 1 and 20 (inclusive).", p_layer_number)); + return bake_mask & (1 << (p_layer_number - 1)); +} + void GPUParticlesCollisionSDF3D::set_texture(const Ref<Texture3D> &p_texture) { texture = p_texture; RID tex = texture.is_valid() ? texture->get_rid() : RID(); diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h index 4b2cb930fa..712bd015ff 100644 --- a/scene/3d/gpu_particles_collision_3d.h +++ b/scene/3d/gpu_particles_collision_3d.h @@ -110,6 +110,7 @@ public: private: Vector3 extents = Vector3(1, 1, 1); Resolution resolution = RESOLUTION_64; + uint32_t bake_mask = 0xFFFFFFFF; Ref<Texture3D> texture; float thickness = 1.0; @@ -161,6 +162,8 @@ protected: static void _bind_methods(); public: + virtual TypedArray<String> get_configuration_warnings() const override; + void set_thickness(float p_thickness); float get_thickness() const; @@ -170,6 +173,12 @@ public: void set_resolution(Resolution p_resolution); Resolution get_resolution() const; + void set_bake_mask(uint32_t p_mask); + uint32_t get_bake_mask() const; + + void set_bake_mask_value(int p_layer_number, bool p_enable); + bool get_bake_mask_value(int p_layer_number) const; + void set_texture(const Ref<Texture3D> &p_texture); Ref<Texture3D> get_texture() const; diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 0abb0e8dea..4150b01651 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -119,7 +119,7 @@ real_t NavigationRegion3D::get_enter_cost() const { void NavigationRegion3D::set_travel_cost(real_t p_travel_cost) { ERR_FAIL_COND_MSG(p_travel_cost < 0.0, "The travel_cost must be positive."); travel_cost = MAX(p_travel_cost, 0.0); - NavigationServer3D::get_singleton()->region_set_enter_cost(region, travel_cost); + NavigationServer3D::get_singleton()->region_set_travel_cost(region, travel_cost); } real_t NavigationRegion3D::get_travel_cost() const { diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 636c9e26a5..09ec086564 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -2124,7 +2124,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode); ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode); - ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled); + ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled", "enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled); ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled); ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 942b7d0bf9..f075510aa4 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -387,7 +387,7 @@ String ConfirmationDialog::get_cancel_button_text() const { void ConfirmationDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_cancel_button"), &ConfirmationDialog::get_cancel_button); - ClassDB::bind_method(D_METHOD("set_cancel_button_text"), &ConfirmationDialog::set_cancel_button_text); + ClassDB::bind_method(D_METHOD("set_cancel_button_text", "text"), &ConfirmationDialog::set_cancel_button_text); ClassDB::bind_method(D_METHOD("get_cancel_button_text"), &ConfirmationDialog::get_cancel_button_text); ADD_PROPERTY(PropertyInfo(Variant::STRING, "cancel_button_text"), "set_cancel_button_text", "get_cancel_button_text"); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index c58513df17..c6d011d4ef 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -498,7 +498,7 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selected_id"), &OptionButton::get_selected_id); ClassDB::bind_method(D_METHOD("get_selected_metadata"), &OptionButton::get_selected_metadata); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &OptionButton::remove_item); - ClassDB::bind_method(D_METHOD("_select_int"), &OptionButton::_select_int); + ClassDB::bind_method(D_METHOD("_select_int", "idx"), &OptionButton::_select_int); ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index c023b06895..3755a8fa34 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5294,7 +5294,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); - ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled"), &TextEdit::set_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled", "enabled"), &TextEdit::set_fit_content_height_enabled); ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &TextEdit::is_fit_content_height_enabled); ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0)); diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp index 5512d0a84e..a9b0285723 100644 --- a/scene/main/resource_preloader.cpp +++ b/scene/main/resource_preloader.cpp @@ -138,7 +138,7 @@ void ResourcePreloader::get_resource_list(List<StringName> *p_list) { } void ResourcePreloader::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_resources"), &ResourcePreloader::_set_resources); + ClassDB::bind_method(D_METHOD("_set_resources", "resources"), &ResourcePreloader::_set_resources); ClassDB::bind_method(D_METHOD("_get_resources"), &ResourcePreloader::_get_resources); ClassDB::bind_method(D_METHOD("add_resource", "name", "resource"), &ResourcePreloader::add_resource); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 4dd4c8419c..584fad9648 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2498,17 +2498,20 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) { Vector2i diff = mm->get_position() - gui.subwindow_drag_from; Size2i min_size = gui.subwindow_focused->get_min_size(); + + Size2i min_size_adjusted = min_size; if (gui.subwindow_focused->is_wrapping_controls()) { Size2i cms = gui.subwindow_focused->get_contents_minimum_size(); - min_size.x = MAX(cms.x, min_size.x); - min_size.y = MAX(cms.y, min_size.y); + min_size_adjusted.x = MAX(cms.x, min_size.x); + min_size_adjusted.y = MAX(cms.y, min_size.y); } - min_size.x = MAX(min_size.x, 1); - min_size.y = MAX(min_size.y, 1); + + min_size_adjusted.x = MAX(min_size_adjusted.x, 1); + min_size_adjusted.y = MAX(min_size_adjusted.y, 1); Rect2i r = gui.subwindow_resize_from_rect; - Size2i limit = r.size - min_size; + Size2i limit = r.size - min_size_adjusted; switch (gui.subwindow_resize_mode) { case SUB_WINDOW_RESIZE_TOP_LEFT: { @@ -2563,6 +2566,19 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { } } + Size2i max_size = gui.subwindow_focused->get_max_size(); + if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) { + max_size.x = MAX(max_size.x, 1); + max_size.y = MAX(max_size.y, 1); + + if (r.size.x > max_size.x) { + r.size.x = max_size.x; + } + if (r.size.y > max_size.y) { + r.size.y = max_size.y; + } + } + gui.subwindow_focused->_rect_changed_callback(r); } @@ -2600,7 +2616,7 @@ bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> close_icon = sw.window->get_theme_icon(SNAME("close")); Rect2 close_rect; - close_rect.position = Vector2(r.position.x + r.size.x - close_v_ofs, r.position.y - close_h_ofs); + close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs); close_rect.size = close_icon->get_size(); if (gui.subwindow_focused != sw.window) { diff --git a/scene/main/window.cpp b/scene/main/window.cpp index bd72858ae6..d40b82f5eb 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -242,8 +242,8 @@ void Window::_make_window() { window_id = DisplayServer::get_singleton()->create_sub_window(DisplayServer::WindowMode(mode), vsync_mode, f, Rect2i(position, size)); ERR_FAIL_COND(window_id == DisplayServer::INVALID_WINDOW_ID); DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); - DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); - DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); + DisplayServer::get_singleton()->window_set_max_size(Size2i(), window_id); + DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); String tr_title = atr(title); #ifdef DEBUG_ENABLED if (window_id == DisplayServer::MAIN_WINDOW_ID) { @@ -596,20 +596,43 @@ void Window::_update_window_size() { size.x = MAX(size_limit.x, size.x); size.y = MAX(size_limit.y, size.y); - if (max_size.x > 0 && max_size.x > min_size.x && size.x > max_size.x) { - size.x = max_size.x; - } + bool reset_min_first = false; + + bool max_size_valid = false; + if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) { + max_size_valid = true; + + if (size.x > max_size.x) { + size.x = max_size.x; + } + if (size_limit.x > max_size.x) { + size_limit.x = max_size.x; + reset_min_first = true; + } - if (max_size.y > 0 && max_size.y > min_size.y && size.y > max_size.y) { - size.y = max_size.y; + if (size.y > max_size.y) { + size.y = max_size.y; + } + if (size_limit.y > max_size.y) { + size_limit.y = max_size.y; + reset_min_first = true; + } } if (embedder) { + size.x = MAX(size.x, 1); + size.y = MAX(size.y, 1); + embedder->_sub_window_update(this); } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + if (reset_min_first && wrap_controls) { + // Avoid an error if setting max_size to a value between min_size and the previous size_limit. + DisplayServer::get_singleton()->window_set_min_size(Size2i(), window_id); + } + DisplayServer::get_singleton()->window_set_size(size, window_id); + DisplayServer::get_singleton()->window_set_max_size(max_size_valid ? max_size : Size2i(), window_id); DisplayServer::get_singleton()->window_set_min_size(size_limit, window_id); - DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); } //update the viewport @@ -953,6 +976,8 @@ void Window::set_wrap_controls(bool p_enable) { wrap_controls = p_enable; if (wrap_controls) { child_controls_changed(); + } else { + _update_window_size(); } } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 1ff72825ac..bef431e980 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -669,7 +669,7 @@ void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("get_size"), &BitMap::get_size); ClassDB::bind_method(D_METHOD("resize", "new_size"), &BitMap::resize); - ClassDB::bind_method(D_METHOD("_set_data"), &BitMap::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &BitMap::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &BitMap::_get_data); ClassDB::bind_method(D_METHOD("grow_mask", "pixels", "rect"), &BitMap::grow_mask); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index da26a0261f..5de92acb75 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -1190,7 +1190,7 @@ void Curve2D::_bind_methods() { ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data); - ClassDB::bind_method(D_METHOD("_set_data"), &Curve2D::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve2D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); @@ -2002,7 +2002,7 @@ void Curve3D::_bind_methods() { ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4)); ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data); - ClassDB::bind_method(D_METHOD("_set_data"), &Curve3D::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval"); ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 55356c2058..88bc01fb25 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -233,7 +233,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { if (!groups.has(last_group)) { PropertyInfo info; info.usage = PROPERTY_USAGE_GROUP; - info.name = last_group; + info.name = last_group.capitalize(); List<PropertyInfo> none_subgroup; none_subgroup.push_back(info); @@ -247,7 +247,7 @@ void ShaderMaterial::_get_property_list(List<PropertyInfo> *p_list) const { if (!groups[last_group].has(last_subgroup)) { PropertyInfo info; info.usage = PROPERTY_USAGE_SUBGROUP; - info.name = last_subgroup; + info.name = last_subgroup.capitalize(); List<PropertyInfo> subgroup; subgroup.push_back(info); diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index e5fc61ade5..ff4a7a4560 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -340,13 +340,13 @@ void MultiMesh::_bind_methods() { #ifndef DISABLE_DEPRECATED // Kept for compatibility from 3.x to 4.0. - ClassDB::bind_method(D_METHOD("_set_transform_array"), &MultiMesh::_set_transform_array); + ClassDB::bind_method(D_METHOD("_set_transform_array", "array"), &MultiMesh::_set_transform_array); ClassDB::bind_method(D_METHOD("_get_transform_array"), &MultiMesh::_get_transform_array); - ClassDB::bind_method(D_METHOD("_set_transform_2d_array"), &MultiMesh::_set_transform_2d_array); + ClassDB::bind_method(D_METHOD("_set_transform_2d_array", "array"), &MultiMesh::_set_transform_2d_array); ClassDB::bind_method(D_METHOD("_get_transform_2d_array"), &MultiMesh::_get_transform_2d_array); - ClassDB::bind_method(D_METHOD("_set_color_array"), &MultiMesh::_set_color_array); + ClassDB::bind_method(D_METHOD("_set_color_array", "array"), &MultiMesh::_set_color_array); ClassDB::bind_method(D_METHOD("_get_color_array"), &MultiMesh::_get_color_array); - ClassDB::bind_method(D_METHOD("_set_custom_data_array"), &MultiMesh::_set_custom_data_array); + ClassDB::bind_method(D_METHOD("_set_custom_data_array", "array"), &MultiMesh::_set_custom_data_array); ClassDB::bind_method(D_METHOD("_get_custom_data_array"), &MultiMesh::_get_custom_data_array); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_transform_array", "_get_transform_array"); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index ac67e6e5e9..0e1b18d584 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -1779,7 +1779,7 @@ void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); - ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); + ClassDB::bind_method(D_METHOD("_set_bundled_scene", "scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp index 29135e30c9..5e18671c11 100644 --- a/scene/resources/polygon_path_finder.cpp +++ b/scene/resources/polygon_path_finder.cpp @@ -553,7 +553,7 @@ void PolygonPathFinder::_bind_methods() { ClassDB::bind_method(D_METHOD("get_point_penalty", "idx"), &PolygonPathFinder::get_point_penalty); ClassDB::bind_method(D_METHOD("get_bounds"), &PolygonPathFinder::get_bounds); - ClassDB::bind_method(D_METHOD("_set_data"), &PolygonPathFinder::_set_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &PolygonPathFinder::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &PolygonPathFinder::_get_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 55c9d7397d..3533e86c3a 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -207,7 +207,7 @@ void SpriteFrames::_bind_methods() { // `animations` property is for serialization. - ClassDB::bind_method(D_METHOD("_set_animations"), &SpriteFrames::_set_animations); + ClassDB::bind_method(D_METHOD("_set_animations", "animations"), &SpriteFrames::_set_animations); ClassDB::bind_method(D_METHOD("_get_animations"), &SpriteFrames::_get_animations); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_animations", "_get_animations"); diff --git a/servers/physics_2d/godot_area_2d.cpp b/servers/physics_2d/godot_area_2d.cpp index 96c8dfc69e..af90f96438 100644 --- a/servers/physics_2d/godot_area_2d.cpp +++ b/servers/physics_2d/godot_area_2d.cpp @@ -248,6 +248,10 @@ void GodotArea2D::call_queries() { Callable::CallError ce; Variant ret; monitor_callback.callp((const Variant **)resptr, 5, ret, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce)); + } } } else { monitored_bodies.clear(); @@ -286,6 +290,10 @@ void GodotArea2D::call_queries() { Callable::CallError ce; Variant ret; area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce)); + } } } else { monitored_areas.clear(); diff --git a/servers/physics_3d/godot_area_3d.cpp b/servers/physics_3d/godot_area_3d.cpp index fdb9f42b40..9765d0bf58 100644 --- a/servers/physics_3d/godot_area_3d.cpp +++ b/servers/physics_3d/godot_area_3d.cpp @@ -277,6 +277,10 @@ void GodotArea3D::call_queries() { Callable::CallError ce; Variant ret; monitor_callback.callp((const Variant **)resptr, 5, ret, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT_ONCE("Error calling monitor callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce)); + } } } else { monitored_bodies.clear(); @@ -315,6 +319,10 @@ void GodotArea3D::call_queries() { Callable::CallError ce; Variant ret; area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce); + + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT_ONCE("Error calling area monitor callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce)); + } } } else { monitored_areas.clear(); diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl index dff24b7144..e9515c7670 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl @@ -988,8 +988,10 @@ void fragment_shader(in SceneData scene_data) { vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); vec3 ref_vec = reflect(-view, bent_normal); + ref_vec = mix(ref_vec, bent_normal, roughness * roughness); #else vec3 ref_vec = reflect(-view, normal); + ref_vec = mix(ref_vec, normal, roughness * roughness); #endif float horizon = min(1.0 + dot(ref_vec, normal), 1.0); @@ -1046,6 +1048,7 @@ void fragment_shader(in SceneData scene_data) { ambient_light *= attenuation; specular_light *= attenuation; + ref_vec = mix(ref_vec, n, clearcoat_roughness * clearcoat_roughness); float horizon = min(1.0 + dot(ref_vec, normal), 1.0); ref_vec = scene_data.radiance_inverse_xform * ref_vec; float roughness_lod = mix(0.001, 0.1, clearcoat_roughness) * MAX_ROUGHNESS_LOD; @@ -1203,6 +1206,7 @@ void fragment_shader(in SceneData scene_data) { uint index1 = instances.data[instance_index].gi_offset & 0xFFFF; vec3 ref_vec = normalize(reflect(-view, normal)); + ref_vec = mix(ref_vec, normal, roughness * roughness); //find arbitrary tangent and bitangent, then build a matrix vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0); vec3 tangent = normalize(cross(v0, normal)); @@ -1302,6 +1306,18 @@ void fragment_shader(in SceneData scene_data) { item_to = subgroupBroadcastFirst(subgroupMax(item_to)); #endif +#ifdef LIGHT_ANISOTROPY_USED + // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy + vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; + vec3 anisotropic_tangent = cross(anisotropic_direction, view); + vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); + vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); +#else + vec3 bent_normal = normal; +#endif + vec3 ref_vec = normalize(reflect(-view, bent_normal)); + ref_vec = mix(ref_vec, bent_normal, roughness * roughness); + for (uint i = item_from; i < item_to; i++) { uint mask = cluster_buffer.data[cluster_reflection_offset + i]; mask &= cluster_get_range_clip_mask(i, item_min, item_max); @@ -1324,16 +1340,8 @@ void fragment_shader(in SceneData scene_data) { if (!bool(reflections.data[reflection_index].mask & instances.data[instance_index].layer_mask)) { continue; //not masked } -#ifdef LIGHT_ANISOTROPY_USED - // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy - vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; - vec3 anisotropic_tangent = cross(anisotropic_direction, view); - vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); - vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); -#else - vec3 bent_normal = normal; -#endif - reflection_process(reflection_index, view, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum); + + reflection_process(reflection_index, vertex, ref_vec, normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum); } } diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl index 50a13de11a..7299bb0576 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl @@ -874,7 +874,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v diffuse_light, specular_light); } -void reflection_process(uint ref_index, vec3 view, vec3 vertex, vec3 normal, float roughness, vec3 ambient_light, vec3 specular_light, inout vec4 ambient_accum, inout vec4 reflection_accum) { +void reflection_process(uint ref_index, vec3 vertex, vec3 ref_vec, vec3 normal, float roughness, vec3 ambient_light, vec3 specular_light, inout vec4 ambient_accum, inout vec4 reflection_accum) { vec3 box_extents = reflections.data[ref_index].box_extents; vec3 local_pos = (reflections.data[ref_index].local_matrix * vec4(vertex, 1.0)).xyz; @@ -882,8 +882,6 @@ void reflection_process(uint ref_index, vec3 view, vec3 vertex, vec3 normal, flo return; } - vec3 ref_vec = normalize(reflect(-view, normal)); - vec3 inner_pos = abs(local_pos / box_extents); float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z)); //make blend more rounded diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl index eb73a33558..6548793bee 100644 --- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl @@ -889,8 +889,10 @@ void main() { vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); vec3 ref_vec = reflect(-view, bent_normal); + ref_vec = mix(ref_vec, bent_normal, roughness * roughness); #else vec3 ref_vec = reflect(-view, normal); + ref_vec = mix(ref_vec, normal, roughness * roughness); #endif float horizon = min(1.0 + dot(ref_vec, normal), 1.0); ref_vec = scene_data.radiance_inverse_xform * ref_vec; @@ -940,6 +942,7 @@ void main() { vec3 n = normalize(normal_interp); // We want to use geometric normal, not normal_map float NoV = max(dot(n, view), 0.0001); vec3 ref_vec = reflect(-view, n); + ref_vec = mix(ref_vec, n, clearcoat_roughness * clearcoat_roughness); // The clear coat layer assumes an IOR of 1.5 (4% reflectance) float Fc = clearcoat * (0.04 + 0.96 * SchlickFresnel(NoV)); float attenuation = 1.0 - Fc; @@ -1036,6 +1039,19 @@ void main() { vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0); uint reflection_indices = draw_call.reflection_probes.x; + +#ifdef LIGHT_ANISOTROPY_USED + // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy + vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; + vec3 anisotropic_tangent = cross(anisotropic_direction, view); + vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); + vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); +#else + vec3 bent_normal = normal; +#endif + vec3 ref_vec = normalize(reflect(-view, bent_normal)); + ref_vec = mix(ref_vec, bent_normal, roughness * roughness); + for (uint i = 0; i < 8; i++) { uint reflection_index = reflection_indices & 0xFF; if (i == 4) { @@ -1047,16 +1063,8 @@ void main() { if (reflection_index == 0xFF) { break; } -#ifdef LIGHT_ANISOTROPY_USED - // https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy - vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent; - vec3 anisotropic_tangent = cross(anisotropic_direction, view); - vec3 anisotropic_normal = cross(anisotropic_tangent, anisotropic_direction); - vec3 bent_normal = normalize(mix(normal, anisotropic_normal, abs(anisotropy) * clamp(5.0 * roughness, 0.0, 1.0))); -#else - vec3 bent_normal = normal; -#endif - reflection_process(reflection_index, view, vertex, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum); + + reflection_process(reflection_index, vertex, ref_vec, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum); } if (reflection_accum.a > 0.0) { diff --git a/tests/core/object/test_class_db.h b/tests/core/object/test_class_db.h index fc329ba0eb..208923edb9 100644 --- a/tests/core/object/test_class_db.h +++ b/tests/core/object/test_class_db.h @@ -71,6 +71,7 @@ struct ArgumentData { String name; bool has_defval = false; Variant defval; + int position; }; struct MethodData { @@ -371,6 +372,39 @@ void validate_property(const Context &p_context, const ExposedClass &p_class, co } } +void validate_argument(const Context &p_context, const ExposedClass &p_class, const String &p_owner_name, const String &p_owner_type, const ArgumentData &p_arg) { + TEST_COND((p_arg.name.is_empty() || p_arg.name.begins_with("_unnamed_arg")), + vformat("Unnamed argument in position %d of %s '%s.%s'.", p_arg.position, p_owner_type, p_class.name, p_owner_name)); + + const ExposedClass *arg_class = p_context.find_exposed_class(p_arg.type); + if (arg_class) { + TEST_COND(arg_class->is_singleton, + vformat("Argument type is a singleton: '%s' of %s '%s.%s'.", p_arg.name, p_owner_type, p_class.name, p_owner_name)); + + if (p_class.api_type == ClassDB::API_CORE) { + TEST_COND(arg_class->api_type == ClassDB::API_EDITOR, + vformat("Argument '%s' of %s '%s.%s' has type '%s' from the editor API. Core API cannot have dependencies on the editor API.", + p_arg.name, p_owner_type, p_class.name, p_owner_name, arg_class->name)); + } + } else { + // Look for types that don't inherit Object. + TEST_FAIL_COND(!p_context.has_type(p_arg.type), + vformat("Argument type '%s' not found: '%s' of %s '%s.%s'.", p_arg.type.name, p_arg.name, p_owner_type, p_class.name, p_owner_name)); + } + + if (p_arg.has_defval) { + String type_error_msg; + bool arg_defval_assignable_to_type = arg_default_value_is_assignable_to_type(p_context, p_arg.defval, p_arg.type, &type_error_msg); + + String err_msg = vformat("Invalid default value for parameter '%s' of %s '%s.%s'.", p_arg.name, p_owner_type, p_class.name, p_owner_name); + if (!type_error_msg.is_empty()) { + err_msg += " " + type_error_msg; + } + + TEST_COND(!arg_defval_assignable_to_type, err_msg.utf8().get_data()); + } +} + void validate_method(const Context &p_context, const ExposedClass &p_class, const MethodData &p_method) { if (p_method.return_type.name != StringName()) { const ExposedClass *return_class = p_context.find_exposed_class(p_method.return_type); @@ -392,54 +426,14 @@ void validate_method(const Context &p_context, const ExposedClass &p_class, cons for (const ArgumentData &F : p_method.arguments) { const ArgumentData &arg = F; - - const ExposedClass *arg_class = p_context.find_exposed_class(arg.type); - if (arg_class) { - TEST_COND(arg_class->is_singleton, - "Argument type is a singleton: '", arg.name, "' of method '", p_class.name, ".", p_method.name, "'."); - - if (p_class.api_type == ClassDB::API_CORE) { - TEST_COND(arg_class->api_type == ClassDB::API_EDITOR, - "Argument '", arg.name, "' of method '", p_class.name, ".", p_method.name, "' has type '", - arg_class->name, "' from the editor API. Core API cannot have dependencies on the editor API."); - } - } else { - // Look for types that don't inherit Object - TEST_FAIL_COND(!p_context.has_type(arg.type), - "Argument type '", arg.type.name, "' not found: '", arg.name, "' of method", p_class.name, ".", p_method.name, "'."); - } - - if (arg.has_defval) { - String type_error_msg; - bool arg_defval_assignable_to_type = arg_default_value_is_assignable_to_type(p_context, arg.defval, arg.type, &type_error_msg); - String err_msg = vformat("Invalid default value for parameter '%s' of method '%s.%s'.", arg.name, p_class.name, p_method.name); - if (!type_error_msg.is_empty()) { - err_msg += " " + type_error_msg; - } - TEST_COND(!arg_defval_assignable_to_type, err_msg.utf8().get_data()); - } + validate_argument(p_context, p_class, p_method.name, "method", arg); } } void validate_signal(const Context &p_context, const ExposedClass &p_class, const SignalData &p_signal) { for (const ArgumentData &F : p_signal.arguments) { const ArgumentData &arg = F; - - const ExposedClass *arg_class = p_context.find_exposed_class(arg.type); - if (arg_class) { - TEST_COND(arg_class->is_singleton, - "Argument class is a singleton: '", arg.name, "' of signal '", p_class.name, ".", p_signal.name, "'."); - - if (p_class.api_type == ClassDB::API_CORE) { - TEST_COND(arg_class->api_type == ClassDB::API_EDITOR, - "Argument '", arg.name, "' of signal '", p_class.name, ".", p_signal.name, "' has type '", - arg_class->name, "' from the editor API. Core API cannot have dependencies on the editor API."); - } - } else { - // Look for types that don't inherit Object - TEST_FAIL_COND(!p_context.has_type(arg.type), - "Argument type '", arg.type.name, "' not found: '", arg.name, "' of signal", p_class.name, ".", p_signal.name, "'."); - } + validate_argument(p_context, p_class, p_signal.name, "signal", arg); } } @@ -625,6 +619,7 @@ void add_exposed_classes(Context &r_context) { ArgumentData arg; arg.name = orig_arg_name; + arg.position = i; if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { arg.type.name = arg_info.class_name; @@ -693,6 +688,7 @@ void add_exposed_classes(Context &r_context) { ArgumentData arg; arg.name = orig_arg_name; + arg.position = i; if (arg_info.type == Variant::INT && arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { arg.type.name = arg_info.class_name; @@ -841,7 +837,7 @@ TEST_SUITE("[ClassDB]") { add_builtin_types(context); add_global_enums(context); - SUBCASE("[ClassDB] Find exposed class") { + SUBCASE("[ClassDB] Validate exposed classes") { const ExposedClass *object_class = context.find_exposed_class(context.names_cache.object_class); TEST_FAIL_COND(!object_class, "Object class not found."); TEST_FAIL_COND(object_class->base != StringName(), diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 916686d7c1..d6799928b4 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -719,6 +719,7 @@ TEST_CASE("[Variant] Assignment To Color from Bool,Int,Float,String,Vec2,Vec2i,V vec3i_v = col_v; CHECK(vec3i_v.get_type() == Variant::COLOR); } + TEST_CASE("[Variant] Writer and parser array") { Array a = build_array(1, String("hello"), build_array(Variant())); String a_str; @@ -911,6 +912,67 @@ TEST_CASE("[Variant] Nested dictionary comparison") { CHECK_FALSE(v_d1 == v_d_other_val); } +struct ArgumentData { + Variant::Type type; + String name; + bool has_defval = false; + Variant defval; + int position; +}; + +struct MethodData { + StringName name; + Variant::Type return_type; + List<ArgumentData> arguments; + bool is_virtual = false; + bool is_vararg = false; +}; + +TEST_CASE("[Variant] Utility functions") { + List<MethodData> functions; + + List<StringName> function_names; + Variant::get_utility_function_list(&function_names); + function_names.sort_custom<StringName::AlphCompare>(); + + for (const StringName &E : function_names) { + MethodData md; + md.name = E; + + // Utility function's return type. + if (Variant::has_utility_function_return_value(E)) { + md.return_type = Variant::get_utility_function_return_type(E); + } + + // Utility function's arguments. + if (Variant::is_utility_function_vararg(E)) { + md.is_vararg = true; + } else { + for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) { + ArgumentData arg; + arg.type = Variant::get_utility_function_argument_type(E, i); + arg.name = Variant::get_utility_function_argument_name(E, i); + arg.position = i; + + md.arguments.push_back(arg); + } + } + + functions.push_back(md); + } + + SUBCASE("[Variant] Validate utility functions") { + for (const MethodData &E : functions) { + for (const ArgumentData &F : E.arguments) { + const ArgumentData &arg = F; + + TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")), + vformat("Unnamed argument in position %d of function '%s'.", arg.position, E.name)); + } + } + } +} + } // namespace TestVariant #endif // TEST_VARIANT_H |