diff options
365 files changed, 4619 insertions, 1963 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 001a351e0b..beef773699 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -153,8 +153,23 @@ const PackedStringArray ProjectSettings::_trim_to_supported_features(const Packe #endif // TOOLS_ENABLED String ProjectSettings::localize_path(const String &p_path) const { - if (resource_path.is_empty() || p_path.begins_with("res://") || p_path.begins_with("user://") || - (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { + if (resource_path.is_empty() || (p_path.is_absolute_path() && !p_path.begins_with(resource_path))) { + return p_path.simplify_path(); + } + + // Check if we have a special path (like res://) or a protocol identifier. + int p = p_path.find("://"); + bool found = false; + if (p > 0) { + found = true; + for (int i = 0; i < p; i++) { + if (!is_ascii_alphanumeric_char(p_path[i])) { + found = false; + break; + } + } + } + if (found) { return p_path.simplify_path(); } diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index 4513c66ab5..1ce50bc186 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -234,6 +234,8 @@ typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtension typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); typedef struct { + GDNativeBool is_virtual; + GDNativeBool is_abstract; GDNativeExtensionClassSet set_func; GDNativeExtensionClassGet get_func; GDNativeExtensionClassGetPropertyList get_property_list_func; diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 6418da2235..cc019584a5 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -152,6 +152,8 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr extension->native_extension.parent_class_name = parent_class_name; extension->native_extension.class_name = class_name; extension->native_extension.editor_class = self->level_initialized == INITIALIZATION_LEVEL_EDITOR; + extension->native_extension.is_virtual = p_extension_funcs->is_virtual; + extension->native_extension.is_abstract = p_extension_funcs->is_abstract; extension->native_extension.set = p_extension_funcs->set_func; extension->native_extension.get = p_extension_funcs->get_func; extension->native_extension.get_property_list = p_extension_funcs->get_property_list_func; diff --git a/core/input/input.cpp b/core/input/input.cpp index 889c2a92fa..1321e40795 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -519,6 +519,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em touch_event.instantiate(); touch_event->set_pressed(mb->is_pressed()); touch_event->set_position(mb->get_position()); + touch_event->set_double_tap(mb->is_double_click()); event_dispatch_function(touch_event); } } @@ -580,6 +581,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em button_event->set_global_position(st->get_position()); button_event->set_pressed(st->is_pressed()); button_event->set_button_index(MouseButton::LEFT); + button_event->set_double_click(st->is_double_tap()); if (st->is_pressed()) { button_event->set_button_mask(MouseButton(mouse_button_mask | MouseButton::MASK_LEFT)); } else { diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 38037eaf72..9dc071de9c 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -1162,6 +1162,13 @@ bool InputEventScreenTouch::is_pressed() const { return pressed; } +void InputEventScreenTouch::set_double_tap(bool p_double_tap) { + double_tap = p_double_tap; +} +bool InputEventScreenTouch::is_double_tap() const { + return double_tap; +} + Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { Ref<InputEventScreenTouch> st; st.instantiate(); @@ -1170,6 +1177,7 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co st->set_index(index); st->set_position(p_xform.xform(pos + p_local_ofs)); st->set_pressed(pressed); + st->set_double_tap(double_tap); return st; } @@ -1182,7 +1190,8 @@ String InputEventScreenTouch::as_text() const { String InputEventScreenTouch::to_string() { String p = pressed ? "true" : "false"; - return vformat("InputEventScreenTouch: index=%d, pressed=%s, position=(%s)", index, p, String(get_position())); + String double_tap_string = double_tap ? "true" : "false"; + return vformat("InputEventScreenTouch: index=%d, pressed=%s, position=(%s), double_tap=%s", index, p, String(get_position()), double_tap_string); } void InputEventScreenTouch::_bind_methods() { @@ -1195,9 +1204,13 @@ void InputEventScreenTouch::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &InputEventScreenTouch::set_pressed); //ClassDB::bind_method(D_METHOD("is_pressed"),&InputEventScreenTouch::is_pressed); + ClassDB::bind_method(D_METHOD("set_double_tap", "double_tap"), &InputEventScreenTouch::set_double_tap); + ClassDB::bind_method(D_METHOD("is_double_tap"), &InputEventScreenTouch::is_double_tap); + ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "double_tap"), "set_double_tap", "is_double_tap"); } /////////////////////////////////// diff --git a/core/input/input_event.h b/core/input/input_event.h index bc3ec3e7ac..adbcb7cf68 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -353,6 +353,7 @@ class InputEventScreenTouch : public InputEventFromWindow { int index = 0; Vector2 pos; bool pressed = false; + bool double_tap = false; protected: static void _bind_methods(); @@ -367,6 +368,9 @@ public: void set_pressed(bool p_pressed); virtual bool is_pressed() const override; + void set_double_tap(bool p_double_tap); + bool is_double_tap() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; virtual String as_text() const override; virtual String to_string() override; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index ce76d11b6e..0c765fe23c 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -337,6 +337,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_text_scroll_down.macos", TTRC("Scroll Down") }, { "ui_text_select_all", TTRC("Select All") }, { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") }, + { "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") }, { "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") }, { "ui_text_submit", TTRC("Text Submitted") }, { "ui_graph_duplicate", TTRC("Duplicate Nodes") }, @@ -641,10 +642,14 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_select_all", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); + inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT)); default_builtin_cache.insert("ui_text_select_word_under_caret", inputs); inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); + default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs); + + inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::INSERT)); default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs); diff --git a/core/io/image.cpp b/core/io/image.cpp index 4be624a5a8..16dd66fc98 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -982,7 +982,7 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { } void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first."); + ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use set_data() first."); ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; @@ -1017,7 +1017,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; if (interpolate_mipmaps) { - dst2.create(p_width, p_height, false, format); + dst2.initialize_data(p_width, p_height, false, format); } bool had_mipmaps = mipmaps; @@ -2016,9 +2016,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con uint8_t* wr = imgdata.ptrw(); memcpy(wr.ptr(), ptr, size); wr = uint8_t*(); - Ref<Image> im; - im.instantiate(); - im->create(w, h, false, format, imgdata); + Ref<Image> im = Image::create_from_data(w, h, false, format, imgdata); im->save_png("res://mipmap_" + itos(i) + ".png"); } #endif @@ -2051,7 +2049,25 @@ Vector<uint8_t> Image::get_data() const { return data; } -void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { +Ref<Image> Image::create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { + Ref<Image> image; + image.instantiate(); + image->initialize_data(p_width, p_height, p_use_mipmaps, p_format); + return image; +} + +Ref<Image> Image::create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { + Ref<Image> image; + image.instantiate(); + image->initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data); + return image; +} + +void Image::set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { + initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data); +} + +void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, @@ -2077,7 +2093,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma format = p_format; } -void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { +void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels."); ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, @@ -2115,7 +2131,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma mipmaps = p_use_mipmaps; } -void Image::create(const char **p_xpm) { +void Image::initialize_data(const char **p_xpm) { int size_width = 0; int size_height = 0; int pixelchars = 0; @@ -2230,7 +2246,7 @@ void Image::create(const char **p_xpm) { } if (line == colormap_size) { status = READING_PIXELS; - create(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); + initialize_data(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8); data_write = data.ptrw(); pixel_size = has_alpha ? 4 : 3; } @@ -2559,7 +2575,7 @@ Image::Image(const char **p_xpm) { mipmaps = false; format = FORMAT_L8; - create(p_xpm); + initialize_data(p_xpm); } Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { @@ -2568,7 +2584,7 @@ Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { mipmaps = p_use_mipmaps; format = FORMAT_L8; - create(p_width, p_height, p_use_mipmaps, p_format); + initialize_data(p_width, p_height, p_use_mipmaps, p_format); } Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { @@ -2577,7 +2593,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const V mipmaps = p_mipmaps; format = FORMAT_L8; - create(p_width, p_height, p_mipmaps, p_format, p_data); + initialize_data(p_width, p_height, p_mipmaps, p_format, p_data); } Rect2i Image::get_used_rect() const { @@ -2846,6 +2862,9 @@ void Image::_repeat_pixel_over_subsequent_memory(uint8_t *p_pixel, int p_pixel_s } void Image::fill(const Color &p_color) { + if (data.size() == 0) { + return; + } ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill in compressed or custom image formats."); uint8_t *dst_data_ptr = data.ptrw(); @@ -2859,6 +2878,9 @@ void Image::fill(const Color &p_color) { } void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) { + if (data.size() == 0) { + return; + } ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill rect in compressed or custom image formats."); Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect.abs()); @@ -2931,7 +2953,7 @@ void Image::_set_data(const Dictionary &p_data) { ERR_FAIL_COND(ddformat == FORMAT_MAX); - create(dwidth, dheight, dmipmaps, ddformat, ddata); + initialize_data(dwidth, dheight, dmipmaps, ddformat, ddata); } Dictionary Image::_get_data() const { @@ -3294,8 +3316,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); - ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty); - ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data); + ClassDB::bind_static_method("Image", D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty); + ClassDB::bind_static_method("Image", D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data); + ClassDB::bind_method(D_METHOD("set_data", "width", "height", "use_mipmaps", "format", "data"), &Image::set_data); ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty); @@ -3460,9 +3483,7 @@ Ref<Image> Image::rgbe_to_srgb() { ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>()); - Ref<Image> new_image; - new_image.instantiate(); - new_image->create(width, height, false, Image::FORMAT_RGB8); + Ref<Image> new_image = create_empty(width, height, false, Image::FORMAT_RGB8); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { diff --git a/core/io/image.h b/core/io/image.h index fd264a7a38..194ee05bc7 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -279,12 +279,12 @@ public: void normalize(); //for normal maps /** - * Create a new image of a given size and format. Current image will be lost + * Creates new internal image data of a given size and format. Current image will be lost. */ - void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format); - void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void initialize_data(const char **p_xpm); - void create(const char **p_xpm); /** * returns true when the image is empty (0,0) in size */ @@ -303,13 +303,9 @@ public: Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const; Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const; - void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - create(p_width, p_height, p_use_mipmaps, p_format); - } - - void create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - create(p_width, p_height, p_use_mipmaps, p_format, p_data); - } + static Ref<Image> create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format); + static Ref<Image> create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); + void set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data); /** * create an empty image diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 2f863baaac..6cda840604 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -69,10 +69,31 @@ void ResourceFormatSaver::get_recognized_extensions(const Ref<Resource> &p_resou } } +bool ResourceFormatSaver::recognize_path(const Ref<Resource> &p_resource, const String &p_path) const { + bool ret = false; + if (GDVIRTUAL_CALL(_recognize_path, p_resource, p_path, ret)) { + return ret; + } + + String extension = p_path.get_extension(); + + List<String> extensions; + get_recognized_extensions(p_resource, &extensions); + + for (const String &E : extensions) { + if (E.nocasecmp_to(extension) == 0) { + return true; + } + } + + return false; +} + void ResourceFormatSaver::_bind_methods() { GDVIRTUAL_BIND(_save, "resource", "path", "flags"); GDVIRTUAL_BIND(_recognize, "resource"); GDVIRTUAL_BIND(_get_recognized_extensions, "resource"); + GDVIRTUAL_BIND(_recognize_path, "resource", "path"); } Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { @@ -90,17 +111,7 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, continue; } - List<String> extensions; - bool recognized = false; - saver[i]->get_recognized_extensions(p_resource, &extensions); - - for (const String &E : extensions) { - if (E.nocasecmp_to(extension) == 0) { - recognized = true; - } - } - - if (!recognized) { + if (!saver[i]->recognize_path(p_resource, path)) { continue; } diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 4fee2bcfd1..5e48ce88c3 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -44,11 +44,13 @@ protected: GDVIRTUAL3R(int64_t, _save, Ref<Resource>, String, uint32_t) GDVIRTUAL1RC(bool, _recognize, Ref<Resource>) GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, Ref<Resource>) + GDVIRTUAL2RC(bool, _recognize_path, Ref<Resource>, String) public: virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); virtual bool recognize(const Ref<Resource> &p_resource) const; virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize_path(const Ref<Resource> &p_resource, const String &p_path) const; virtual ~ResourceFormatSaver() {} }; diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index e573e8de19..200e5f5e83 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -37,11 +37,17 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) { String fname; fname.parse_utf8(p_fname); + int file_access_mode = 0; if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - (*fa) = FileAccess::open(fname, FileAccess::WRITE); - } else { - (*fa) = FileAccess::open(fname, FileAccess::READ); + file_access_mode |= FileAccess::WRITE; } + if (mode & ZLIB_FILEFUNC_MODE_READ) { + file_access_mode |= FileAccess::READ; + } + if (mode & ZLIB_FILEFUNC_MODE_CREATE) { + file_access_mode |= FileAccess::WRITE_READ; + } + (*fa) = FileAccess::open(fname, file_access_mode); if (fa->is_null()) { return nullptr; diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index ca56add2ab..41585943b3 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1528,7 +1528,10 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION; c.native_extension = p_extension; c.name = p_extension->class_name; - c.creation_func = parent->creation_func; + c.is_virtual = p_extension->is_virtual; + if (!p_extension->is_abstract) { + c.creation_func = parent->creation_func; + } c.inherits = parent->name; c.class_ptr = parent->class_ptr; c.inherits_ptr = parent; diff --git a/core/object/object.h b/core/object/object.h index 5ba5453b31..359ab0f211 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -295,6 +295,8 @@ struct ObjectNativeExtension { StringName parent_class_name; StringName class_name; bool editor_class = false; + bool is_virtual = false; + bool is_abstract = false; GDNativeExtensionClassSet set; GDNativeExtensionClassGet get; GDNativeExtensionClassGetPropertyList get_property_list; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 671b06f0ed..c86c8316fe 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -1567,10 +1567,11 @@ String String::num_real(double p_num, bool p_trailing) { #else int decimals = 6; #endif - // We want to align the digits to the above sane default, so we only - // need to subtract log10 for numbers with a positive power of ten. - if (p_num > 10) { - decimals -= (int)floor(log10(p_num)); + // We want to align the digits to the above sane default, so we only need + // to subtract log10 for numbers with a positive power of ten magnitude. + double abs_num = Math::abs(p_num); + if (abs_num > 10) { + decimals -= (int)floor(log10(abs_num)); } return num(p_num, decimals); } @@ -3674,7 +3675,7 @@ String String::simplify_path() const { if (p > 0) { bool only_chars = true; for (int i = 0; i < p; i++) { - if (!is_ascii_char(s[i])) { + if (!is_ascii_alphanumeric_char(s[i])) { only_chars = false; break; } diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 8b958814db..c6bbd43dc4 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -31,6 +31,7 @@ #include "array.h" #include "container_type_validate.h" +#include "core/math/math_funcs.h" #include "core/object/class_db.h" #include "core/object/script_language.h" #include "core/templates/hashfuncs.h" @@ -299,6 +300,11 @@ Variant Array::back() const { return operator[](_p->array.size() - 1); } +Variant Array::pick_random() const { + ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + return operator[](Math::rand() % _p->array.size()); +} + int Array::find(const Variant &p_value, int p_from) const { ERR_FAIL_COND_V(!_p->typed.validate(p_value, "find"), -1); return _p->array.find(p_value, p_from); diff --git a/core/variant/array.h b/core/variant/array.h index 3d9a794969..ee265a9ffd 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -79,6 +79,7 @@ public: Variant front() const; Variant back() const; + Variant pick_random() const; void sort(); void sort_custom(const Callable &p_callable); diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h index 6171c8c88f..427a337aab 100644 --- a/core/variant/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -79,9 +79,12 @@ struct ContainerTypeValidate { return true; } - ERR_FAIL_COND_V_MSG(type != p_variant.get_type(), false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(p_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'."); if (type != p_variant.get_type()) { - return false; + if (p_variant.get_type() == Variant::NIL && type == Variant::OBJECT) { + return true; + } + + ERR_FAIL_V_MSG(false, "Attempted to " + String(p_operation) + " a variable of type '" + Variant::get_type_name(p_variant.get_type()) + "' into a " + where + " of type '" + Variant::get_type_name(type) + "'."); } if (type != Variant::OBJECT) { @@ -90,7 +93,7 @@ struct ContainerTypeValidate { #ifdef DEBUG_ENABLED ObjectID object_id = p_variant; if (object_id == ObjectID()) { - return true; //fine its null; + return true; // This is fine, it's null. } Object *object = ObjectDB::get_instance(object_id); ERR_FAIL_COND_V_MSG(object == nullptr, false, "Attempted to " + String(p_operation) + " an invalid (previously freed?) object instance into a '" + String(where) + "."); @@ -101,7 +104,7 @@ struct ContainerTypeValidate { } #endif if (class_name == StringName()) { - return true; //all good, no class type requested + return true; // All good, no class type requested. } StringName obj_class = object->get_class_name(); @@ -110,12 +113,12 @@ struct ContainerTypeValidate { } if (script.is_null()) { - return true; //all good + return true; // All good, no script requested. } Ref<Script> other_script = object->get_script(); - //check base script.. + // Check base script.. ERR_FAIL_COND_V_MSG(other_script.is_null(), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); ERR_FAIL_COND_V_MSG(!other_script->inherits_script(script), false, "Attempted to " + String(p_operation) + " an object into a " + String(where) + ", that does not inherit from '" + String(script->get_class_name()) + "'."); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 900e3d8e77..688650f532 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1930,7 +1930,7 @@ static void _register_variant_builtin_methods() { bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, real_t) const>(&Basis::rotated), sarray("axis", "angle"), varray()); bind_method(Basis, scaled, sarray("scale"), varray()); bind_method(Basis, get_scale, sarray(), varray()); - bind_method(Basis, get_euler, sarray("order"), varray(Basis::EULER_ORDER_YXZ)); + bind_method(Basis, get_euler, sarray("order"), varray((int64_t)Basis::EULER_ORDER_YXZ)); bind_method(Basis, tdotx, sarray("with"), varray()); bind_method(Basis, tdoty, sarray("with"), varray()); bind_method(Basis, tdotz, sarray("with"), varray()); @@ -1940,7 +1940,7 @@ static void _register_variant_builtin_methods() { bind_method(Basis, get_rotation_quaternion, sarray(), varray()); bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); bind_static_method(Basis, from_scale, sarray("scale"), varray()); - bind_static_method(Basis, from_euler, sarray("euler", "order"), varray(Basis::EULER_ORDER_YXZ)); + bind_static_method(Basis, from_euler, sarray("euler", "order"), varray((int64_t)Basis::EULER_ORDER_YXZ)); /* AABB */ @@ -2053,6 +2053,7 @@ static void _register_variant_builtin_methods() { bind_method(Array, erase, sarray("value"), varray()); bind_method(Array, front, sarray(), varray()); bind_method(Array, back, sarray(), varray()); + bind_method(Array, pick_random, sarray(), varray()); bind_method(Array, find, sarray("what", "from"), varray(0)); bind_method(Array, rfind, sarray("what", "from"), varray(-1)); bind_method(Array, find_last, sarray("value"), varray()); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 7ae2368fe4..636710c934 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -826,7 +826,7 @@ VARIANT_ACCESSOR_NUMBER(Projection::Planes) template <> struct VariantInternalAccessor<Basis::EulerOrder> { static _FORCE_INLINE_ Basis::EulerOrder get(const Variant *v) { return Basis::EulerOrder(*VariantInternal::get_int(v)); } - static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = p_value; } + static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = (int64_t)p_value; } }; template <> diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index 6cca7955ae..301fd00d26 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -384,10 +384,6 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorDivNZ<Vector2, Vector2i, double>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::FLOAT); register_op<OperatorEvaluatorDivNZ<Vector2i, Vector2i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2I, Variant::INT); - register_op<OperatorEvaluatorDiv<Vector2, Vector2, Vector2>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::VECTOR2); - register_op<OperatorEvaluatorDiv<Vector2, Vector2, double>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::FLOAT); - register_op<OperatorEvaluatorDiv<Vector2, Vector2, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR2, Variant::INT); - register_op<OperatorEvaluatorDiv<Vector3, Vector3, Vector3>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::VECTOR3); register_op<OperatorEvaluatorDiv<Vector3, Vector3, double>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::FLOAT); register_op<OperatorEvaluatorDiv<Vector3, Vector3, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR3, Variant::INT); @@ -498,8 +494,6 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorBitXor<int64_t, int64_t, int64_t>>(Variant::OP_BIT_XOR, Variant::INT, Variant::INT); register_op<OperatorEvaluatorBitNeg<int64_t, int64_t>>(Variant::OP_BIT_NEGATE, Variant::INT, Variant::NIL); - register_op<OperatorEvaluatorBitNeg<int64_t, int64_t>>(Variant::OP_BIT_NEGATE, Variant::INT, Variant::NIL); - register_op<OperatorEvaluatorAlwaysTrue<Variant::OP_EQUAL, Variant::NIL, Variant::NIL>>(Variant::OP_EQUAL, Variant::NIL, Variant::NIL); register_op<OperatorEvaluatorEqual<bool, bool>>(Variant::OP_EQUAL, Variant::BOOL, Variant::BOOL); register_op<OperatorEvaluatorEqual<int64_t, int64_t>>(Variant::OP_EQUAL, Variant::INT, Variant::INT); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 43f85fcdbc..9eeb69d824 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -16,7 +16,7 @@ <return type="Variant" /> <param index="0" name="x" type="Variant" /> <description> - Returns the absolute value of a [Variant] parameter [param x] (i.e. non-negative value). Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. + Returns the absolute value of a [Variant] parameter [param x] (i.e. non-negative value). Variant types [int], [float], [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. [codeblock] var a = abs(-1) # a is 1 @@ -114,7 +114,7 @@ <param index="3" name="end" type="float" /> <param index="4" name="t" type="float" /> <description> - Returns the point at the given [param t] on a one-dimnesional [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by the given [param control_1], [param control_2], and [param end] points. + Returns the point at the given [param t] on a one-dimensional [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by the given [param control_1], [param control_2], and [param end] points. </description> </method> <method name="bytes_to_var"> @@ -143,7 +143,7 @@ i = ceil(1.001) # i is 2.0 [/codeblock] See also [method floor], [method round], and [method snapped]. - [b]Note:[/b] For better type safety, you can use [method ceilf], [method ceili], [method Vector2.ceil], [method Vector3.ceil] or [method Vector4.ceil] instead. + [b]Note:[/b] For better type safety, see [method ceilf], [method ceili], [method Vector2.ceil], [method Vector3.ceil] and [method Vector4.ceil]. </description> </method> <method name="ceilf"> @@ -151,7 +151,7 @@ <param index="0" name="x" type="float" /> <description> Rounds [param x] upward (towards positive infinity), returning the smallest whole number that is not less than [param x]. - A type-safe version of [method ceil], specialzied in floats. + A type-safe version of [method ceil], returning a [float]. </description> </method> <method name="ceili"> @@ -159,7 +159,7 @@ <param index="0" name="x" type="float" /> <description> Rounds [param x] upward (towards positive infinity), returning the smallest whole number that is not less than [param x]. - A type-safe version of [method ceil] that returns integer. + A type-safe version of [method ceil], returning an [int]. </description> </method> <method name="clamp"> @@ -168,7 +168,7 @@ <param index="1" name="min" type="Variant" /> <param index="2" name="max" type="Variant" /> <description> - Clamps the [Variant] [param value] and returns a value not less than [param min] and not more than [param max]. Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. + Clamps the [param value], returning a [Variant] not less than [param min] and not more than [param max]. Variant types [int], [float], [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. [codeblock] var a = clamp(-10, -1, 5) # a is -1 @@ -196,15 +196,13 @@ <param index="1" name="min" type="float" /> <param index="2" name="max" type="float" /> <description> - Clamps the float [param value] and returns a value not less than [param min] and not more than [param max]. + Clamps the [param value], returning a [float] not less than [param min] and not more than [param max]. [codeblock] var speed = 42.1 - # a is 20.0 - var a = clampf(speed, 1.0, 20.0) + var a = clampf(speed, 1.0, 20.5) # a is 20.5 speed = -10.0 - # a is -1.0 - a = clampf(speed, -1.0, 1.0) + var b = clampf(speed, -1.0, 1.0) # b is -1.0 [/codeblock] </description> </method> @@ -214,15 +212,13 @@ <param index="1" name="min" type="int" /> <param index="2" name="max" type="int" /> <description> - Clamps the integer [param value] and returns a value not less than [param min] and not more than [param max]. + Clamps the [param value], returning an [int] not less than [param min] and not more than [param max]. [codeblock] var speed = 42 - # a is 20 - var a = clampi(speed, 1, 20) + var a = clampi(speed, 1, 20) # a is 20 speed = -10 - # a is -1 - a = clampi(speed, -1, 1) + var b = clampi(speed, -1, 1) # b is -1 [/codeblock] </description> </method> @@ -244,8 +240,7 @@ <description> Returns the hyperbolic cosine of [param x] in radians. [codeblock] - # Prints 1.543081 - print(cosh(1)) + print(cosh(1)) # Prints 1.543081 [/codeblock] </description> </method> @@ -257,7 +252,7 @@ <param index="3" name="post" type="float" /> <param index="4" name="weight" type="float" /> <description> - Cubic interpolates between two values by the factor defined in [param weight] with pre and post values. + Cubic interpolates between two values by the factor defined in [param weight] with [param pre] and [param post] values. </description> </method> <method name="cubic_interpolate_angle"> @@ -268,7 +263,7 @@ <param index="3" name="post" type="float" /> <param index="4" name="weight" type="float" /> <description> - Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with pre and post values. See also [method lerp_angle]. + Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with [param pre] and [param post] values. See also [method lerp_angle]. </description> </method> <method name="cubic_interpolate_angle_in_time"> @@ -282,7 +277,7 @@ <param index="6" name="pre_t" type="float" /> <param index="7" name="post_t" type="float" /> <description> - Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with pre and post values. See also [method lerp_angle]. + Cubic interpolates between two rotation values with shortest path by the factor defined in [param weight] with [param pre] and [param post] values. See also [method lerp_angle]. It can perform smoother interpolation than [code]cubic_interpolate()[/code] by the time values. </description> </method> @@ -297,8 +292,8 @@ <param index="6" name="pre_t" type="float" /> <param index="7" name="post_t" type="float" /> <description> - Cubic interpolates between two values by the factor defined in [param weight] with pre and post values. - It can perform smoother interpolation than [code]cubic_interpolate()[/code] by the time values. + Cubic interpolates between two values by the factor defined in [param weight] with [param pre] and [param post] values. + It can perform smoother interpolation than [method cubic_interpolate] by the time values. </description> </method> <method name="db_to_linear"> @@ -314,8 +309,7 @@ <description> Converts an angle expressed in degrees to radians. [codeblock] - # r is 3.141593 - var r = deg_to_rad(180) + var r = deg_to_rad(180) # r is 3.141593 [/codeblock] </description> </method> @@ -342,7 +336,13 @@ <return type="String" /> <param index="0" name="error" type="int" /> <description> - Returns a human-readable name for the given error code. + Returns a human-readable name for the given [enum Error] code. + [codeblock] + print(OK) # Prints 0 + print(error_string(OK)) # Prints OK + print(error_string(ERR_BUSY)) # Prints Busy + print(error_string(ERR_OUT_OF_MEMORY)) # Prints Out of memory + [/codeblock] </description> </method> <method name="exp"> @@ -363,13 +363,11 @@ <description> Rounds [param x] downward (towards negative infinity), returning the largest whole number that is not more than [param x]. Supported types: [int], [float], [Vector2], [Vector3], [Vector4]. [codeblock] - # a is 2.0 - var a = floor(2.99) - # a is -3.0 - a = floor(-2.99) + var a = floor(2.99) # a is 2.0 + a = floor(-2.99) # a is -3.0 [/codeblock] See also [method ceil], [method round], and [method snapped]. - [b]Note:[/b] For better type safety, you can use [method floorf], [method floori], [method Vector2.floor], [method Vector3.floor] or [method Vector4.floor] instead. + [b]Note:[/b] For better type safety, see [method floorf], [method floori], [method Vector2.floor], [method Vector3.floor] and [method Vector4.floor]. </description> </method> <method name="floorf"> @@ -377,7 +375,7 @@ <param index="0" name="x" type="float" /> <description> Rounds [param x] downward (towards negative infinity), returning the largest whole number that is not more than [param x]. - A type-safe version of [method floor], specialzied in floats. + A type-safe version of [method floor], returning a [float]. </description> </method> <method name="floori"> @@ -385,7 +383,8 @@ <param index="0" name="x" type="float" /> <description> Rounds [param x] downward (towards negative infinity), returning the largest whole number that is not more than [param x]. - Equivalent of doing [code]int(x)[/code]. + A type-safe version of [method floor], returning an [int]. + [b]Note:[/b] This function is [i]not[/i] the same as [code]int(x)[/code], which rounds towards 0. </description> </method> <method name="fmod"> @@ -393,10 +392,9 @@ <param index="0" name="x" type="float" /> <param index="1" name="y" type="float" /> <description> - Returns the floating-point remainder of [code]x/y[/code], keeping the sign of [param x]. + Returns the floating-point remainder of [param x] divided by [param y], keeping the sign of [param x]. [codeblock] - # Remainder is 1.5 - var remainder = fmod(7, 5.5) + var remainder = fmod(7, 5.5) # remainder is 1.5 [/codeblock] For the integer remainder operation, use the [code]%[/code] operator. </description> @@ -406,21 +404,23 @@ <param index="0" name="x" type="float" /> <param index="1" name="y" type="float" /> <description> - Returns the floating-point modulus of [code]x/y[/code] that wraps equally in positive and negative. + Returns the floating-point modulus of [param x] divided by [param y], wrapping equally in positive and negative. [codeblock] + print(" (x) (fmod(x, 1.5)) (fposmod(x, 1.5))") for i in 7: - var x = 0.5 * i - 1.5 - print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)]) + var x = i * 0.5 - 1.5 + print("%4.1f %4.1f | %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)]) [/codeblock] Produces: [codeblock] - -1.5 -0.0 0.0 - -1.0 -1.0 0.5 - -0.5 -0.5 1.0 - 0.0 0.0 0.0 - 0.5 0.5 0.5 - 1.0 1.0 1.0 - 1.5 0.0 0.0 + (x) (fmod(x, 1.5)) (fposmod(x, 1.5)) + -1.5 -0.0 | 0.0 + -1.0 -1.0 | 0.5 + -0.5 -0.5 | 1.0 + 0.0 0.0 | 0.0 + 0.5 0.5 | 0.5 + 1.0 1.0 | 1.0 + 1.5 0.0 | 0.0 [/codeblock] </description> </method> @@ -428,7 +428,7 @@ <return type="int" /> <param index="0" name="variable" type="Variant" /> <description> - Returns the integer hash of the variable passed. + Returns the integer hash of the passed [param variable]. [codeblock] print(hash("a")) # Prints 177670 [/codeblock] @@ -438,7 +438,7 @@ <return type="Object" /> <param index="0" name="instance_id" type="int" /> <description> - Returns the Object that corresponds to [param instance_id]. All Objects have a unique instance ID. + Returns the [Object] that corresponds to [param instance_id]. All Objects have a unique instance ID. See also [method Object.get_instance_id]. [codeblock] var foo = "bar" func _ready(): @@ -458,12 +458,13 @@ [codeblock] # The interpolation ratio in the `lerp()` call below is 0.75. var middle = lerp(20, 30, 0.75) - # `middle` is now 27.5. + # middle is now 27.5. + # Now, we pretend to have forgotten the original ratio and want to get it back. var ratio = inverse_lerp(20, 30, 27.5) - # `ratio` is now 0.75. + # ratio is now 0.75. [/codeblock] - See also [method lerp] which performs the reverse of this operation, and [method remap] to map a continuous series of values to another. + See also [method lerp], which performs the reverse of this operation, and [method remap] to map a continuous series of values to another. </description> </method> <method name="is_equal_approx"> @@ -472,7 +473,7 @@ <param index="1" name="b" type="float" /> <description> Returns [code]true[/code] if [param a] and [param b] are approximately equal to each other. - Here, approximately equal means that [param a] and [param b] are within a small internal epsilon of each other, which scales with the magnitude of the numbers. + Here, "approximately equal" means that [param a] and [param b] are within a small internal epsilon of each other, which scales with the magnitude of the numbers. Infinity values of the same sign are considered equal. </description> </method> @@ -487,7 +488,7 @@ <return type="bool" /> <param index="0" name="x" type="float" /> <description> - Returns whether [param x] is an infinity value (either positive infinity or negative infinity). + Returns [code]true[/code] if [param x] is either positive infinity or negative infinity. </description> </method> <method name="is_instance_id_valid"> @@ -501,14 +502,14 @@ <return type="bool" /> <param index="0" name="instance" type="Variant" /> <description> - Returns whether [param instance] is a valid object (e.g. has not been deleted from memory). + Returns [code]true[/code] if [param instance] is a valid Object (e.g. has not been deleted from memory). </description> </method> <method name="is_nan"> <return type="bool" /> <param index="0" name="x" type="float" /> <description> - Returns whether [param x] is a NaN ("Not a Number" or invalid) value. + Returns [code]true[/code] if [param x] is a NaN ("Not a Number" or invalid) value. </description> </method> <method name="is_zero_approx"> @@ -516,7 +517,7 @@ <param index="0" name="x" type="float" /> <description> Returns [code]true[/code] if [param x] is zero or almost zero. - This method is faster than using [method is_equal_approx] with one value as zero. + This function is faster than using [method is_equal_approx] with one value as zero. </description> </method> <method name="lerp"> @@ -525,13 +526,13 @@ <param index="1" name="to" type="Variant" /> <param index="2" name="weight" type="Variant" /> <description> - Linearly interpolates between two values by the factor defined in [param weight]. To perform interpolation, [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. Use [method clamp] on the result of [method lerp] if this is not desired. - Both [param from] and [param to] must have matching types. Supported types: [float], [Vector2], [Vector3], [Vector4], [Color], [Quaternion], [Basis]. + Linearly interpolates between two values by the factor defined in [param weight]. To perform interpolation, [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. If this is not desired, use [method clamp] on the result of this function. + Both [param from] and [param to] must be the same type. Supported types: [float], [Vector2], [Vector3], [Vector4], [Color], [Quaternion], [Basis]. [codeblock] lerp(0, 4, 0.75) # Returns 3.0 [/codeblock] See also [method inverse_lerp] which performs the reverse of this operation. To perform eased interpolation with [method lerp], combine it with [method ease] or [method smoothstep]. See also [method remap] to map a continuous series of values to another. - [b]Note:[/b] For better type safety, you can use [method lerpf], [method Vector2.lerp], [method Vector3.lerp], [method Vector4.lerp], [method Color.lerp], [method Quaternion.slerp] or [method Basis.slerp] instead. + [b]Note:[/b] For better type safety, use [method lerpf], [method Vector2.lerp], [method Vector3.lerp], [method Vector4.lerp], [method Color.lerp], [method Quaternion.slerp] or [method Basis.slerp]. </description> </method> <method name="lerp_angle"> @@ -540,7 +541,7 @@ <param index="1" name="to" type="float" /> <param index="2" name="weight" type="float" /> <description> - Linearly interpolates between two angles (in radians) by a normalized value. + Linearly interpolates between two angles (in radians) by a [param weight] value between 0.0 and 1.0. Similar to [method lerp], but interpolates correctly when the angles wrap around [constant @GDScript.TAU]. To perform eased interpolation with [method lerp_angle], combine it with [method ease] or [method smoothstep]. [codeblock] extends Sprite @@ -551,7 +552,7 @@ rotation = lerp_angle(min_angle, max_angle, elapsed) elapsed += delta [/codeblock] - [b]Note:[/b] This method lerps through the shortest path between [param from] and [param to]. However, when these two angles are approximately [code]PI + k * TAU[/code] apart for any integer [code]k[/code], it's not obvious which way they lerp due to floating-point precision errors. For example, [code]lerp_angle(0, PI, weight)[/code] lerps counter-clockwise, while [code]lerp_angle(0, PI + 5 * TAU, weight)[/code] lerps clockwise. + [b]Note:[/b] This function lerps through the shortest path between [param from] and [param to]. However, when these two angles are approximately [code]PI + k * TAU[/code] apart for any integer [code]k[/code], it's not obvious which way they lerp due to floating-point precision errors. For example, [code]lerp_angle(0, PI, weight)[/code] lerps counter-clockwise, while [code]lerp_angle(0, PI + 5 * TAU, weight)[/code] lerps clockwise. </description> </method> <method name="lerpf"> @@ -560,7 +561,7 @@ <param index="1" name="to" type="float" /> <param index="2" name="weight" type="float" /> <description> - Linearly interpolates between two values by the factor defined in [param weight]. To perform interpolation, [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. + Linearly interpolates between two values by the factor defined in [param weight]. To perform interpolation, [param weight] should be between [code]0.0[/code] and [code]1.0[/code] (inclusive). However, values outside this range are allowed and can be used to perform [i]extrapolation[/i]. If this is not desired, use [method clampf] on the result of this function. [codeblock] lerp(0, 4, 0.75) # Returns 3.0 [/codeblock] @@ -584,7 +585,7 @@ <return type="float" /> <param index="0" name="x" type="float" /> <description> - Natural logarithm. The amount of time needed to reach a certain level of continuous growth. + Returns the natural logarithm of [param x]. This is the amount of time needed to reach a certain level of continuous growth. [b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm. [codeblock] log(10) # Returns 2.302585 @@ -595,7 +596,7 @@ <method name="max" qualifiers="vararg"> <return type="Variant" /> <description> - Returns the maximum of the given values. This method can take any number of arguments. + Returns the maximum of the given values. This function can take any number of arguments. [codeblock] max(1, 7, 3, -6, 5) # Returns 7 [/codeblock] @@ -606,9 +607,9 @@ <param index="0" name="a" type="float" /> <param index="1" name="b" type="float" /> <description> - Returns the maximum of two float values. + Returns the maximum of two [float] values. [codeblock] - maxf(3.6, 24) # Returns 24.0 + maxf(3.6, 24) # Returns 24.0 maxf(-3.99, -4) # Returns -3.99 [/codeblock] </description> @@ -618,9 +619,9 @@ <param index="0" name="a" type="int" /> <param index="1" name="b" type="int" /> <description> - Returns the maximum of two int values. + Returns the maximum of two [int] values. [codeblock] - maxi(1, 2) # Returns 2 + maxi(1, 2) # Returns 2 maxi(-3, -4) # Returns -3 [/codeblock] </description> @@ -628,7 +629,7 @@ <method name="min" qualifiers="vararg"> <return type="Variant" /> <description> - Returns the minimum of the given values. This method can take any number of arguments. + Returns the minimum of the given values. This function can take any number of arguments. [codeblock] min(1, 7, 3, -6, 5) # Returns -6 [/codeblock] @@ -639,9 +640,9 @@ <param index="0" name="a" type="float" /> <param index="1" name="b" type="float" /> <description> - Returns the minimum of two float values. + Returns the minimum of two [float] values. [codeblock] - minf(3.6, 24) # Returns 3.6 + minf(3.6, 24) # Returns 3.6 minf(-3.99, -4) # Returns -4.0 [/codeblock] </description> @@ -651,9 +652,9 @@ <param index="0" name="a" type="int" /> <param index="1" name="b" type="int" /> <description> - Returns the minimum of two int values. + Returns the minimum of two [int] values. [codeblock] - mini(1, 2) # Returns 1 + mini(1, 2) # Returns 1 mini(-3, -4) # Returns -4 [/codeblock] </description> @@ -667,8 +668,8 @@ Moves [param from] toward [param to] by the [param delta] value. Use a negative [param delta] value to move away. [codeblock] - move_toward(5, 10, 4) # Returns 9 - move_toward(10, 5, 4) # Returns 6 + move_toward(5, 10, 4) # Returns 9 + move_toward(10, 5, 4) # Returns 6 move_toward(10, 5, -1.5) # Returns 11.5 [/codeblock] </description> @@ -677,17 +678,17 @@ <return type="int" /> <param index="0" name="value" type="int" /> <description> - Returns the nearest equal or larger power of 2 for integer [param value]. + Returns the nearest equal or larger power of 2 for the integer [param value]. In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value <= a[/code] for some non-negative integer [code]n[/code]. [codeblock] nearest_po2(3) # Returns 4 nearest_po2(4) # Returns 4 nearest_po2(5) # Returns 8 - nearest_po2(0) # Returns 0 (this may not be what you expect) - nearest_po2(-1) # Returns 0 (this may not be what you expect) + nearest_po2(0) # Returns 0 (this may not be expected) + nearest_po2(-1) # Returns 0 (this may not be expected) [/codeblock] - [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [param value] (in reality, 1 is the smallest integer power of 2). + [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for negative values of [param value] (in reality, 1 is the smallest integer power of 2). </description> </method> <method name="pingpong"> @@ -695,18 +696,18 @@ <param index="0" name="value" type="float" /> <param index="1" name="length" type="float" /> <description> - Returns the [param value] wrapped between [code]0[/code] and the [param length]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [param length] side (like a triangle wave). If [param length] is less than zero, it becomes positive. + Wraps [param value] between [code]0[/code] and the [param length]. If the limit is reached, the next value the function returns is decreased to the [code]0[/code] side or increased to the [param length] side (like a triangle wave). If [param length] is less than zero, it becomes positive. [codeblock] - pingpong(-3.0, 3.0) # Returns 3 - pingpong(-2.0, 3.0) # Returns 2 - pingpong(-1.0, 3.0) # Returns 1 - pingpong(0.0, 3.0) # Returns 0 - pingpong(1.0, 3.0) # Returns 1 - pingpong(2.0, 3.0) # Returns 2 - pingpong(3.0, 3.0) # Returns 3 - pingpong(4.0, 3.0) # Returns 2 - pingpong(5.0, 3.0) # Returns 1 - pingpong(6.0, 3.0) # Returns 0 + pingpong(-3.0, 3.0) # Returns 3.0 + pingpong(-2.0, 3.0) # Returns 2.0 + pingpong(-1.0, 3.0) # Returns 1.0 + pingpong(0.0, 3.0) # Returns 0.0 + pingpong(1.0, 3.0) # Returns 1.0 + pingpong(2.0, 3.0) # Returns 2.0 + pingpong(3.0, 3.0) # Returns 3.0 + pingpong(4.0, 3.0) # Returns 2.0 + pingpong(5.0, 3.0) # Returns 1.0 + pingpong(6.0, 3.0) # Returns 0.0 [/codeblock] </description> </method> @@ -715,20 +716,22 @@ <param index="0" name="x" type="int" /> <param index="1" name="y" type="int" /> <description> - Returns the integer modulus of [code]x/y[/code] that wraps equally in positive and negative. + Returns the integer modulus of [param x] divided by [param y] that wraps equally in positive and negative. [codeblock] + print("#(i) (i % 3) (posmod(i, 3))") for i in range(-3, 4): - print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)]) + print("%2d %2d | %2d" % [i, i % 3, posmod(i, 3)]) [/codeblock] Produces: [codeblock] - -3 0 0 - -2 -2 1 - -1 -1 2 - 0 0 0 - 1 1 1 - 2 2 2 - 3 0 0 + (i) (i % 3) (posmod(i, 3)) + -3 0 | 0 + -2 -2 | 1 + -1 -1 | 2 + 0 0 | 0 + 1 1 | 1 + 2 2 | 2 + 3 0 | 0 [/codeblock] </description> </method> @@ -738,6 +741,7 @@ <param index="1" name="exp" type="float" /> <description> Returns the result of [param base] raised to the power of [param exp]. + In GDScript, this is the equivalent of the [code]**[/code] operator. [codeblock] pow(2, 5) # Returns 32 [/codeblock] @@ -750,7 +754,7 @@ var a = [1, 2, 3] print("a", "b", a) # Prints ab[1, 2, 3] [/codeblock] - [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. + [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print] or [method print_rich]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed. </description> </method> <method name="print_rich" qualifiers="vararg"> @@ -778,11 +782,12 @@ </method> <method name="printraw" qualifiers="vararg"> <description> - Prints one or more arguments to strings in the best way possible to console. No newline is added at the end. + Prints one or more arguments to strings in the best way possible to console. Unlike [method print], no newline is automatically added at the end. [codeblock] printraw("A") printraw("B") - # Prints AB + printraw("C") + # Prints ABC [/codeblock] [b]Note:[/b] Due to limitations with Godot's built-in console, this only prints to the terminal. If you need to print in the editor, use another method, such as [method print]. </description> @@ -809,7 +814,7 @@ [codeblock] push_error("test error") # Prints "test error" to debugger and terminal as error call [/codeblock] - [b]Note:[/b] Errors printed this way will not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. + [b]Note:[/b] This function does not pause project execution. To print an error message and pause project execution in debug builds, use [code]assert(false, "test error")[/code] instead. </description> </method> <method name="push_warning" qualifiers="vararg"> @@ -827,6 +832,8 @@ Converts an angle expressed in radians to degrees. [codeblock] rad_to_deg(0.523599) # Returns 30 + rad_to_deg(PI) # Returns 180 + rad_to_deg(PI * 2) # Returns 360 [/codeblock] </description> </method> @@ -834,7 +841,14 @@ <return type="PackedInt64Array" /> <param index="0" name="seed" type="int" /> <description> - Random from seed: pass a [param seed], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits. + Given a [param seed], returns a [PackedInt64Array] of size [code]2[/code], where its first element is the randomised [int] value, and the second element is the same as [param seed]. Passing the same [param seed] consistently returns the same array. + [b]Note:[/b] "Seed" here refers to the internal state of the pseudo random number generator, currently implemented as a 64 bit integer. + [codeblock] + var a = rand_from_seed(4) + + print(a[0]) # Prints 2879024997 + print(a[1]) # Prints 4 + [/codeblock] </description> </method> <method name="randf"> @@ -851,9 +865,10 @@ <param index="0" name="from" type="float" /> <param index="1" name="to" type="float" /> <description> - Returns a random floating point value on the interval between [param from] and [param to] (inclusive). + Returns a random floating point value between [param from] and [param to] (inclusive). [codeblock] - prints(randf_range(-10, 10), randf_range(-10, 10)) # Prints e.g. -3.844535 7.45315 + randf_range(0, 20.5) # Returns e.g. 7.45315 + randf_range(-10, 10) # Returns e.g. -3.844535 [/codeblock] </description> </method> @@ -884,15 +899,15 @@ <description> Returns a random signed 32-bit integer between [param from] and [param to] (inclusive). If [param to] is lesser than [param from], they are swapped. [codeblock] - print(randi_range(0, 1)) # Prints 0 or 1 - print(randi_range(-10, 1000)) # Prints any number from -10 to 1000 + randi_range(0, 1) # Returns either 0 or 1 + randi_range(-10, 1000) # Returns random integer between -10 and 1000 [/codeblock] </description> </method> <method name="randomize"> <description> - Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time. - [b]Note:[/b] This method is called automatically when the project is run. If you need to fix the seed to have reproducible results, use [method seed] to initialize the random number generator. + Randomizes the seed (or the internal state) of the random number generator. The current implementation uses a number based on the device's time. + [b]Note:[/b] This function is called automatically when the project is run. If you need to fix the seed to have consistent, reproducible results, use [method seed] to initialize the random number generator. </description> </method> <method name="remap"> @@ -903,63 +918,67 @@ <param index="3" name="ostart" type="float" /> <param index="4" name="ostop" type="float" /> <description> - Maps a [param value] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. See also [method lerp] and [method inverse_lerp]. If [param value] is outside [code][istart, istop][/code], then the resulting value will also be outside [code][ostart, ostop][/code]. Use [method clamp] on the result of [method remap] if this is not desired. + Maps a [param value] from range [code][istart, istop][/code] to [code][ostart, ostop][/code]. See also [method lerp] and [method inverse_lerp]. If [param value] is outside [code][istart, istop][/code], then the resulting value will also be outside [code][ostart, ostop][/code]. If this is not desired, use [method clamp] on the result of this function. [codeblock] remap(75, 0, 100, -1, 1) # Returns 0.5 [/codeblock] - For complex use cases where you need multiple ranges, consider using [Curve] or [Gradient] instead. + For complex use cases where multiple ranges are needed, consider using [Curve] or [Gradient] instead. </description> </method> <method name="rid_allocate_id"> <return type="int" /> <description> - Allocate a unique ID which can be used by the implementation to construct a RID. This is used mainly from native extensions to implement servers. + Allocates a unique ID which can be used by the implementation to construct a RID. This is used mainly from native extensions to implement servers. </description> </method> <method name="rid_from_int64"> <return type="RID" /> <param index="0" name="base" type="int" /> <description> - Create a RID from an int64. This is used mainly from native extensions to build servers. + Creates a RID from a [param base]. This is used mainly from native extensions to build servers. </description> </method> <method name="round"> <return type="Variant" /> <param index="0" name="x" type="Variant" /> <description> - Rounds [param x] to the nearest whole number, with halfway cases rounded away from zero. Supported types: [int], [float], [Vector2], [Vector3], [Vector4]. + Rounds [param x] to the nearest whole number, with halfway cases rounded away from 0. Supported types: [int], [float], [Vector2], [Vector3], [Vector4]. [codeblock] round(2.4) # Returns 2 round(2.5) # Returns 3 round(2.6) # Returns 3 [/codeblock] See also [method floor], [method ceil], and [method snapped]. - [b]Note:[/b] For better type safety, you can use [method roundf], [method roundi], [method Vector2.round], [method Vector3.round] or [method Vector4.round] instead. + [b]Note:[/b] For better type safety, use [method roundf], [method roundi], [method Vector2.round], [method Vector3.round] or [method Vector4.round], instead. </description> </method> <method name="roundf"> <return type="float" /> <param index="0" name="x" type="float" /> <description> - Rounds [param x] to the nearest whole number, with halfway cases rounded away from zero. - A type-safe version of [method round], specialzied in floats. + Rounds [param x] to the nearest whole number, with halfway cases rounded away from 0. + A type-safe version of [method round], returning a [float]. </description> </method> <method name="roundi"> <return type="int" /> <param index="0" name="x" type="float" /> <description> - Rounds [param x] to the nearest whole number, with halfway cases rounded away from zero. - A type-safe version of [method round] that returns integer. + Rounds [param x] to the nearest whole number, with halfway cases rounded away from 0. + A type-safe version of [method round], returning an [int]. </description> </method> <method name="seed"> <param index="0" name="base" type="int" /> <description> - Sets seed for the random number generator. + Sets the seed for the random number generator to [param base]. Setting the seed manually can ensure consistent, repeatable results for most random functions. [codeblock] - var my_seed = "Godot Rocks" - seed(my_seed.hash()) + var my_seed = "Godot Rocks".hash() + seed(my_seed) + var a = randf() + randi() + seed(my_seed) + var b = randf() + randi() + # a and b are now identical [/codeblock] </description> </method> @@ -967,7 +986,7 @@ <return type="Variant" /> <param index="0" name="x" type="Variant" /> <description> - Returns the sign of [param x] as same type of [Variant] as [param x] with each component being -1, 0 and 1 for each negative, zero and positive values respectivelu. Variant types [int], [float] (real), [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. + Returns the sign of [param x] as same type of [Variant] as [param x] with each component being -1, 0 and 1 for each negative, zero and positive values respectively. Variant types [int], [float], [Vector2], [Vector2i], [Vector3] and [Vector3i] are supported. [codeblock] sign(-6.0) # Returns -1 sign(0.0) # Returns 0 @@ -981,11 +1000,11 @@ <return type="float" /> <param index="0" name="x" type="float" /> <description> - Returns the sign of [param x] as a float: -1.0 or 1.0. Returns 0.0 if [param x] is 0. + Returns the sign of [param x] as a [float]: -1.0 or 1.0. Returns 0.0 if [param x] is 0.0. [codeblock] - sign(-6.0) # Returns -1.0 + sign(-6.5) # Returns -1.0 sign(0.0) # Returns 0.0 - sign(6.0) # Returns 1.0 + sign(6.5) # Returns 1.0 [/codeblock] </description> </method> @@ -993,7 +1012,7 @@ <return type="int" /> <param index="0" name="x" type="int" /> <description> - Returns the sign of [param x] as an integer: -1 or 1. Returns 0 if [param x] is 0. + Returns the sign of [param x] as an [int]: -1 or 1. Returns 0 if [param x] is 0. [codeblock] sign(-6) # Returns -1 sign(0) # Returns 0 @@ -1047,7 +1066,7 @@ <param index="0" name="x" type="float" /> <param index="1" name="step" type="float" /> <description> - Snaps float value [param x] to a given [param step]. This can also be used to round a floating point number to an arbitrary number of decimals. + Snaps the float value [param x] to a given [param step]. This can also be used to round a floating point number to an arbitrary number of decimals. [codeblock] snapped(100, 32) # Returns 96 snapped(3.14159, 0.01) # Returns 3.14 @@ -1061,9 +1080,11 @@ <description> Returns the square root of [param x], where [param x] is a non-negative number. [codeblock] - sqrt(9) # Returns 3 + sqrt(9) # Returns 3 + sqrt(10.24) # Returns 3.2 + sqrt(-1) # Returns NaN [/codeblock] - [b]Note:[/b] Negative values of [param x] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#. + [b]Note:[/b] Negative values of [param x] return NaN ("Not a Number"). in C#, if you need negative inputs, use [code]System.Numerics.Complex[/code]. </description> </method> <method name="step_decimals"> @@ -1072,30 +1093,27 @@ <description> Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation. [codeblock] - # n is 0 - var n = step_decimals(5) - # n is 4 - n = step_decimals(1.0005) - # n is 9 - n = step_decimals(0.000000005) + var n = step_decimals(5) # n is 0 + n = step_decimals(1.0005) # n is 4 + n = step_decimals(0.000000005) # n is 9 [/codeblock] </description> </method> <method name="str" qualifiers="vararg"> <return type="String" /> <description> - Converts one or more arguments of any type to string in the best way possible. + Converts one or more arguments of any [Variant] type to [String] in the best way possible. </description> </method> <method name="str_to_var"> <return type="Variant" /> <param index="0" name="string" type="String" /> <description> - Converts a formatted [param string] that was returned by [method var_to_str] to the original value. + Converts a formatted [param string] that was returned by [method var_to_str] to the original [Variant]. [codeblock] - var a = '{ "a": 1, "b": 2 }' - var b = str_to_var(a) - print(b["a"]) # Prints 1 + var a = '{ "a": 1, "b": 2 }' # a is a String + var b = str_to_var(a) # b is a Dictionary + print(b["a"]) # Prints 1 [/codeblock] </description> </method> @@ -1116,7 +1134,7 @@ Returns the hyperbolic tangent of [param x]. [codeblock] var a = log(2.0) # Returns 0.693147 - tanh(a) # Returns 0.6 + tanh(a) # Returns 0.6 [/codeblock] </description> </method> @@ -1124,7 +1142,7 @@ <return type="int" /> <param index="0" name="variable" type="Variant" /> <description> - Returns the internal type of the given Variant object, using the [enum Variant.Type] values. + Returns the internal type of the given [param variable], using the [enum Variant.Type] values. [codeblock] var json = JSON.new() json.parse('["a", "b", "c"]') @@ -1148,19 +1166,19 @@ <return type="PackedByteArray" /> <param index="0" name="variable" type="Variant" /> <description> - Encodes a [Variant] value to a byte array. Encoding objects is allowed (and can potentially include code). Deserialization can be done with [method bytes_to_var_with_objects]. + Encodes a [Variant] value to a byte array. Encoding objects is allowed (and can potentially include executable code). Deserialization can be done with [method bytes_to_var_with_objects]. </description> </method> <method name="var_to_str"> <return type="String" /> <param index="0" name="variable" type="Variant" /> <description> - Converts a Variant [param variable] to a formatted string that can later be parsed using [method str_to_var]. + Converts a [Variant] [param variable] to a formatted [String] that can then be parsed using [method str_to_var]. [codeblock] a = { "a": 1, "b": 2 } print(var_to_str(a)) [/codeblock] - prints + Prints: [codeblock] { "a": 1, @@ -1173,7 +1191,7 @@ <return type="Variant" /> <param index="0" name="obj" type="Variant" /> <description> - Returns a weak reference to an object, or [code]null[/code] if the argument is invalid. + Returns a weak reference to an object, or [code]null[/code] if [param obj] is invalid. A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it. </description> </method> @@ -1183,9 +1201,8 @@ <param index="1" name="min" type="Variant" /> <param index="2" name="max" type="Variant" /> <description> - Wraps the [Variant] [param value] between [param min] and [param max]. - Usable for creating loop-alike behavior or infinite surfaces. - Variant types [int] and [float] (real) are supported. If any of the argument is [float] the result will be [float], otherwise it is [int]. + Wraps the [Variant] [param value] between [param min] and [param max]. Can be used for creating loop-alike behavior or infinite surfaces. + Variant types [int] and [float] are supported. If any of the arguments is [float] this function returns a [float], otherwise it returns an [int]. [codeblock] var a = wrap(4, 5, 10) # a is 9 (int) @@ -1204,8 +1221,7 @@ <param index="1" name="min" type="float" /> <param index="2" name="max" type="float" /> <description> - Wraps float [param value] between [param min] and [param max]. - Usable for creating loop-alike behavior or infinite surfaces. + Wraps the float [param value] between [param min] and [param max]. Can be used for creating loop-alike behavior or infinite surfaces. [codeblock] # Infinite loop between 5.0 and 9.9 value = wrapf(value + 0.1, 5.0, 10.0) @@ -1228,8 +1244,7 @@ <param index="1" name="min" type="int" /> <param index="2" name="max" type="int" /> <description> - Wraps integer [param value] between [param min] and [param max]. - Usable for creating loop-alike behavior or infinite surfaces. + Wraps the integer [param value] between [param min] and [param max]. Can be used for creating loop-alike behavior or infinite surfaces. [codeblock] # Infinite loop between 5 and 9 frame = wrapi(frame + 1, 5, 10) @@ -1290,6 +1305,7 @@ The [Marshalls] singleton. </member> <member name="NativeExtensionManager" type="NativeExtensionManager" setter="" getter=""> + The [NativeExtensionManager] singleton. </member> <member name="NavigationMeshGenerator" type="NavigationMeshGenerator" setter="" getter=""> The [NavigationMeshGenerator] singleton. @@ -1384,8 +1400,10 @@ General horizontal alignment, usually used for [Separator], [ScrollBar], [Slider], etc. </constant> <constant name="CLOCKWISE" value="0" enum="ClockDirection"> + Clockwise rotation. Used by some methods (e.g. [method Image.rotate_90]). </constant> <constant name="COUNTERCLOCKWISE" value="1" enum="ClockDirection"> + Counter-clockwise rotation. Used by some methods (e.g. [method Image.rotate_90]). </constant> <constant name="HORIZONTAL_ALIGNMENT_LEFT" value="0" enum="HorizontalAlignment"> Horizontal left alignment, usually for text-derived classes. @@ -1880,34 +1898,34 @@ / key. </constant> <constant name="KEY_0" value="48" enum="Key"> - Number 0. + Number 0 key. </constant> <constant name="KEY_1" value="49" enum="Key"> - Number 1. + Number 1 key. </constant> <constant name="KEY_2" value="50" enum="Key"> - Number 2. + Number 2 key. </constant> <constant name="KEY_3" value="51" enum="Key"> - Number 3. + Number 3 key. </constant> <constant name="KEY_4" value="52" enum="Key"> - Number 4. + Number 4 key. </constant> <constant name="KEY_5" value="53" enum="Key"> - Number 5. + Number 5 key. </constant> <constant name="KEY_6" value="54" enum="Key"> - Number 6. + Number 6 key. </constant> <constant name="KEY_7" value="55" enum="Key"> - Number 7. + Number 7 key. </constant> <constant name="KEY_8" value="56" enum="Key"> - Number 8. + Number 8 key. </constant> <constant name="KEY_9" value="57" enum="Key"> - Number 9. + Number 9 key. </constant> <constant name="KEY_COLON" value="58" enum="Key"> : key. @@ -2267,10 +2285,10 @@ Enum value which doesn't correspond to any mouse button. This is used to initialize [enum MouseButton] properties with a generic state. </constant> <constant name="MOUSE_BUTTON_LEFT" value="1" enum="MouseButton"> - Primary mouse button, usually the left button. + Primary mouse button, usually assigned to the left button. </constant> <constant name="MOUSE_BUTTON_RIGHT" value="2" enum="MouseButton"> - Secondary mouse button, usually the right button. + Secondary mouse button, usually assigned to the right button. </constant> <constant name="MOUSE_BUTTON_MIDDLE" value="3" enum="MouseButton"> Middle mouse button. @@ -2288,10 +2306,10 @@ Mouse wheel right button (only present on some mice). </constant> <constant name="MOUSE_BUTTON_XBUTTON1" value="8" enum="MouseButton"> - Extra mouse button 1 (only present on some mice). + Extra mouse button 1. This is sometimes present, usually to the sides of the mouse. </constant> <constant name="MOUSE_BUTTON_XBUTTON2" value="9" enum="MouseButton"> - Extra mouse button 2 (only present on some mice). + Extra mouse button 2. This is sometimes present, usually to the sides of the mouse. </constant> <constant name="MOUSE_BUTTON_MASK_LEFT" value="1" enum="MouseButton"> Primary mouse button mask, usually for the left button. @@ -2357,7 +2375,7 @@ Game controller D-pad right button. </constant> <constant name="JOY_BUTTON_MISC1" value="15" enum="JoyButton"> - Game controller SDL miscellaneous button. Corresponds to Xbox share button, PS5 microphone button, Nintendo capture button. + Game controller SDL miscellaneous button. Corresponds to Xbox share button, PS5 microphone button, Nintendo Switch capture button. </constant> <constant name="JOY_BUTTON_PADDLE1" value="16" enum="JoyButton"> Game controller SDL paddle 1 button. @@ -2379,9 +2397,9 @@ </constant> <constant name="JOY_BUTTON_MAX" value="128" enum="JoyButton"> The maximum number of game controller buttons supported by the engine. The actual limit may be lower on specific platforms: - - Android: Up to 36 buttons. - - Linux: Up to 80 buttons. - - Windows and macOS: Up to 128 buttons. + - [b]Android:[/b] Up to 36 buttons. + - [b]Linux:[/b] Up to 80 buttons. + - [b]Windows[/b] and [b]macOS:[/b] Up to 128 buttons. </constant> <constant name="JOY_AXIS_INVALID" value="-1" enum="JoyAxis"> An invalid game controller axis. @@ -2468,16 +2486,18 @@ MIDI system reset message. Reset all receivers in the system to power-up status. It should not be sent on power-up itself. </constant> <constant name="OK" value="0" enum="Error"> - Methods that return [enum Error] return [constant OK] when no error occurred. Note that many functions don't return an error code but will print error messages to standard output. - Since [constant OK] has value 0, and all other failure codes are positive integers, it can also be used in boolean checks, e.g.: + Methods that return [enum Error] return [constant OK] when no error occurred. + Since [constant OK] has value 0, and all other error constants are positive integers, it can also be used in boolean checks. For example: [codeblock] - var err = method_that_returns_error() - if err != OK: - print("Failure!") - # Or, equivalent: - if err: - print("Still failing!") + var error = method_that_returns_error() + if error != OK: + printerr("Failure!") + + # Or, alternatively: + if error: + printerr("Still failing!") [/codeblock] + [b]Note:[/b] Many functions do not return an error code, but will print error messages to standard output. </constant> <constant name="FAILED" value="1" enum="Error"> Generic error. @@ -2615,100 +2635,101 @@ Skip error. </constant> <constant name="ERR_HELP" value="46" enum="Error"> - Help error. + Help error. Used internally when passing [code]--version[/code] or [code]--help[/code] as executable options. </constant> <constant name="ERR_BUG" value="47" enum="Error"> - Bug error. + Bug error, caused by an implementation issue in the method. + [b]Note:[/b] If a built-in method returns this code, please open an issue on [url=https://github.com/godotengine/godot/issues]the GitHub Issue Tracker[/url]. </constant> <constant name="ERR_PRINTER_ON_FIRE" value="48" enum="Error"> - Printer on fire error. (This is an easter egg, no engine methods return this error code.) + Printer on fire error (This is an easter egg, no built-in methods return this error code). </constant> <constant name="PROPERTY_HINT_NONE" value="0" enum="PropertyHint"> - No hint for the edited property. + The property has no hint for the editor. </constant> <constant name="PROPERTY_HINT_RANGE" value="1" enum="PropertyHint"> - Hints that an integer or float property should be within a range specified via the hint string [code]"min,max"[/code] or [code]"min,max,step"[/code]. The hint string can optionally include [code]"or_greater"[/code] and/or [code]"or_less"[/code] to allow manual input going respectively above the max or below the min values. Example: [code]"-360,360,1,or_greater,or_less"[/code]. + Hints that an [int] or [float] property should be within a range specified via the hint string [code]"min,max"[/code] or [code]"min,max,step"[/code]. The hint string can optionally include [code]"or_greater"[/code] and/or [code]"or_less"[/code] to allow manual input going respectively above the max or below the min values. Example: [code]"-360,360,1,or_greater,or_less"[/code]. Additionally, other keywords can be included: [code]"exp"[/code] for exponential range editing, [code]"radians"[/code] for editing radian angles in degrees, [code]"degrees"[/code] to hint at an angle and [code]"hide_slider"[/code] to hide the slider. </constant> <constant name="PROPERTY_HINT_ENUM" value="2" enum="PropertyHint"> - Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string. + Hints that an [int], [float], or [String] property is an enumerated value to pick in a list specified via a hint string. The hint string is a comma separated list of names such as [code]"Hello,Something,Else"[/code]. Whitespaces are [b]not[/b] removed from either end of a name. For integer and float properties, the first name in the list has value 0, the next 1, and so on. Explicit values can also be specified by appending [code]:integer[/code] to the name, e.g. [code]"Zero,One,Three:3,Four,Six:6"[/code]. </constant> <constant name="PROPERTY_HINT_ENUM_SUGGESTION" value="3" enum="PropertyHint"> - Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. - Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values. + Hints that a [String] property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. + Unlike [constant PROPERTY_HINT_ENUM], a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values. </constant> <constant name="PROPERTY_HINT_EXP_EASING" value="4" enum="PropertyHint"> - Hints that a float property should be edited via an exponential easing function. The hint string can include [code]"attenuation"[/code] to flip the curve horizontally and/or [code]"positive_only"[/code] to exclude in/out easing and limit values to be greater than or equal to zero. + Hints that a [float] property should be edited via an exponential easing function. The hint string can include [code]"attenuation"[/code] to flip the curve horizontally and/or [code]"positive_only"[/code] to exclude in/out easing and limit values to be greater than or equal to zero. </constant> <constant name="PROPERTY_HINT_LINK" value="5" enum="PropertyHint"> - Hints that a vector property should allow linking values (e.g. to edit both [code]x[/code] and [code]y[/code] together). + Hints that a vector property should allow its components to be linked. For example, this allows [member Vector2.x] and [member Vector2.y] to be edited together. </constant> <constant name="PROPERTY_HINT_FLAGS" value="6" enum="PropertyHint"> - Hints that an integer property is a bitmask with named bit flags. For example, to allow toggling bits 0, 1, 2 and 4, the hint could be something like [code]"Bit0,Bit1,Bit2,,Bit4"[/code]. + Hints that an [int] property is a bitmask with named bit flags. For example, to allow toggling bits 0, 1, 2 and 4, the hint could be something like [code]"Bit0,Bit1,Bit2,,Bit4"[/code]. </constant> <constant name="PROPERTY_HINT_LAYERS_2D_RENDER" value="7" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 2D render layers. + Hints that an [int] property is a bitmask using the optionally named 2D render layers. </constant> <constant name="PROPERTY_HINT_LAYERS_2D_PHYSICS" value="8" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 2D physics layers. + Hints that an [int] property is a bitmask using the optionally named 2D physics layers. </constant> <constant name="PROPERTY_HINT_LAYERS_2D_NAVIGATION" value="9" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 2D navigation layers. + Hints that an [int] property is a bitmask using the optionally named 2D navigation layers. </constant> <constant name="PROPERTY_HINT_LAYERS_3D_RENDER" value="10" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 3D render layers. + Hints that an [int] property is a bitmask using the optionally named 3D render layers. </constant> <constant name="PROPERTY_HINT_LAYERS_3D_PHYSICS" value="11" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 3D physics layers. + Hints that an [int] property is a bitmask using the optionally named 3D physics layers. </constant> <constant name="PROPERTY_HINT_LAYERS_3D_NAVIGATION" value="12" enum="PropertyHint"> - Hints that an integer property is a bitmask using the optionally named 3D navigation layers. + Hints that an [int] property is a bitmask using the optionally named 3D navigation layers. </constant> <constant name="PROPERTY_HINT_FILE" value="13" enum="PropertyHint"> - Hints that a string property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. + Hints that a [String] property is a path to a file. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. </constant> <constant name="PROPERTY_HINT_DIR" value="14" enum="PropertyHint"> - Hints that a string property is a path to a directory. Editing it will show a file dialog for picking the path. + Hints that a [String] property is a path to a directory. Editing it will show a file dialog for picking the path. </constant> <constant name="PROPERTY_HINT_GLOBAL_FILE" value="15" enum="PropertyHint"> - Hints that a string property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards like [code]"*.png,*.jpg"[/code]. + Hints that a [String] property is an absolute path to a file outside the project folder. Editing it will show a file dialog for picking the path. The hint string can be a set of filters with wildcards, like [code]"*.png,*.jpg"[/code]. </constant> <constant name="PROPERTY_HINT_GLOBAL_DIR" value="16" enum="PropertyHint"> - Hints that a string property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path. + Hints that a [String] property is an absolute path to a directory outside the project folder. Editing it will show a file dialog for picking the path. </constant> <constant name="PROPERTY_HINT_RESOURCE_TYPE" value="17" enum="PropertyHint"> Hints that a property is an instance of a [Resource]-derived type, optionally specified via the hint string (e.g. [code]"Texture2D"[/code]). Editing it will show a popup menu of valid resource types to instantiate. </constant> <constant name="PROPERTY_HINT_MULTILINE_TEXT" value="18" enum="PropertyHint"> - Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed. + Hints that a [String] property is text with line breaks. Editing it will show a text input field where line breaks can be typed. </constant> <constant name="PROPERTY_HINT_EXPRESSION" value="19" enum="PropertyHint"> - Hints that a string property is an [Expression]. + Hints that a [String] property is an [Expression]. </constant> <constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="20" enum="PropertyHint"> - Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use. + Hints that a [String] property should show a placeholder text on its input field, if empty. The hint string is the placeholder text to use. </constant> <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="21" enum="PropertyHint"> - Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited. + Hints that a [Color] property should be edited without affecting its transparency ([member Color.a] is not editable). </constant> <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="22" enum="PropertyHint"> - Hints that an image is compressed using lossy compression. + Hints that an image is compressed using lossy compression. The editor does not internally use this property hint. </constant> <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="23" enum="PropertyHint"> - Hints that an image is compressed using lossless compression. + Hints that an image is compressed using lossless compression. The editor does not internally use this property hint. </constant> <constant name="PROPERTY_HINT_OBJECT_ID" value="24" enum="PropertyHint"> </constant> <constant name="PROPERTY_HINT_TYPE_STRING" value="25" enum="PropertyHint"> - Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance: + Hints that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For example: [codeblock] - hint_string = "%s:" % [TYPE_INT] # Array of inteters. + hint_string = "%s:" % [TYPE_INT] # Array of integers. hint_string = "%s:%s:" % [TYPE_ARRAY, TYPE_REAL] # Two-dimensional array of floats. hint_string = "%s/%s:Resource" % [TYPE_OBJECT, TYPE_OBJECT] # Array of resources. hint_string = "%s:%s/%s:Resource" % [TYPE_ARRAY, TYPE_OBJECT, TYPE_OBJECT] # Two-dimensional array of resources. [/codeblock] - [b]Note:[/b] The final colon is required to specify for properly detecting built-in types. + [b]Note:[/b] The final colon is required for properly detecting built-in types. </constant> <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="26" enum="PropertyHint"> </constant> @@ -2729,6 +2750,7 @@ <constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="34" enum="PropertyHint"> </constant> <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="35" enum="PropertyHint"> + Hints that a property's size (in bytes) is too big to be displayed, when debugging a running project. The debugger uses this hint internally. </constant> <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="36" enum="PropertyHint"> </constant> @@ -2743,28 +2765,31 @@ <constant name="PROPERTY_HINT_ARRAY_TYPE" value="40" enum="PropertyHint"> </constant> <constant name="PROPERTY_HINT_LOCALE_ID" value="42" enum="PropertyHint"> - Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country. + Hints that a [String] property is a locale code. Editing it will show a locale dialog for picking language and country. </constant> <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="43" enum="PropertyHint"> - Hints that a dictionary property is string translation map. Dictionary keys are locale codes and, values are translated strings. + Hints that a [Dictionary] property is string translation map. Dictionary keys are locale codes and, values are translated strings. </constant> <constant name="PROPERTY_HINT_NODE_TYPE" value="44" enum="PropertyHint"> </constant> <constant name="PROPERTY_HINT_HIDE_QUATERNION_EDIT" value="45" enum="PropertyHint"> - Hints that a quaternion property should disable the temporary euler editor. + Hints that a [Quaternion] property should disable the temporary euler editor. </constant> <constant name="PROPERTY_HINT_PASSWORD" value="46" enum="PropertyHint"> - Hints that a string property is a password, and every character is replaced with the secret character. + Hints that a [String] property is a password. Every character of the string is displayed as the secret character (typically [code]*[/code]). + An optional placeholder text can be shown on its input field, similarly to [constant PROPERTY_HINT_PLACEHOLDER_TEXT]. </constant> <constant name="PROPERTY_HINT_MAX" value="47" enum="PropertyHint"> + Represents the size of the [enum PropertyHint] enum. </constant> <constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags"> + The property is not stored, and does not display in the editor. This is the default for non-exported properties. </constant> <constant name="PROPERTY_USAGE_STORAGE" value="2" enum="PropertyUsageFlags"> The property is serialized and saved in the scene file (default). </constant> <constant name="PROPERTY_USAGE_EDITOR" value="4" enum="PropertyUsageFlags"> - The property is shown in the editor inspector (default). + The property is shown in the editor Inspector (default). </constant> <constant name="PROPERTY_USAGE_CHECKABLE" value="8" enum="PropertyUsageFlags"> The property can be checked in the editor inspector. @@ -2810,8 +2835,10 @@ <constant name="PROPERTY_USAGE_INTERNAL" value="524288" enum="PropertyUsageFlags"> </constant> <constant name="PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE" value="1048576" enum="PropertyUsageFlags"> + If the property is a [Resource], a new copy of it is always created when calling [method Node.duplicate] or [method Resource.duplicate]. </constant> <constant name="PROPERTY_USAGE_HIGH_END_GFX" value="2097152" enum="PropertyUsageFlags"> + The property is only shown in the editor if modern renderers are supported (GLES3 is excluded). </constant> <constant name="PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT" value="4194304" enum="PropertyUsageFlags"> </constant> @@ -2826,9 +2853,10 @@ <constant name="PROPERTY_USAGE_EDITOR_BASIC_SETTING" value="134217728" enum="PropertyUsageFlags"> </constant> <constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags"> - The property is read-only in the editor inspector. + The property is read-only in the editor Inspector. </constant> <constant name="PROPERTY_USAGE_ARRAY" value="536870912" enum="PropertyUsageFlags"> + The property is an array. </constant> <constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags"> Default usage (storage, editor and network). @@ -2852,14 +2880,16 @@ Flag for a virtual method. </constant> <constant name="METHOD_FLAG_VARARG" value="16" enum="MethodFlags"> + Flag for a method with a variable number of arguments. </constant> <constant name="METHOD_FLAG_STATIC" value="32" enum="MethodFlags"> + Flag for a static method. </constant> <constant name="METHOD_FLAG_OBJECT_CORE" value="64" enum="MethodFlags"> - Used internally. Allows to not dump core virtuals such as [code]_notification[/code] to the JSON API. + Used internally. Allows to not dump core virtual methods (such as [method Object._notification]) to the JSON API. </constant> <constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags"> - Default method flags. + Default method flags (normal). </constant> <constant name="TYPE_NIL" value="0" enum="Variant.Type"> Variable is [code]null[/code]. @@ -2871,7 +2901,7 @@ Variable is of type [int]. </constant> <constant name="TYPE_FLOAT" value="3" enum="Variant.Type"> - Variable is of type [float] (real). + Variable is of type [float]. </constant> <constant name="TYPE_STRING" value="4" enum="Variant.Type"> Variable is of type [String]. @@ -2898,8 +2928,10 @@ Variable is of type [Transform2D]. </constant> <constant name="TYPE_VECTOR4" value="12" enum="Variant.Type"> + Variable is of type [Vector4]. </constant> <constant name="TYPE_VECTOR4I" value="13" enum="Variant.Type"> + Variable is of type [Vector4i]. </constant> <constant name="TYPE_PLANE" value="14" enum="Variant.Type"> Variable is of type [Plane]. @@ -2917,6 +2949,7 @@ Variable is of type [Transform3D]. </constant> <constant name="TYPE_PROJECTION" value="19" enum="Variant.Type"> + Variable is of type [Projection]. </constant> <constant name="TYPE_COLOR" value="20" enum="Variant.Type"> Variable is of type [Color]. diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index 1ac3e6b64c..ca454cafa3 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -188,6 +188,7 @@ <param index="0" name="from" type="Vector3" /> <param index="1" name="dir" type="Vector3" /> <description> + Returns [code]true[/code] if the given ray intersects with this [AABB]. Ray length is infinite. </description> </method> <method name="intersects_segment" qualifiers="const"> diff --git a/doc/classes/AStar2D.xml b/doc/classes/AStar2D.xml index 6e76df647e..2414b068e6 100644 --- a/doc/classes/AStar2D.xml +++ b/doc/classes/AStar2D.xml @@ -271,7 +271,7 @@ <return type="void" /> <param index="0" name="num_nodes" type="int" /> <description> - Reserves space internally for [param num_nodes] points, useful if you're adding a known large number of points at once, for a grid for instance. New capacity must be greater or equals to old capacity. + Reserves space internally for [param num_nodes] points, useful if you're adding a known large number of points at once, such as points on a grid. New capacity must be greater or equals to old capacity. </description> </method> <method name="set_point_disabled"> diff --git a/doc/classes/AStar3D.xml b/doc/classes/AStar3D.xml index 45b1019bab..4e8394195d 100644 --- a/doc/classes/AStar3D.xml +++ b/doc/classes/AStar3D.xml @@ -298,7 +298,7 @@ <return type="void" /> <param index="0" name="num_nodes" type="int" /> <description> - Reserves space internally for [param num_nodes] points, useful if you're adding a known large number of points at once, for a grid for instance. New capacity must be greater or equals to old capacity. + Reserves space internally for [param num_nodes] points. Useful if you're adding a known large number of points at once, such as points on a grid. New capacity must be greater or equals to old capacity. </description> </method> <method name="set_point_disabled"> diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index df0fb11ac7..008296713d 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -26,6 +26,7 @@ [/csharp] [/codeblocks] Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] to be played back. Animation tracks have different types, each with its own set of dedicated methods. Check [enum TrackType] to see available types. + [b]Note:[/b] For 3D position/rotation/scale, using the dedicated [constant TYPE_POSITION_3D], [constant TYPE_ROTATION_3D] and [constant TYPE_SCALE_3D] track types instead of [constant TYPE_VALUE] is recommended for performance reasons. </description> <tutorials> <link title="Animation documentation index">$DOCS_URL/tutorials/animation/index.html</link> @@ -209,6 +210,7 @@ <param index="1" name="time" type="float" /> <param index="2" name="amount" type="float" /> <description> + Inserts a key in a given blend shape track. Returns the key index. </description> </method> <method name="clear"> @@ -223,6 +225,8 @@ <param index="1" name="fps" type="int" default="120" /> <param index="2" name="split_tolerance" type="float" default="4.0" /> <description> + Compress the animation and all its tracks in-place. This will make [method track_is_compressed] return [code]true[/code] once called on this [Animation]. Compressed tracks require less memory to be played, and are designed to be used for complex 3D animations (such as cutscenes) imported from external 3D software. Compression is lossy, but the difference is usually not noticeable in real world conditions. + [b]Note:[/b] Compressed tracks have various limitations (such as not being editable from the editor), so only use compressed animations if you actually need them. </description> </method> <method name="copy_track"> @@ -278,6 +282,7 @@ <param index="1" name="time" type="float" /> <param index="2" name="position" type="Vector3" /> <description> + Inserts a key in a given 3D position track. Returns the key index. </description> </method> <method name="remove_track"> @@ -293,6 +298,7 @@ <param index="1" name="time" type="float" /> <param index="2" name="rotation" type="Quaternion" /> <description> + Inserts a key in a given 3D rotation track. Returns the key index. </description> </method> <method name="scale_track_insert_key"> @@ -301,6 +307,7 @@ <param index="1" name="time" type="float" /> <param index="2" name="scale" type="Vector3" /> <description> + Inserts a key in a given 3D scale track. Returns the key index. </description> </method> <method name="track_find_key" qualifiers="const"> @@ -385,6 +392,7 @@ <return type="bool" /> <param index="0" name="track_idx" type="int" /> <description> + Returns [code]true[/code] if the track is compressed, [code]false[/code] otherwise. See also [method compress]. </description> </method> <method name="track_is_enabled" qualifiers="const"> @@ -569,15 +577,19 @@ </signals> <constants> <constant name="TYPE_VALUE" value="0" enum="TrackType"> - Value tracks set values in node properties, but only those which can be Interpolated. + Value tracks set values in node properties, but only those which can be interpolated. For 3D position/rotation/scale, using the dedicated [constant TYPE_POSITION_3D], [constant TYPE_ROTATION_3D] and [constant TYPE_SCALE_3D] track types instead of [constant TYPE_VALUE] is recommended for performance reasons. </constant> <constant name="TYPE_POSITION_3D" value="1" enum="TrackType"> + 3D position track (values are stored in [Vector3]s). </constant> <constant name="TYPE_ROTATION_3D" value="2" enum="TrackType"> + 3D rotation track (values are stored in [Quaternion]s). </constant> <constant name="TYPE_SCALE_3D" value="3" enum="TrackType"> + 3D scale track (values are stored in [Vector3]s). </constant> <constant name="TYPE_BLEND_SHAPE" value="4" enum="TrackType"> + Blend shape track. </constant> <constant name="TYPE_METHOD" value="5" enum="TrackType"> Method tracks call functions with given arguments per key. @@ -598,7 +610,7 @@ Linear interpolation. </constant> <constant name="INTERPOLATION_CUBIC" value="2" enum="InterpolationType"> - Cubic interpolation. + Cubic interpolation. This looks smoother than linear interpolation, but is more expensive to interpolate. Stick to [constant INTERPOLATION_LINEAR] for complex 3D animations imported from external software, even if it requires using a higher animation framerate in return. </constant> <constant name="INTERPOLATION_LINEAR_ANGLE" value="3" enum="InterpolationType"> Linear interpolation with shortest path rotation. diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml index c32cc5c5b8..bf5ac7fa9e 100644 --- a/doc/classes/AnimationPlayer.xml +++ b/doc/classes/AnimationPlayer.xml @@ -242,7 +242,7 @@ The process notification in which to update animations. </member> <member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> - The speed scaling ratio. For instance, if this value is 1, then the animation plays at normal speed. If it's 0.5, then it plays at half speed. If it's 2, then it plays at double speed. + The speed scaling ratio. For example, if this value is 1, then the animation plays at normal speed. If it's 0.5, then it plays at half speed. If it's 2, then it plays at double speed. </member> <member name="reset_on_save" type="bool" setter="set_reset_on_save_enabled" getter="is_reset_on_save_enabled" default="true"> This is used by the editor. If set to [code]true[/code], the scene will be saved with the effects of the reset animation (the animation with the key [code]"RESET"[/code]) applied as if it had been seeked to time 0, with the editor keeping the values that the scene had before saving. diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 047089c917..0f76639caf 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -58,6 +58,7 @@ <param index="2" name="class_name" type="StringName" /> <param index="3" name="script" type="Variant" /> <description> + Creates a typed array from the [param base] array. The base array can't be already typed. See [method set_typed] for more details. </description> </constructor> <constructor name="Array"> @@ -315,16 +316,19 @@ <method name="get_typed_builtin" qualifiers="const"> <return type="int" /> <description> + Returns the [code]TYPE[/code] constant for a typed array. If the [Array] is not typed, returns [constant @GlobalScope.TYPE_NIL]. </description> </method> <method name="get_typed_class_name" qualifiers="const"> <return type="StringName" /> <description> + Returns a class name of a typed [Array] of type [constant @GlobalScope.TYPE_OBJECT]. </description> </method> <method name="get_typed_script" qualifiers="const"> <return type="Variant" /> <description> + Returns the script associated with a typed array tied to a class name. </description> </method> <method name="has" qualifiers="const"> @@ -393,11 +397,13 @@ <method name="is_read_only" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if the array is read-only. See [method set_read_only]. Arrays are automatically read-only if declared with [code]const[/code] keyword. </description> </method> <method name="is_typed" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if the array is typed. Typed arrays can only store elements of their associated type and provide type safety for the [code][][/code] operator. Methods of typed array still return [Variant]. </description> </method> <method name="map" qualifiers="const"> @@ -429,6 +435,16 @@ Returns the minimum value contained in the array if all elements are of comparable types. If the elements can't be compared, [code]null[/code] is returned. </description> </method> + <method name="pick_random" qualifiers="const"> + <return type="Variant" /> + <description> + Returns a random value from the target array. + [codeblock] + var array: Array[int] = [1, 2, 3, 4] + print(array.pick_random()) # Prints either of the four numbers. + [/codeblock] + </description> + </method> <method name="pop_at"> <return type="Variant" /> <param index="0" name="position" type="int" /> @@ -517,6 +533,7 @@ <return type="void" /> <param index="0" name="enable" type="bool" /> <description> + Makes the [Array] read-only, i.e. disabled modifying of the array's elements. Does not apply to nested content, e.g. content of nested arrays. </description> </method> <method name="set_typed"> @@ -525,6 +542,8 @@ <param index="1" name="class_name" type="StringName" /> <param index="2" name="script" type="Variant" /> <description> + Makes the [Array] typed. The [param type] should be one of the [@GlobalScope] [code]TYPE[/code] constants. [param class_name] is optional and can only be provided for [constant @GlobalScope.TYPE_OBJECT]. [param script] can only be provided if [param class_name] is not empty. + The method fails if an array is already typed. </description> </method> <method name="shuffle"> @@ -610,6 +629,7 @@ <return type="bool" /> <param index="0" name="array" type="Array" /> <description> + Assigns a different [Array] to this array reference. It the array is typed, the new array's type must be compatible and its elements will be automatically converted. </description> </method> </methods> diff --git a/doc/classes/AudioStreamGeneratorPlayback.xml b/doc/classes/AudioStreamGeneratorPlayback.xml index 1c02dbd3ce..c16198c5c6 100644 --- a/doc/classes/AudioStreamGeneratorPlayback.xml +++ b/doc/classes/AudioStreamGeneratorPlayback.xml @@ -27,7 +27,7 @@ <method name="get_frames_available" qualifiers="const"> <return type="int" /> <description> - Returns the number of audio data frames left to play. If this returned number reaches [code]0[/code], the audio will stop playing until frames are added again. Therefore, make sure your script can always generate and push new audio frames fast enough to avoid audio cracking. + Returns the number of frames that can be pushed to the audio sample data buffer without overflowing it. If the result is [code]0[/code], the buffer is full. </description> </method> <method name="get_skips" qualifiers="const"> diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml index dbc7d0fb29..de980eab0c 100644 --- a/doc/classes/BaseMaterial3D.xml +++ b/doc/classes/BaseMaterial3D.xml @@ -115,7 +115,7 @@ The color used by the backlight effect. Represents the light passing through an object. </member> <member name="backlight_enabled" type="bool" setter="set_feature" getter="get_feature" default="false"> - If [code]true[/code], the backlight effect is enabled. + If [code]true[/code], the backlight effect is enabled. See also [member subsurf_scatter_transmittance_enabled]. </member> <member name="backlight_texture" type="Texture2D" setter="set_texture" getter="get_texture"> Texture used to control the backlight effect per-pixel. Added to [member backlight]. @@ -289,6 +289,7 @@ [b]Note:[/b] If [member detail_enabled] is [code]true[/code], the [member detail_albedo] texture is drawn [i]below[/i] the [member normal_texture]. To display a normal map [i]above[/i] the [member detail_albedo] texture, use [member detail_normal] instead. </member> <member name="orm_texture" type="Texture2D" setter="set_texture" getter="get_texture"> + The Occlusion/Roughness/Metallic texture to use. This is a more efficient replacement of [member ao_texture], [member roughness_texture] and [member metallic_texture] in [ORMMaterial3D]. Ambient occlusion is stored in the red channel. Roughness map is stored in the green channel. Metallic map is stored in the blue channel. The alpha channel is ignored. </member> <member name="particles_anim_h_frames" type="int" setter="set_particles_anim_h_frames" getter="get_particles_anim_h_frames"> The number of horizontal frames in the particle sprite sheet. Only enabled when using [constant BILLBOARD_PARTICLES]. See [member billboard_mode]. @@ -350,13 +351,13 @@ </member> <member name="specular_mode" type="int" setter="set_specular_mode" getter="get_specular_mode" enum="BaseMaterial3D.SpecularMode" default="0"> The method for rendering the specular blob. See [enum SpecularMode]. - [b]Note:[/b] Only applies to the specular blob. Does not affect specular reflections from the Sky, SSR, or ReflectionProbes. + [b]Note:[/b] [member specular_mode] only applies to the specular blob. It does not affect specular reflections from the sky, screen-space reflections, [VoxelGI], SDFGI or [ReflectionProbe]s. To disable reflections from these sources as well, set [member metallic_specular] to [code]0.0[/code] instead. </member> <member name="subsurf_scatter_enabled" type="bool" setter="set_feature" getter="get_feature" default="false"> If [code]true[/code], subsurface scattering is enabled. Emulates light that penetrates an object's surface, is scattered, and then emerges. </member> <member name="subsurf_scatter_skin_mode" type="bool" setter="set_flag" getter="get_flag" default="false"> - If [code]true[/code], subsurface scattering will use a special mode optimized for the color and density of human skin. + If [code]true[/code], subsurface scattering will use a special mode optimized for the color and density of human skin, such as boosting the intensity of the red channel in subsurface scattering. </member> <member name="subsurf_scatter_strength" type="float" setter="set_subsurface_scattering_strength" getter="get_subsurface_scattering_strength" default="0.0"> The strength of the subsurface scattering effect. @@ -365,14 +366,19 @@ Texture used to control the subsurface scattering strength. Stored in the red texture channel. Multiplied by [member subsurf_scatter_strength]. </member> <member name="subsurf_scatter_transmittance_boost" type="float" setter="set_transmittance_boost" getter="get_transmittance_boost" default="0.0"> + The intensity of the subsurface scattering transmittance effect. </member> <member name="subsurf_scatter_transmittance_color" type="Color" setter="set_transmittance_color" getter="get_transmittance_color" default="Color(1, 1, 1, 1)"> + The color to multiply the subsurface scattering transmittance effect with. Ignored if [member subsurf_scatter_skin_mode] is [code]true[/code]. </member> <member name="subsurf_scatter_transmittance_depth" type="float" setter="set_transmittance_depth" getter="get_transmittance_depth" default="0.1"> + The depth of the subsurface scattering transmittance effect. </member> <member name="subsurf_scatter_transmittance_enabled" type="bool" setter="set_feature" getter="get_feature" default="false"> + If [code]true[/code], enables subsurface scattering transmittance. Only effective if [member subsurf_scatter_enabled] is [code]true[/code]. See also [member backlight_enabled]. </member> <member name="subsurf_scatter_transmittance_texture" type="Texture2D" setter="set_texture" getter="get_texture"> + The texture to use for multiplying the intensity of the subsurface scattering transmitteance intensity. See also [member subsurf_scatter_texture]. Ignored if [member subsurf_scatter_skin_mode] is [code]true[/code]. </member> <member name="texture_filter" type="int" setter="set_texture_filter" getter="get_texture_filter" enum="BaseMaterial3D.TextureFilter" default="3"> Filter flags for the texture. See [enum TextureFilter] for options. @@ -385,6 +391,7 @@ If [code]true[/code], transparency is enabled on the body. See also [member blend_mode]. </member> <member name="use_particle_trails" type="bool" setter="set_flag" getter="get_flag" default="false"> + If [code]true[/code], enables parts of the shader required for [GPUParticles3D] trails to function. This also requires using a mesh with appropriate skinning, such as [RibbonTrailMesh] or [TubeTrailMesh]. Enabling this feature outside of materials used in [GPUParticles3D] meshes will break material rendering. </member> <member name="use_point_size" type="bool" setter="set_flag" getter="get_flag" default="false"> If [code]true[/code], render point size can be changed. @@ -682,6 +689,7 @@ Enables the skin mode for subsurface scattering which is used to improve the look of subsurface scattering when used for human skin. </constant> <constant name="FLAG_PARTICLE_TRAILS_MODE" value="19" enum="Flags"> + Enables parts of the shader required for [GPUParticles3D] trails to function. This also requires using a mesh with appropriate skinning, such as [RibbonTrailMesh] or [TubeTrailMesh]. Enabling this feature outside of materials used in [GPUParticles3D] meshes will break material rendering. </constant> <constant name="FLAG_ALBEDO_TEXTURE_MSDF" value="20" enum="Flags"> Enables multichannel signed distance field rendering shader. diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml index f499be34a0..652d6d2407 100644 --- a/doc/classes/Basis.xml +++ b/doc/classes/Basis.xml @@ -70,6 +70,7 @@ <param index="0" name="euler" type="Vector3" /> <param index="1" name="order" type="int" default="2" /> <description> + Constructs a pure rotation Basis matrix from Euler angles in the specified Euler rotation order. By default, use YXZ order (most common). </description> </method> <method name="from_scale" qualifiers="static"> @@ -197,16 +198,22 @@ </members> <constants> <constant name="EULER_ORDER_XYZ" value="0"> + Euler angle composing/decomposing order where X component is first, then Y, then Z. </constant> <constant name="EULER_ORDER_XZY" value="1"> + Euler angle composing/decomposing order where X component is first, then Z, then Y. </constant> <constant name="EULER_ORDER_YXZ" value="2"> + Euler angle composing/decomposing order where Y component is first, then X, then Z. </constant> <constant name="EULER_ORDER_YZX" value="3"> + Euler angle composing/decomposing order where Y component is first, then Z, then X. </constant> <constant name="EULER_ORDER_ZXY" value="4"> + Euler angle composing/decomposing order where Z component is first, then X, then Y. </constant> <constant name="EULER_ORDER_ZYX" value="5"> + Euler angle composing/decomposing order where Z component is first, then Y, then X. </constant> <constant name="IDENTITY" value="Basis(1, 0, 0, 0, 1, 0, 0, 0, 1)"> The identity basis, with no rotation or scaling applied. diff --git a/doc/classes/BoneMap.xml b/doc/classes/BoneMap.xml index f7a4845b7d..7135406d3c 100644 --- a/doc/classes/BoneMap.xml +++ b/doc/classes/BoneMap.xml @@ -8,6 +8,7 @@ By assigning the actual [Skeleton3D] bone name as the key value, it maps the [Skeleton3D] to the [SkeletonProfile]. </description> <tutorials> + <link title="Retargeting 3D Skeletons">$DOCS_URL/tutorials/assets_pipeline/retargeting_3d_skeletons.html</link> </tutorials> <methods> <method name="find_profile_bone_name" qualifiers="const"> diff --git a/doc/classes/CPUParticles2D.xml b/doc/classes/CPUParticles2D.xml index 906789d09f..314e46d9d0 100644 --- a/doc/classes/CPUParticles2D.xml +++ b/doc/classes/CPUParticles2D.xml @@ -29,12 +29,14 @@ <return type="float" /> <param index="0" name="param" type="int" enum="CPUParticles2D.Parameter" /> <description> + Returns the maximum value range for the given parameter. </description> </method> <method name="get_param_min" qualifiers="const"> <return type="float" /> <param index="0" name="param" type="int" enum="CPUParticles2D.Parameter" /> <description> + Returns the minimum value range for the given parameter. </description> </method> <method name="get_particle_flag" qualifiers="const"> @@ -63,6 +65,7 @@ <param index="0" name="param" type="int" enum="CPUParticles2D.Parameter" /> <param index="1" name="value" type="float" /> <description> + Sets the maximum value for the given parameter. </description> </method> <method name="set_param_min"> @@ -70,6 +73,7 @@ <param index="0" name="param" type="int" enum="CPUParticles2D.Parameter" /> <param index="1" name="value" type="float" /> <description> + Sets the minimum value for the given parameter. </description> </method> <method name="set_particle_flag"> @@ -89,29 +93,38 @@ Each particle's rotation will be animated along this [Curve]. </member> <member name="angle_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum initial rotation applied to each particle, in degrees. </member> <member name="angle_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member angle_max]. </member> <member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> Each particle's angular velocity will vary along this [Curve]. </member> <member name="angular_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second. </member> <member name="angular_velocity_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member angular_velocity_max]. </member> <member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> Each particle's animation offset will vary along this [Curve]. </member> <member name="anim_offset_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum animation offset that corresponds to frame index in the texture. [code]0[/code] is the first frame, [code]1[/code] is the last one. See [member CanvasItemMaterial.particles_animation]. </member> <member name="anim_offset_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member anim_offset_max]. </member> <member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve"> Each particle's animation speed will vary along this [Curve]. </member> <member name="anim_speed_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum particle animation speed. Animation speed of [code]1[/code] means that the particles will make full [code]0[/code] to [code]1[/code] offset cycle during lifetime, [code]2[/code] means [code]2[/code] cycles etc. + With animation speed greater than [code]1[/code], remember to enable [member CanvasItemMaterial.particles_anim_loop] property if you want the animation to repeat. </member> <member name="anim_speed_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member anim_speed_max]. </member> <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)"> Each particle's initial color. If [member texture] is defined, it will be multiplied by this color. @@ -126,8 +139,10 @@ Damping will vary along this [Curve]. </member> <member name="damping_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + The maximum rate at which particles lose velocity. For example value of [code]100[/code] means that the particle will go from [code]100[/code] velocity to [code]0[/code] in [code]1[/code] second. </member> <member name="damping_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member damping_max]. </member> <member name="direction" type="Vector2" setter="set_direction" getter="get_direction" default="Vector2(1, 0)"> Unit vector specifying the particles' emission direction. @@ -160,7 +175,7 @@ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. </member> <member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps" default="0"> - The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. + The particle system's frame rate is fixed to a value. For example, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. </member> <member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta" default="true"> If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. @@ -172,12 +187,16 @@ Each particle's hue will vary along this [Curve]. </member> <member name="hue_variation_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum initial hue variation applied to each particle. It will shift the particle color's hue. </member> <member name="hue_variation_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member hue_variation_max]. </member> <member name="initial_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum initial velocity magnitude for each particle. Direction comes from [member direction] and [member spread]. </member> <member name="initial_velocity_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member initial_velocity_max]. </member> <member name="lifetime" type="float" setter="set_lifetime" getter="get_lifetime" default="1.0"> Amount of time each particle will exist. @@ -189,8 +208,10 @@ Each particle's linear acceleration will vary along this [Curve]. </member> <member name="linear_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum linear acceleration applied to each particle in the direction of motion. </member> <member name="linear_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member linear_accel_max]. </member> <member name="local_coords" type="bool" setter="set_use_local_coordinates" getter="get_use_local_coordinates" default="false"> If [code]true[/code], particles use the parent node's coordinate space (known as local coordinates). This will cause particles to move and rotate along the [CPUParticles2D] node (and its parents) when it is moved or rotated. If [code]false[/code], particles use global coordinates; they will not move or rotate along the [CPUParticles2D] node (and its parents) when it is moved or rotated. @@ -202,8 +223,10 @@ Each particle's orbital velocity will vary along this [Curve]. </member> <member name="orbit_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum orbital velocity applied to each particle. Makes the particles circle around origin. Specified in number of full rotations around origin per second. </member> <member name="orbit_velocity_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member orbit_velocity_max]. </member> <member name="particle_flag_align_y" type="bool" setter="set_particle_flag" getter="get_particle_flag" default="false"> Align Y axis of particle with the direction of its velocity. @@ -215,8 +238,10 @@ Each particle's radial acceleration will vary along this [Curve]. </member> <member name="radial_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum radial acceleration applied to each particle. Makes particle accelerate away from the origin or towards it if negative. </member> <member name="radial_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member radial_accel_max]. </member> <member name="randomness" type="float" setter="set_randomness_ratio" getter="get_randomness_ratio" default="0.0"> Emission lifetime randomness ratio. @@ -225,17 +250,24 @@ Each particle's scale will vary along this [Curve]. </member> <member name="scale_amount_max" type="float" setter="set_param_max" getter="get_param_max" default="1.0"> + Maximum initial scale applied to each particle. </member> <member name="scale_amount_min" type="float" setter="set_param_min" getter="get_param_min" default="1.0"> + Minimum equivalent of [member scale_amount_max]. </member> <member name="scale_curve_x" type="Curve" setter="set_scale_curve_x" getter="get_scale_curve_x"> + Each particle's horizontal scale will vary along this [Curve]. + [member split_scale] must be enabled. </member> <member name="scale_curve_y" type="Curve" setter="set_scale_curve_y" getter="get_scale_curve_y"> + Each particle's vertical scale will vary along this [Curve]. + [member split_scale] must be enabled. </member> <member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0"> Particle system's running speed scaling ratio. A value of [code]0[/code] can be used to pause the particles. </member> <member name="split_scale" type="bool" setter="set_split_scale" getter="get_split_scale" default="false"> + If [code]true[/code], the scale curve will be split into x and y components. See [member scale_curve_x] and [member scale_curve_y]. </member> <member name="spread" type="float" setter="set_spread" getter="get_spread" default="45.0"> Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. @@ -244,8 +276,10 @@ Each particle's tangential acceleration will vary along this [Curve]. </member> <member name="tangential_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0"> + Maximum tangential acceleration applied to each particle. Tangential acceleration is perpendicular to the particle's velocity giving the particles a swirling motion. </member> <member name="tangential_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> + Minimum equivalent of [member tangential_accel_max]. </member> <member name="texture" type="Texture2D" setter="set_texture" getter="get_texture"> Particle texture. If [code]null[/code], particles will be squares. diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index 6b39c08b3f..3a15a117f5 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -28,12 +28,14 @@ <return type="float" /> <param index="0" name="param" type="int" enum="CPUParticles3D.Parameter" /> <description> + Returns the maximum value range for the given parameter. </description> </method> <method name="get_param_min" qualifiers="const"> <return type="float" /> <param index="0" name="param" type="int" enum="CPUParticles3D.Parameter" /> <description> + Returns the minimum value range for the given parameter. </description> </method> <method name="get_particle_flag" qualifiers="const"> @@ -62,7 +64,7 @@ <param index="0" name="param" type="int" enum="CPUParticles3D.Parameter" /> <param index="1" name="value" type="float" /> <description> - Sets the maximum value for the given parameter + Sets the maximum value for the given parameter. </description> </method> <method name="set_param_min"> @@ -70,7 +72,7 @@ <param index="0" name="param" type="int" enum="CPUParticles3D.Parameter" /> <param index="1" name="value" type="float" /> <description> - Sets the minimum value for the given parameter + Sets the minimum value for the given parameter. </description> </method> <method name="set_particle_flag"> @@ -187,7 +189,7 @@ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. </member> <member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps" default="0"> - The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the particle system itself. + The particle system's frame rate is fixed to a value. For example, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the particle system itself. </member> <member name="flatness" type="float" setter="set_flatness" getter="get_flatness" default="0.0"> Amount of [member spread] in Y/Z plane. A value of [code]1[/code] restricts particles to X/Z plane. diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 947e6a3d4c..b2d3e5cfdb 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -535,7 +535,7 @@ </method> </methods> <members> - <member name="clip_children" type="bool" setter="set_clip_children" getter="is_clipping_children" default="false"> + <member name="clip_children" type="int" setter="set_clip_children_mode" getter="get_clip_children_mode" enum="CanvasItem.ClipChildrenMode" default="0"> Allows the current node to clip children nodes, essentially acting as a mask. </member> <member name="light_mask" type="int" setter="set_light_mask" getter="get_light_mask" default="1"> @@ -653,5 +653,13 @@ <constant name="TEXTURE_REPEAT_MAX" value="4" enum="TextureRepeat"> Represents the size of the [enum TextureRepeat] enum. </constant> + <constant name="CLIP_CHILDREN_DISABLED" value="0" enum="ClipChildrenMode"> + </constant> + <constant name="CLIP_CHILDREN_ONLY" value="1" enum="ClipChildrenMode"> + </constant> + <constant name="CLIP_CHILDREN_AND_DRAW" value="2" enum="ClipChildrenMode"> + </constant> + <constant name="CLIP_CHILDREN_MAX" value="3" enum="ClipChildrenMode"> + </constant> </constants> </class> diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml index 50c0860d1f..7c1b19b961 100644 --- a/doc/classes/CanvasLayer.xml +++ b/doc/classes/CanvasLayer.xml @@ -5,6 +5,7 @@ </brief_description> <description> Canvas drawing layer. [CanvasItem] nodes that are direct or indirect children of a [CanvasLayer] will be drawn in that layer. The layer is a numeric index that defines the draw order. The default 2D scene renders with index 0, so a [CanvasLayer] with index -1 will be drawn below, and one with index 1 will be drawn above. This is very useful for HUDs (in layer 1+ or above), or backgrounds (in layer -1 or below). + Embedded [Window]s are placed in layer 1024. CanvasItems in layer 1025 or above appear in front of embedded windows, CanvasItems in layer 1023 or below appear behind embedded windows. </description> <tutorials> <link title="Viewport and canvas transforms">$DOCS_URL/tutorials/2d/2d_transforms.html</link> diff --git a/doc/classes/CanvasTexture.xml b/doc/classes/CanvasTexture.xml index ac18c2d474..2dc83790c7 100644 --- a/doc/classes/CanvasTexture.xml +++ b/doc/classes/CanvasTexture.xml @@ -1,25 +1,36 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="CanvasTexture" inherits="Texture2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Texture with optional normal and specular maps for use in 2D rendering. </brief_description> <description> + [CanvasTexture] is an alternative to [ImageTexture] for 2D rendering. It allows using normal maps and specular maps in any node that inherits from [CanvasItem]. [CanvasTexture] also allows overriding the texture's filter and repeat mode independently of the node's properties (or the project settings). + [b]Note:[/b] [CanvasTexture] cannot be used in 3D rendering. For physically-based materials in 3D, use [BaseMaterial3D] instead. </description> <tutorials> </tutorials> <members> <member name="diffuse_texture" type="Texture2D" setter="set_diffuse_texture" getter="get_diffuse_texture"> + The diffuse (color) texture to use. This is the main texture you want to set in most cases. </member> <member name="normal_texture" type="Texture2D" setter="set_normal_texture" getter="get_normal_texture"> + The normal map texture to use. Only has a visible effect if [Light2D]s are affecting this [CanvasTexture]. + [b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines. </member> <member name="specular_color" type="Color" setter="set_specular_color" getter="get_specular_color" default="Color(1, 1, 1, 1)"> + The multiplier for specular reflection colors. The [Light2D]'s color is also taken into account when determining the reflection color. Only has a visible effect if [Light2D]s are affecting this [CanvasTexture]. </member> <member name="specular_shininess" type="float" setter="set_specular_shininess" getter="get_specular_shininess" default="1.0"> + The specular exponent for [Light2D] specular reflections. Higher values result in a more glossy/"wet" look, with reflections becoming more localized and less visible overall. The default value of [code]1.0[/code] disables specular reflections entirely. Only has a visible effect if [Light2D]s are affecting this [CanvasTexture]. </member> <member name="specular_texture" type="Texture2D" setter="set_specular_texture" getter="get_specular_texture"> + The specular map to use for [Light2D] specular reflections. This should be a grayscale or colored texture, with brighter areas resulting in a higher [member specular_shininess] value. Using a colored [member specular_texture] allows controlling specular shininess on a per-channel basis. Only has a visible effect if [Light2D]s are affecting this [CanvasTexture]. </member> <member name="texture_filter" type="int" setter="set_texture_filter" getter="get_texture_filter" enum="CanvasItem.TextureFilter" default="0"> + The texture filtering mode to use when drawing this [CanvasTexture]. </member> <member name="texture_repeat" type="int" setter="set_texture_repeat" getter="get_texture_repeat" enum="CanvasItem.TextureRepeat" default="0"> + The texture repeat mode to use when drawing this [CanvasTexture]. </member> </members> </class> diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml index 2ffe880971..699e872e99 100644 --- a/doc/classes/CheckBox.xml +++ b/doc/classes/CheckBox.xml @@ -4,7 +4,7 @@ Binary choice user interface widget. See also [CheckButton]. </brief_description> <description> - A checkbox allows the user to make a binary choice (choosing only one of two possible options). It's similar to [CheckButton] in functionality, but it has a different appearance. To follow established UX patterns, it's recommended to use CheckBox when toggling it has [b]no[/b] immediate effect on something. For instance, it should be used when toggling it will only do something once a confirmation button is pressed. + A checkbox allows the user to make a binary choice (choosing only one of two possible options). It's similar to [CheckButton] in functionality, but it has a different appearance. To follow established UX patterns, it's recommended to use CheckBox when toggling it has [b]no[/b] immediate effect on something. For example, it could be used when toggling it will only do something once a confirmation button is pressed. See also [BaseButton] which contains common properties and methods associated with this node. </description> <tutorials> diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index 2f584103be..dfbaa7cbee 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -4,7 +4,7 @@ Checkable button. See also [CheckBox]. </brief_description> <description> - CheckButton is a toggle button displayed as a check field. It's similar to [CheckBox] in functionality, but it has a different appearance. To follow established UX patterns, it's recommended to use CheckButton when toggling it has an [b]immediate[/b] effect on something. For instance, it should be used if toggling it enables/disables a setting without requiring the user to press a confirmation button. + CheckButton is a toggle button displayed as a check field. It's similar to [CheckBox] in functionality, but it has a different appearance. To follow established UX patterns, it's recommended to use CheckButton when toggling it has an [b]immediate[/b] effect on something. For example, it could be used if toggling it enables/disables a setting without requiring the user to press a confirmation button. See also [BaseButton] which contains common properties and methods associated with this node. </description> <tutorials> diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml index 58a536406f..5d3c6210f6 100644 --- a/doc/classes/ClassDB.xml +++ b/doc/classes/ClassDB.xml @@ -13,7 +13,7 @@ <return type="bool" /> <param index="0" name="class" type="StringName" /> <description> - Returns [code]true[/code] if you can instance objects from the specified [param class], [code]false[/code] in other case. + Returns [code]true[/code] if objects can be instantiated from the specified [param class], otherwise returns [code]false[/code]. </description> </method> <method name="class_exists" qualifiers="const"> diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml index 3a3803c1da..a11d7157f1 100644 --- a/doc/classes/Color.xml +++ b/doc/classes/Color.xml @@ -145,6 +145,11 @@ <return type="int" /> <param index="0" name="name" type="String" /> <description> + Returns the index of a named color. Use [method get_named_color] to get the actual color. + [codeblock] + var idx = Color.find_named_color("khaki") + modulate = Color.get_named_color(idx) + [/codeblock] </description> </method> <method name="from_hsv" qualifiers="static"> @@ -187,6 +192,7 @@ <return type="Color" /> <param index="0" name="rgbe" type="int" /> <description> + Encodes a [Color] from a RGBE9995 format integer. See [constant Image.FORMAT_RGBE9995]. </description> </method> <method name="from_string" qualifiers="static"> @@ -194,6 +200,7 @@ <param index="0" name="str" type="String" /> <param index="1" name="default" type="Color" /> <description> + Creates a [Color] from string, which can be either a HTML color code or a named color. Fallbacks to [param default] if the string does not denote any valid color. </description> </method> <method name="get_luminance" qualifiers="const"> @@ -208,29 +215,37 @@ <return type="Color" /> <param index="0" name="idx" type="int" /> <description> + Returns a named color with the given index. You can get the index from [method find_named_color] or iteratively from [method get_named_color_count]. </description> </method> <method name="get_named_color_count" qualifiers="static"> <return type="int" /> <description> + Returns the number of available named colors. </description> </method> <method name="get_named_color_name" qualifiers="static"> <return type="String" /> <param index="0" name="idx" type="int" /> <description> + Returns the name of a color with the given index. You can get the index from [method find_named_color] or iteratively from [method get_named_color_count]. </description> </method> <method name="hex" qualifiers="static"> <return type="Color" /> <param index="0" name="hex" type="int" /> <description> + Returns the [Color] associated with the provided integer number, with 8 bits per channel in ARGB order. The integer should be 32-bit. Best used with hexadecimal notation. + [codeblock] + modulate = Color.hex(0xffff0000) # red + [/codeblock] </description> </method> <method name="hex64" qualifiers="static"> <return type="Color" /> <param index="0" name="hex" type="int" /> <description> + Same as [method hex], but takes 64-bit integer and the color uses 16 bits per channel. </description> </method> <method name="html" qualifiers="static"> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index d09b7f003e..cd1d0b016d 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -131,6 +131,7 @@ The width of the hue selection slider. </theme_item> <theme_item name="label_width" data_type="constant" type="int" default="10"> + The minimum width of the color labels next to sliders. </theme_item> <theme_item name="margin" data_type="constant" type="int" default="4"> The margin around the [ColorPicker]. @@ -160,8 +161,10 @@ The indicator used to signalize that the color value is outside the 0-1 range. </theme_item> <theme_item name="picker_cursor" data_type="icon" type="Texture2D"> + The image displayed over the color box/circle (depending on the [member picker_shape]), marking the currently selected color. </theme_item> <theme_item name="sample_bg" data_type="icon" type="Texture2D"> + Background panel for the color preview box (visible when the color is translucent). </theme_item> <theme_item name="screen_picker" data_type="icon" type="Texture2D"> The icon for the screen color picker button. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 7968b03c4b..91e9b65a8a 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -383,7 +383,7 @@ <method name="get_global_rect" qualifiers="const"> <return type="Rect2" /> <description> - Returns the position and size of the control relative to the top-left corner of the screen. See [member position] and [member size]. + Returns the position and size of the control relative to the [CanvasLayer]. See [member global_position] and [member size]. </description> </method> <method name="get_minimum_size" qualifiers="const"> @@ -972,7 +972,7 @@ <member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" default="false"> Enables whether rendering of [CanvasItem] based children should be clipped to this control's rectangle. If [code]true[/code], parts of a child which would be visibly outside of this control's rectangle will not be rendered and won't receive input. </member> - <member name="custom_minimum_size" type="Vector2i" setter="set_custom_minimum_size" getter="get_custom_minimum_size" default="Vector2i(0, 0)"> + <member name="custom_minimum_size" type="Vector2" setter="set_custom_minimum_size" getter="get_custom_minimum_size" default="Vector2(0, 0)"> The minimum size of the node's bounding rectangle. If you set it to a value greater than (0, 0), the node's bounding rectangle will always have at least this size, even if its content is smaller. If it's set to (0, 0), the node sizes automatically to fit its content, be it a texture or child nodes. </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode" default="0"> @@ -999,7 +999,7 @@ If this property is not set, Godot will select a "best guess" based on surrounding nodes in the scene tree. </member> <member name="global_position" type="Vector2" setter="_set_global_position" getter="get_global_position"> - The node's global position, relative to the world (usually to the top-left corner of the window). + The node's global position, relative to the world (usually to the [CanvasLayer]). </member> <member name="grow_horizontal" type="int" setter="set_h_grow_direction" getter="get_h_grow_direction" enum="Control.GrowDirection" default="1"> Controls the direction on the horizontal axis in which the control should grow if its horizontal minimum size is changed to be greater than its current size, as the control always has to be at least the minimum size. diff --git a/doc/classes/CylinderShape3D.xml b/doc/classes/CylinderShape3D.xml index 6ff142da59..dcb14ec945 100644 --- a/doc/classes/CylinderShape3D.xml +++ b/doc/classes/CylinderShape3D.xml @@ -5,6 +5,7 @@ </brief_description> <description> Cylinder shape for collisions. Like [CapsuleShape3D], but without hemispheres at the cylinder's ends. + [b]Note:[/b] There are several known bugs with cylinder collision shapes. Using [CapsuleShape3D] or [BoxShape3D] instead is recommended. [b]Performance:[/b] Being a primitive collision shape, [CylinderShape3D] is fast to check collisions against (though not as fast as [SphereShape3D]). [CylinderShape3D] is also more demanding compared to [CapsuleShape3D]. </description> <tutorials> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index a86aff060d..ac1f63af56 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -325,18 +325,21 @@ <return type="bool" /> <param index="0" name="right" type="Dictionary" /> <description> + Returns [code]true[/code] if the dictionaries differ, i.e. their key or value lists are different (including the order). </description> </operator> <operator name="operator =="> <return type="bool" /> <param index="0" name="right" type="Dictionary" /> <description> + Returns [code]true[/code] if both dictionaries have the same contents, i.e. their keys list and value list are equal. </description> </operator> <operator name="operator []"> <return type="Variant" /> <param index="0" name="key" type="Variant" /> <description> + Retunrs a value at the given [param key] or [code]null[/code] and error if the key does not exist. For safe access, use [method get] or [method has]. </description> </operator> </operators> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index 85d9fd34ca..de1a92949f 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1,8 +1,11 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="DisplayServer" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Singleton for window management functions. </brief_description> <description> + [DisplayServer] handles everything related to window management. This is separated from [OS] as a single operating system may support multiple display servers. + [b]Headless mode:[/b] Starting the engine with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] disables all rendering and window management functions. Most functions from [DisplayServer] will return dummy values in this case. </description> <tutorials> </tutorials> @@ -16,8 +19,8 @@ <method name="clipboard_get_primary" qualifiers="const"> <return type="String" /> <description> - Returns the user's primary clipboard as a string if possible. - [b]Note:[/b] This method is only implemented on Linux. + Returns the user's [url=https://unix.stackexchange.com/questions/139191/whats-the-difference-between-primary-selection-and-clipboard-buffer]primary[/url] clipboard as a string if possible. This is the clipboard that is set when the user selects text in any application, rather than when pressing [kbd]Ctrl + C[/kbd]. The clipboard data can then be pasted by clicking the middle mouse button in any application that supports the primary clipboard mechanism. + [b]Note:[/b] This method is only implemented on Linux (X11). </description> </method> <method name="clipboard_has" qualifiers="const"> @@ -37,8 +40,8 @@ <return type="void" /> <param index="0" name="clipboard_primary" type="String" /> <description> - Sets the user's primary clipboard content to the given string. - [b]Note:[/b] This method is only implemented on Linux. + Sets the user's [url=https://unix.stackexchange.com/questions/139191/whats-the-difference-between-primary-selection-and-clipboard-buffer]primary[/url] clipboard content to the given string. This is the clipboard that is set when the user selects text in any application, rather than when pressing [kbd]Ctrl + C[/kbd]. The clipboard data can then be pasted by clicking the middle mouse button in any application that supports the primary clipboard mechanism. + [b]Note:[/b] This method is only implemented on Linux (X11). </description> </method> <method name="create_sub_window"> @@ -48,11 +51,13 @@ <param index="2" name="flags" type="int" /> <param index="3" name="rect" type="Rect2i" default="Rect2i(0, 0, 0, 0)" /> <description> + Create a new subwindow (a [Window] whose presence is linked to another window). This window will appear to be embedded within the main window if [member Viewport.gui_embed_subwindows] is [code]true[/code]. Otherwise, the window will appear to be a separate window which can be dragged outside the main window. [code]vsync_mode[/code] is ignored if [member Viewport.gui_embed_subwindows] is [code]true[/code], as the subwindow will be drawn at the same time as the main window in this case. </description> </method> <method name="cursor_get_shape" qualifiers="const"> <return type="int" enum="DisplayServer.CursorShape" /> <description> + Returns the default mouse cursor shape set by [method cursor_set_shape]. </description> </method> <method name="cursor_set_custom_image"> @@ -61,18 +66,21 @@ <param index="1" name="shape" type="int" enum="DisplayServer.CursorShape" default="0" /> <param index="2" name="hotspot" type="Vector2" default="Vector2(0, 0)" /> <description> + Sets a custom mouse cursor image for the defined [param shape]. This means the user's operating system and mouse cursor theme will no longer influence the mouse cursor's appearance. The image must be [code]256x256[/code] or smaller for correct appearance. [param hotspot] can optionally be set to define the area where the cursor will click. By default, [param hotspot] is set to [code]Vector2(0, 0)[/code], which is the top-left corner of the image. See also [method cursor_set_shape]. </description> </method> <method name="cursor_set_shape"> <return type="void" /> <param index="0" name="shape" type="int" enum="DisplayServer.CursorShape" /> <description> + Sets the default mouse cursor shape. The cursor's appearance will vary depending on the user's operating system and mouse cursor theme. See also [method cursor_get_shape] and [method cursor_set_custom_image]. </description> </method> <method name="delete_sub_window"> <return type="void" /> <param index="0" name="window_id" type="int" /> <description> + Removes the subwindow specified by [param window_id]. </description> </method> <method name="dialog_input_text"> @@ -82,6 +90,8 @@ <param index="2" name="existing_text" type="String" /> <param index="3" name="callback" type="Callable" /> <description> + Shows a text input dialog which uses the operating system's native look-and-feel. [param callback] will be called with a [String] argument equal to the text field's contents when the dialog is closed for any reason. + [b]Note:[/b] This method is implemented on macOS. </description> </method> <method name="dialog_show"> @@ -91,17 +101,23 @@ <param index="2" name="buttons" type="PackedStringArray" /> <param index="3" name="callback" type="Callable" /> <description> + Shows a text dialog which uses the operating system's native look-and-feel. [param callback] will be called when the dialog is closed for any reason. + [b]Note:[/b] This method is implemented on macOS. </description> </method> <method name="enable_for_stealing_focus"> <return type="void" /> <param index="0" name="process_id" type="int" /> <description> + Allows the [param process_id] PID to steal focus from this window. In other words, this disables the operating system's focus stealing protection for the specified PID. + [b]Note:[/b] This method is implemented on Windows. </description> </method> <method name="force_process_and_drop_events"> <return type="void" /> <description> + Forces window manager processing while ignoring all [InputEvent]s. See also [method process_events]. + [b]Note:[/b] This method is implemented on Windows and macOS. </description> </method> <method name="get_accent_color" qualifiers="const"> @@ -127,27 +143,43 @@ <method name="get_name" qualifiers="const"> <return type="String" /> <description> + Returns the name of the [DisplayServer] currently in use. Most operating systems only have a single [DisplayServer], but Linux has access to more than one [DisplayServer] (although only X11 is currently implemented in Godot). + The names of built-in display servers are [code]Windows[/code], [code]macOS[/code], [code]X11[/code] (Linux), [code]Android[/code], [code]iOS[/code], [code]web[/code] (HTML5) and [code]headless[/code] (when started with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]). </description> </method> <method name="get_screen_count" qualifiers="const"> <return type="int" /> <description> + Returns the number of displays available. </description> </method> <method name="get_swap_cancel_ok"> <return type="bool" /> <description> + Returns [code]true[/code] if positions of [b]OK[/b] and [b]Cancel[/b] buttons are swapped in dialogs. This is enabled by default on Windows and UWP to follow interface conventions, and be toggled by changing [member ProjectSettings.gui/common/swap_cancel_ok]. + [b]Note:[/b] This doesn't affect native dialogs such as the ones spawned by [method DisplayServer.dialog_show]. </description> </method> <method name="get_window_at_screen_position" qualifiers="const"> <return type="int" /> <param index="0" name="position" type="Vector2i" /> <description> + Returns the ID of the window at the specified screen [param position] (in pixels). On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this: + [codeblock] + * (0, 0) +-------+ + | | + +-------------+ | | + | | | | + | | | | + +-------------+ +-------+ + [/codeblock] </description> </method> <method name="get_window_list" qualifiers="const"> <return type="PackedInt32Array" /> <description> + Returns the list of Godot window IDs belonging to this process. + [b]Note:[/b] Native dialogs are not included in this list. </description> </method> <method name="global_menu_add_check_item"> @@ -665,37 +697,42 @@ <return type="bool" /> <param index="0" name="feature" type="int" enum="DisplayServer.Feature" /> <description> + Returns [code]true[/code] if the specified [param feature] is supported by the current [DisplayServer], [code]false[/code] otherwise. </description> </method> <method name="ime_get_selection" qualifiers="const"> <return type="Vector2i" /> <description> + Returns the text selection in the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] composition string, with the [Vector2i]'s [code]x[/code] component being the caret position and [code]y[/code] being the length of the selection. + [b]Note:[/b] This method is implemented on macOS. </description> </method> <method name="ime_get_text" qualifiers="const"> <return type="String" /> <description> + Returns the composition string contained within the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] window. + [b]Note:[/b] This method is implemented on macOS. </description> </method> <method name="is_dark_mode" qualifiers="const"> <return type="bool" /> <description> Returns [code]true[/code] if OS is using dark mode. - [b]Note:[/b] This method is implemented on macOS, Windows and Linux. + [b]Note:[/b] This method is implemented on macOS, Windows and Linux (X11). </description> </method> <method name="is_dark_mode_supported" qualifiers="const"> <return type="bool" /> <description> Returns [code]true[/code] if OS supports dark mode. - [b]Note:[/b] This method is implemented on macOS, Windows and Linux. + [b]Note:[/b] This method is implemented on macOS, Windows and Linux (X11). </description> </method> <method name="keyboard_get_current_layout" qualifiers="const"> <return type="int" /> <description> Returns active keyboard layout index. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="keyboard_get_keycode_from_physical" qualifiers="const"> @@ -703,14 +740,14 @@ <param index="0" name="keycode" type="int" enum="Key" /> <description> Converts a physical (US QWERTY) [param keycode] to one in the active keyboard layout. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="keyboard_get_layout_count" qualifiers="const"> <return type="int" /> <description> Returns the number of keyboard layouts. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="keyboard_get_layout_language" qualifiers="const"> @@ -718,7 +755,7 @@ <param index="0" name="index" type="int" /> <description> Returns the ISO-639/BCP-47 language code of the keyboard layout at position [param index]. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="keyboard_get_layout_name" qualifiers="const"> @@ -726,25 +763,27 @@ <param index="0" name="index" type="int" /> <description> Returns the localized name of the keyboard layout at position [param index]. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="keyboard_set_current_layout"> <return type="void" /> <param index="0" name="index" type="int" /> <description> - Sets active keyboard layout. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + Sets the active keyboard layout. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="mouse_get_button_state" qualifiers="const"> <return type="int" enum="MouseButton" /> <description> + Returns the current state of mouse buttons (whether each button is pressed) as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method Input.get_mouse_button_mask]. </description> </method> <method name="mouse_get_mode" qualifiers="const"> <return type="int" enum="DisplayServer.MouseMode" /> <description> + Returns the current mouse mode. See also [method mouse_set_mode]. </description> </method> <method name="mouse_get_position" qualifiers="const"> @@ -757,11 +796,13 @@ <return type="void" /> <param index="0" name="mouse_mode" type="int" enum="DisplayServer.MouseMode" /> <description> + Sets the current mouse mode. See also [method mouse_get_mode]. </description> </method> <method name="process_events"> <return type="void" /> <description> + Perform window manager processing, including input flushing. See also [method force_process_and_drop_events], [method Input.flush_buffered_events] and [member Input.use_accumulated_input]. </description> </method> <method name="screen_get_dpi" qualifiers="const"> @@ -779,7 +820,7 @@ xxhdpi - 480 dpi xxxhdpi - 640 dpi [/codeblock] - [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows. Returns [code]72[/code] on unsupported platforms. + [b]Note:[/b] This method is implemented on Android, Linux (X11), macOS and Windows. Returns [code]72[/code] on unsupported platforms. </description> </method> <method name="screen_get_max_scale" qualifiers="const"> @@ -794,12 +835,24 @@ <return type="int" enum="DisplayServer.ScreenOrientation" /> <param index="0" name="screen" type="int" default="-1" /> <description> + Returns the [param screen]'s current orientation. See also [method screen_set_orientation]. + [b]Note:[/b] This method is implemented on Android and iOS. </description> </method> <method name="screen_get_position" qualifiers="const"> <return type="Vector2i" /> <param index="0" name="screen" type="int" default="-1" /> <description> + Returns the screen's top-left corner position in pixels. On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this: + [codeblock] + * (0, 0) +-------+ + | | + +-------------+ | | + | | | | + | | | | + +-------------+ +-------+ + [/codeblock] + See also [method screen_get_size]. </description> </method> <method name="screen_get_refresh_rate" qualifiers="const"> @@ -829,29 +882,34 @@ <return type="Vector2i" /> <param index="0" name="screen" type="int" default="-1" /> <description> + Returns the screen's size in pixels. See also [method screen_get_position] and [method screen_get_usable_rect]. </description> </method> <method name="screen_get_usable_rect" qualifiers="const"> <return type="Rect2i" /> <param index="0" name="screen" type="int" default="-1" /> <description> + Returns the portion of the screen that is not obstructed by a status bar in pixels. See also [method screen_get_size]. </description> </method> <method name="screen_is_kept_on" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if the screen should never be turned off by the operating system's power-saving measures. See also [method screen_set_keep_on]. </description> </method> <method name="screen_is_touchscreen" qualifiers="const"> <return type="bool" /> <param index="0" name="screen" type="int" default="-1" /> <description> + Returns [code]true[/code] if the screen can send touch events or if [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse] is [code]true[/code]. </description> </method> <method name="screen_set_keep_on"> <return type="void" /> <param index="0" name="enable" type="bool" /> <description> + Sets whether the screen should never be turned off by the operating system's power-saving measures. See also [method screen_is_kept_on]. </description> </method> <method name="screen_set_orientation"> @@ -859,18 +917,21 @@ <param index="0" name="orientation" type="int" enum="DisplayServer.ScreenOrientation" /> <param index="1" name="screen" type="int" default="-1" /> <description> + Sets the [param screen]'s [param orientation]. See also [method screen_get_orientation]. </description> </method> <method name="set_icon"> <return type="void" /> <param index="0" name="image" type="Image" /> <description> + Sets the window icon (usually displayed in the top-left corner) in the operating system's [i]native[/i] format. To use icons in the operating system's native format, use [method set_native_icon] instead. </description> </method> <method name="set_native_icon"> <return type="void" /> <param index="0" name="filename" type="String" /> <description> + Sets the window icon (usually displayed in the top-left corner) in the operating system's [i]native[/i] format. The file at [param filename] must be in [code].ico[/code] format on Windows or [code].icns[/code] on macOS. By using specially crafted [code].ico[/code] or [code].icns[/code] icons, [method set_native_icon] allows specifying different icons depending on the size the icon is displayed at. This size is determined by the operating system and user preferences (including the display scale factor). To use icons in other formats, use [method set_icon] instead. </description> </method> <method name="tablet_get_current_driver" qualifiers="const"> @@ -911,7 +972,7 @@ - [code]name[/code] is voice name. - [code]id[/code] is voice identifier. - [code]language[/code] is language code in [code]lang_Variant[/code] format. [code]lang[/code] part is a 2 or 3-letter code based on the ISO-639 standard, in lowercase. And [code]Variant[/code] part is an engine dependent string describing country, region or/and dialect. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_get_voices_for_language" qualifiers="const"> @@ -919,35 +980,35 @@ <param index="0" name="language" type="String" /> <description> Returns an [PackedStringArray] of voice identifiers for the [param language]. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_is_paused" qualifiers="const"> <return type="bool" /> <description> Returns [code]true[/code] if the synthesizer is in a paused state. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_is_speaking" qualifiers="const"> <return type="bool" /> <description> Returns [code]true[/code] if the synthesizer is generating speech, or have utterance waiting in the queue. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_pause"> <return type="void" /> <description> Puts the synthesizer into a paused state. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_resume"> <return type="void" /> <description> Resumes the synthesizer if it was paused. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_set_utterance_callback"> @@ -959,7 +1020,7 @@ - [code]TTS_UTTERANCE_STARTED[/code], [code]TTS_UTTERANCE_ENDED[/code], and [code]TTS_UTTERANCE_CANCELED[/code] callable's method should take one [int] parameter, the utterance id. - [code]TTS_UTTERANCE_BOUNDARY[/code] callable's method should take two [int] parameters, the index of the character and the utterance id. [b]Note:[/b] The granularity of the boundary callbacks is engine dependent. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_speak"> @@ -978,16 +1039,16 @@ - [param pitch] ranges from [code]0.0[/code] (lowest) to [code]2.0[/code] (highest), [code]1.0[/code] is default pitch for the current voice. - [param rate] ranges from [code]0.1[/code] (lowest) to [code]10.0[/code] (highest), [code]1.0[/code] is a normal speaking rate. Other values act as a percentage relative. - [param utterance_id] is passed as a parameter to the callback functions. - [b]Note:[/b] On Windows and Linux, utterance [param text] can use SSML markup. SSML support is engine and voice dependent. If the engine does not support SSML, you should strip out all XML markup before calling [method tts_speak]. + [b]Note:[/b] On Windows and Linux (X11), utterance [param text] can use SSML markup. SSML support is engine and voice dependent. If the engine does not support SSML, you should strip out all XML markup before calling [method tts_speak]. [b]Note:[/b] The granularity of pitch, rate, and volume is engine and voice dependent. Values may be truncated. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="tts_stop"> <return type="void" /> <description> Stops synthesis in progress and removes all utterances from the queue. - [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS, and Windows. + [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux (X11), macOS, and Windows. </description> </method> <method name="virtual_keyboard_get_height" qualifiers="const"> @@ -1033,12 +1094,14 @@ <param index="0" name="instance_id" type="int" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [Window] instance ID ([method Object.get_instance_id]) the [param window_id] should be attached to. See also [method window_get_attached_instance_id]. </description> </method> <method name="window_can_draw" qualifiers="const"> <return type="bool" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns [code]true[/code] if anything can be drawn in the window specified by [param window_id], [code]false[/code] otherwise. Using the [code]--disable-render-loop[/code] command line argument or a headless build will return [code]false[/code]. </description> </method> <method name="window_get_active_popup" qualifiers="const"> @@ -1051,12 +1114,14 @@ <return type="int" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the [method Object.get_instance_id] of the [Window] the [param window_id] is attached to. also [method window_get_attached_instance_id]. </description> </method> <method name="window_get_current_screen" qualifiers="const"> <return type="int" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the screen the window specified by [param window_id] is currently positioned on. If the screen overlaps multiple displays, the screen where the window's center is located is returned. See also [method window_set_current_screen]. </description> </method> <method name="window_get_flag" qualifiers="const"> @@ -1071,12 +1136,14 @@ <return type="Vector2i" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the window's maximum size (in pixels). See also [method window_set_max_size]. </description> </method> <method name="window_get_min_size" qualifiers="const"> <return type="Vector2i" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the window's minimum size (in pixels). See also [method window_set_min_size]. </description> </method> <method name="window_get_mode" qualifiers="const"> @@ -1092,7 +1159,7 @@ <param index="1" name="window_id" type="int" default="0" /> <description> Returns internal structure pointers for use in plugins. - [b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows. + [b]Note:[/b] This method is implemented on Android, Linux (X11), macOS and Windows. </description> </method> <method name="window_get_popup_safe_rect" qualifiers="const"> @@ -1113,6 +1180,7 @@ <return type="Vector2i" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the size of the window specified by [param window_id] (in pixels), including the borders drawn by the operating system. See also [method window_get_size]. </description> </method> <method name="window_get_safe_title_margins" qualifiers="const"> @@ -1126,6 +1194,7 @@ <return type="Vector2i" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Returns the size of the window specified by [param window_id] (in pixels), excluding the borders drawn by the operating system. This is also called the "client area". See also [method window_get_real_size], [method window_set_size] and [method window_get_position]. </description> </method> <method name="window_get_vsync_mode" qualifiers="const"> @@ -1153,12 +1222,14 @@ <return type="void" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Moves the window specified by [param window_id] to the foreground, so that it is visible over other windows. </description> </method> <method name="window_request_attention"> <return type="void" /> <param index="0" name="window_id" type="int" default="0" /> <description> + Makes the window specified by [param window_id] request attention, which is materialized by the window title and taskbar entry blinking until the window is focused. This usually has no visible effect if the window is currently focused. The exact behavior varies depending on the operating system. </description> </method> <method name="window_set_current_screen"> @@ -1166,6 +1237,7 @@ <param index="0" name="screen" type="int" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Moves the window specified by [param window_id] to the specified [param screen]. See also [method window_get_current_screen]. </description> </method> <method name="window_set_drop_files_callback"> @@ -1173,6 +1245,8 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [param callback] that should be called when files are dropped from the operating system's file manager to the window specified by [param window_id]. + [b]Note:[/b] This method is implemented on Windows, macOS, Linux (X11) and Web. </description> </method> <method name="window_set_exclusive"> @@ -1199,6 +1273,7 @@ <param index="0" name="active" type="bool" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets whether [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] should be enabled for the window specified by [param window_id]. See also [method window_set_ime_position]. </description> </method> <method name="window_set_ime_position"> @@ -1206,6 +1281,7 @@ <param index="0" name="position" type="Vector2i" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the position of the [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url] popup for the specified [param window_id]. Only effective if [method window_set_ime_active] was set to [code]true[/code] for the specified [param window_id]. </description> </method> <method name="window_set_input_event_callback"> @@ -1213,6 +1289,7 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [param callback] that should be called when any [InputEvent] is sent to the window specified by [param window_id]. </description> </method> <method name="window_set_input_text_callback"> @@ -1220,6 +1297,7 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [param callback] that should be called when text is entered using the virtual keyboard to the window specified by [param window_id]. </description> </method> <method name="window_set_max_size"> @@ -1227,6 +1305,8 @@ <param index="0" name="max_size" type="Vector2i" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the maximum size of the window specified by [param window_id] in pixels. Normally, the user will not be able to drag the window to make it smaller than the specified size. See also [method window_get_max_size]. + [b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit. </description> </method> <method name="window_set_min_size"> @@ -1234,8 +1314,9 @@ <param index="0" name="min_size" type="Vector2i" /> <param index="1" name="window_id" type="int" default="0" /> <description> - Sets the minimum size for the given window to [param min_size] (in pixels). + Sets the minimum size for the given window to [param min_size] (in pixels). Normally, the user will not be able to drag the window to make it larger than the specified size. See also [method window_get_min_size]. [b]Note:[/b] By default, the main window has a minimum size of [code]Vector2i(64, 64)[/code]. This prevents issues that can arise when the window is resized to a near-zero size. + [b]Note:[/b] Using third-party tools, it is possible for users to disable window geometry restrictions and therefore bypass this limit. </description> </method> <method name="window_set_mode"> @@ -1276,8 +1357,8 @@ DisplayServer.WindowSetMousePassthrough(new Vector2[] {}); [/csharp] [/codeblocks] - [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux and macOS it is. - [b]Note:[/b] This method is implemented on Linux, macOS and Windows. + [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux (X11) and macOS it is. + [b]Note:[/b] This method is implemented on Linux (X11), macOS and Windows. </description> </method> <method name="window_set_popup_safe_rect"> @@ -1293,7 +1374,16 @@ <param index="0" name="position" type="Vector2i" /> <param index="1" name="window_id" type="int" default="0" /> <description> - Sets the position of the given window to [param position]. + Sets the position of the given window to [param position]. On multi-monitor setups, the screen position is relative to the virtual desktop area. On multi-monitor setups with different screen resolutions or orientations, the origin may be located outside any display like this: + [codeblock] + * (0, 0) +-------+ + | | + +-------------+ | | + | | | | + | | | | + +-------------+ +-------+ + [/codeblock] + See also [method window_get_position] and [method window_set_size]. </description> </method> <method name="window_set_rect_changed_callback"> @@ -1301,6 +1391,7 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [param callback] that will be called when the window specified by [param window_id] is moved or resized. </description> </method> <method name="window_set_size"> @@ -1308,7 +1399,7 @@ <param index="0" name="size" type="Vector2i" /> <param index="1" name="window_id" type="int" default="0" /> <description> - Sets the size of the given window to [param size]. + Sets the size of the given window to [param size] (in pixels). See also [method window_get_size] and [method window_get_position]. </description> </method> <method name="window_set_title"> @@ -1317,6 +1408,7 @@ <param index="1" name="window_id" type="int" default="0" /> <description> Sets the title of the given window to [param title]. + [b]Note:[/b] Avoid changing the window title every frame, as this can cause performance issues on certain window managers. Try to change the window title only a few times per second at most. </description> </method> <method name="window_set_transient"> @@ -1352,51 +1444,70 @@ <param index="0" name="callback" type="Callable" /> <param index="1" name="window_id" type="int" default="0" /> <description> + Sets the [param callback] that will be called when an event occurs in the window specified by [param window_id]. </description> </method> </methods> <constants> <constant name="FEATURE_GLOBAL_MENU" value="0" enum="Feature"> + Display server supports global menu. This allows the application to display its menu items in the operating system's top bar. [b]macOS[/b] </constant> <constant name="FEATURE_SUBWINDOWS" value="1" enum="Feature"> + Display server supports multiple windows that can be moved outside of the main window. [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_TOUCHSCREEN" value="2" enum="Feature"> + Display server supports touchscreen input. [b]Windows, Linux (X11), Android, iOS, Web[/b] </constant> <constant name="FEATURE_MOUSE" value="3" enum="Feature"> + Display server supports mouse input. [b]Windows, macOS, Linux (X11), Android, Web[/b] </constant> <constant name="FEATURE_MOUSE_WARP" value="4" enum="Feature"> + Display server supports warping mouse coordinates to keep the mouse cursor constrained within an area, but looping when one of the edges is reached. [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_CLIPBOARD" value="5" enum="Feature"> + Display server supports setting and getting clipboard data. See also [constant FEATURE_CLIPBOARD_PRIMARY]. [b]Windows, macOS, Linux (X11), Android, iOS, Web[/b] </constant> <constant name="FEATURE_VIRTUAL_KEYBOARD" value="6" enum="Feature"> + Display server supports popping up a virtual keyboard when requested to input text without a physical keyboard. [b]Android, iOS, Web[/b] </constant> <constant name="FEATURE_CURSOR_SHAPE" value="7" enum="Feature"> + Display server supports setting the mouse cursor shape to be different from the default. [b]Windows, macOS, Linux (X11), Android, Web[/b] </constant> <constant name="FEATURE_CUSTOM_CURSOR_SHAPE" value="8" enum="Feature"> + Display server supports setting the mouse cursor shape to a custom image. [b]Windows, macOS, Linux (X11), Web[/b] </constant> <constant name="FEATURE_NATIVE_DIALOG" value="9" enum="Feature"> + Display server supports spawning dialogs using the operating system's native look-and-feel. [b]macOS[/b] </constant> <constant name="FEATURE_IME" value="10" enum="Feature"> + Display server supports [url=https://en.wikipedia.org/wiki/Input_method]Input Method Editor[/url], which is commonly used for inputting Chinese/Japanese/Korean text. This is handled by the operating system, rather than by Godot. [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_WINDOW_TRANSPARENCY" value="11" enum="Feature"> + Display server supports windows can use per-pixel transparency to make windows behind them partially or fully visible. [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_HIDPI" value="12" enum="Feature"> + Display server supports querying the operating system's display scale factor. This allows for [i]reliable[/i] automatic hiDPI display detection, as opposed to guessing based on the screen resolution and reported display DPI (which can be unreliable due to broken monitor EDID). [b]Windows, macOS[/b] </constant> <constant name="FEATURE_ICON" value="13" enum="Feature"> + Display server supports changing the window icon (usually displayed in the top-left corner). [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_NATIVE_ICON" value="14" enum="Feature"> + Display server supports changing the window icon (usually displayed in the top-left corner). [b]Windows, macOS[/b] </constant> <constant name="FEATURE_ORIENTATION" value="15" enum="Feature"> + Display server supports changing the screen orientation. [b]Android, iOS[/b] </constant> <constant name="FEATURE_SWAP_BUFFERS" value="16" enum="Feature"> + Display server supports V-Sync status can be changed from the default (which is forced to be enabled platforms not supporting this feature). [b]Windows, macOS, Linux (X11)[/b] </constant> <constant name="FEATURE_CLIPBOARD_PRIMARY" value="18" enum="Feature"> + Display server supports Primary clipboard can be used. This is a different clipboard from [constant FEATURE_CLIPBOARD]. [b]Linux (X11)[/b] </constant> <constant name="FEATURE_TEXT_TO_SPEECH" value="19" enum="Feature"> - Display server supports text-to-speech. See [code]tts_*[/code] methods. + Display server supports text-to-speech. See [code]tts_*[/code] methods. [b]Windows, macOS, Linux (X11), Android, iOS, Web[/b] </constant> <constant name="FEATURE_EXTEND_TO_TITLE" value="20" enum="Feature"> - Display server supports expanding window content to the title. See [constant WINDOW_FLAG_EXTEND_TO_TITLE]. + Display server supports expanding window content to the title. See [constant WINDOW_FLAG_EXTEND_TO_TITLE]. [b]macOS[/b] </constant> <constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode"> Makes the mouse cursor visible if it is hidden. @@ -1415,24 +1526,34 @@ Confines the mouse cursor to the game window, and make it hidden. </constant> <constant name="SCREEN_OF_MAIN_WINDOW" value="-1"> + Represents the screen where the main window is located. This is usually the default value in functions that allow specifying one of several screens. </constant> <constant name="MAIN_WINDOW_ID" value="0"> + The ID of the main window spawned by the engine, which can be passed to methods expecting a [code]window_id[/code]. </constant> <constant name="INVALID_WINDOW_ID" value="-1"> + The ID that refers to a nonexisting window. This is be returned by some [DisplayServer] methods if no window matches the requested result. </constant> <constant name="SCREEN_LANDSCAPE" value="0" enum="ScreenOrientation"> + Default landscape orientation. </constant> <constant name="SCREEN_PORTRAIT" value="1" enum="ScreenOrientation"> + Default portrait orienstation. </constant> <constant name="SCREEN_REVERSE_LANDSCAPE" value="2" enum="ScreenOrientation"> + Reverse landscape orientation (upside down). </constant> <constant name="SCREEN_REVERSE_PORTRAIT" value="3" enum="ScreenOrientation"> + Reverse portrait orientation (upside down). </constant> <constant name="SCREEN_SENSOR_LANDSCAPE" value="4" enum="ScreenOrientation"> + Automatic landscape orientation (default or reverse depending on sensor). </constant> <constant name="SCREEN_SENSOR_PORTRAIT" value="5" enum="ScreenOrientation"> + Automatic portrait orientation (default or reverse depending on sensor). </constant> <constant name="SCREEN_SENSOR" value="6" enum="ScreenOrientation"> + Automatic landscape or portrait orientation (default or reverse depending on sensor). </constant> <constant name="KEYBOARD_TYPE_DEFAULT" value="0" enum="VirtualKeyboardType"> Default text virtual keyboard. @@ -1460,40 +1581,58 @@ Virtual keyboard with additional keys to assist with typing URLs. </constant> <constant name="CURSOR_ARROW" value="0" enum="CursorShape"> + Arrow cursor shape. This is the default when not pointing anything that overrides the mouse cursor, such as a [LineEdit] or [TextEdit]. </constant> <constant name="CURSOR_IBEAM" value="1" enum="CursorShape"> + I-beam cursor shape. This is used by default when hovering a control that accepts text input, such as [LineEdit] or [TextEdit]. </constant> <constant name="CURSOR_POINTING_HAND" value="2" enum="CursorShape"> + Pointing hand cursor shape. This is used by default when hovering a [LinkButton] or an URL tag in a [RichTextLabel].â‹… </constant> <constant name="CURSOR_CROSS" value="3" enum="CursorShape"> + Crosshair cursor. This is intended to be displayed when the user needs precise aim over an element, such as a rectangle selection tool or a color picker. </constant> <constant name="CURSOR_WAIT" value="4" enum="CursorShape"> + Wait cursor. On most cursor themes, this displays a spinning icon [i]besides[/i] the arrow. Intended to be used for non-blocking operations (when the user can do something else at the moment). See also [constant CURSOR_BUSY]. </constant> <constant name="CURSOR_BUSY" value="5" enum="CursorShape"> + Wait cursor. On most cursor themes, this [i]replaces[/i] the arrow with a spinning icon. Intended to be used for blocking operations (when the user can't do anything else at the moment). See also [constant CURSOR_WAIT]. </constant> <constant name="CURSOR_DRAG" value="6" enum="CursorShape"> + Dragging hand cursor. This is displayed during drag-and-drop operations. See also [constant CURSOR_CAN_DROP]. </constant> <constant name="CURSOR_CAN_DROP" value="7" enum="CursorShape"> + "Can drop" cursor. This is displayed during drag-and-drop operations if hovering over a [Control] that can accept the drag-and-drop event. On most cursor themes, this displays a dragging hand with an arrow symbol besides it. See also [constant CURSOR_DRAG]. </constant> <constant name="CURSOR_FORBIDDEN" value="8" enum="CursorShape"> + Forbidden cursor. This is displayed during drag-and-drop operations if the hovered [Control] can't accept the drag-and-drop event. </constant> <constant name="CURSOR_VSIZE" value="9" enum="CursorShape"> + Vertical resize cursor. Intended to be displayed when the hovered [Control] can be vertically resized using the mouse. See also [constant CURSOR_VSPLIT]. </constant> <constant name="CURSOR_HSIZE" value="10" enum="CursorShape"> + Horizontal resize cursor. Intended to be displayed when the hovered [Control] can be horizontally resized using the mouse. See also [constant CURSOR_HSPLIT]. </constant> <constant name="CURSOR_BDIAGSIZE" value="11" enum="CursorShape"> + Secondary diagonal resize cursor (top-right/bottom-left). Intended to be displayed when the hovered [Control] can be resized on both axes at once using the mouse. </constant> <constant name="CURSOR_FDIAGSIZE" value="12" enum="CursorShape"> + Main diagonal resize cursor (top-left/bottom-right). Intended to be displayed when the hovered [Control] can be resized on both axes at once using the mouse. </constant> <constant name="CURSOR_MOVE" value="13" enum="CursorShape"> + Move cursor. Intended to be displayed when the hovered [Control] can be moved using the mouse. </constant> <constant name="CURSOR_VSPLIT" value="14" enum="CursorShape"> + Vertical split cursor. This is displayed when hovering a [Control] with splits that can be vertically resized using the mouse, such as [VSplitContainer]. On some cursor themes, this cursor may have the same appearance as [constant CURSOR_VSIZE]. </constant> <constant name="CURSOR_HSPLIT" value="15" enum="CursorShape"> + Horizontal split cursor. This is displayed when hovering a [Control] with splits that can be horizontally resized using the mouse, such as [HSplitContainer]. On some cursor themes, this cursor may have the same appearance as [constant CURSOR_HSIZE]. </constant> <constant name="CURSOR_HELP" value="16" enum="CursorShape"> + Help cursor. On most cursor themes, this displays a question mark icon instead of the mouse cursor. Intended to be used when the user has requested help on the next element that will be clicked. </constant> <constant name="CURSOR_MAX" value="17" enum="CursorShape"> + Represents the size of the [enum CursorShape] enum. </constant> <constant name="WINDOW_MODE_WINDOWED" value="0" enum="WindowMode"> Windowed mode, i.e. [Window] doesn't occupy the whole screen (unless set to the size of the screen). @@ -1505,7 +1644,7 @@ Maximized window mode, i.e. [Window] will occupy whole screen area except task bar and still display its borders. Normally happens when the minimize button is pressed. </constant> <constant name="WINDOW_MODE_FULLSCREEN" value="3" enum="WindowMode"> - Full screen window mode. Note that this is not [i]exclusive[/i] full screen. On Windows and Linux, a borderless window is used to emulate full screen. On macOS, a new desktop is used to display the running project. + Full screen window mode. Note that this is not [i]exclusive[/i] full screen. On Windows and Linux (X11), a borderless window is used to emulate full screen. On macOS, a new desktop is used to display the running project. Regardless of the platform, enabling full screen will change the window size to match the monitor's size. Therefore, make sure your project supports [url=$DOCS_URL/tutorials/rendering/multiple_resolutions.html]multiple resolutions[/url] when enabling full screen mode. </constant> <constant name="WINDOW_MODE_EXCLUSIVE_FULLSCREEN" value="4" enum="WindowMode"> @@ -1525,7 +1664,7 @@ <constant name="WINDOW_FLAG_TRANSPARENT" value="3" enum="WindowFlags"> The window background can be transparent. [b]Note:[/b] This flag has no effect if [member ProjectSettings.display/window/per_pixel_transparency/allowed] is set to [code]false[/code]. - [b]Note:[/b] Transparency support is implemented on Linux, macOS and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. + [b]Note:[/b] Transparency support is implemented on Linux (X11), macOS and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. </constant> <constant name="WINDOW_FLAG_NO_FOCUS" value="4" enum="WindowFlags"> The window can't be focused. No-focus window will ignore all input, except mouse clicks. @@ -1570,26 +1709,26 @@ [b]Note:[/b] This flag is implemented on macOS. </constant> <constant name="VSYNC_DISABLED" value="0" enum="VSyncMode"> - No vertical synchronization, which means the engine will display frames as fast as possible (tearing may be visible). + No vertical synchronization, which means the engine will display frames as fast as possible (tearing may be visible). Framerate is unlimited (nonwithstanding [member Engine.max_fps]). </constant> <constant name="VSYNC_ENABLED" value="1" enum="VSyncMode"> - Default vertical synchronization mode, the image is displayed only on vertical blanking intervals (no tearing is visible). + Default vertical synchronization mode, the image is displayed only on vertical blanking intervals (no tearing is visible). Framerate is limited by the monitor refresh rate (nonwithstanding [member Engine.max_fps]). </constant> <constant name="VSYNC_ADAPTIVE" value="2" enum="VSyncMode"> - Behaves like [constant VSYNC_DISABLED] when the framerate drops below the screen's refresh rate to reduce stuttering (tearing may be visible), otherwise vertical synchronization is enabled to avoid tearing. + Behaves like [constant VSYNC_DISABLED] when the framerate drops below the screen's refresh rate to reduce stuttering (tearing may be visible). Otherwise, vertical synchronization is enabled to avoid tearing. Framerate is limited by the monitor refresh rate (nonwithstanding [member Engine.max_fps]). </constant> <constant name="VSYNC_MAILBOX" value="3" enum="VSyncMode"> - Displays the most recent image in the queue on vertical blanking intervals, while rendering to the other images (no tearing is visible). - Although not guaranteed, the images can be rendered as fast as possible, which may reduce input lag. + Displays the most recent image in the queue on vertical blanking intervals, while rendering to the other images (no tearing is visible). Framerate is unlimited (nonwithstanding [member Engine.max_fps]). + Although not guaranteed, the images can be rendered as fast as possible, which may reduce input lag (also called "Fast" V-Sync mode). [constant VSYNC_MAILBOX] works best when at least twice as many frames as the display refresh rate are rendered. </constant> <constant name="DISPLAY_HANDLE" value="0" enum="HandleType"> Display handle: - - Linux: [code]X11::Display*[/code] for the display. + - Linux (X11): [code]X11::Display*[/code] for the display. </constant> <constant name="WINDOW_HANDLE" value="1" enum="HandleType"> Window handle: - Windows: [code]HWND[/code] for the window. - - Linux: [code]X11::Window*[/code] for the window. + - Linux (X11): [code]X11::Window*[/code] for the window. - macOS: [code]NSWindow*[/code] for the window. - iOS: [code]UIViewController*[/code] for the view controller. - Android: [code]jObject[/code] for the activity. diff --git a/doc/classes/EditorFileDialog.xml b/doc/classes/EditorFileDialog.xml index 891c8d7d92..51e3706981 100644 --- a/doc/classes/EditorFileDialog.xml +++ b/doc/classes/EditorFileDialog.xml @@ -4,6 +4,7 @@ A modified version of [FileDialog] used by the editor. </brief_description> <description> + [EditorFileDialog] is an enhanced version of [FileDialog] avaiable only to editor plugins. Additional features include list of favorited/recent files and ability to see files as thumbnails grid instead of list. </description> <tutorials> </tutorials> @@ -62,7 +63,7 @@ The dialog's open or save mode, which affects the selection behavior. See [enum FileMode] </member> <member name="show_hidden_files" type="bool" setter="set_show_hidden_files" getter="is_showing_hidden_files" default="false"> - If [code]true[/code], hidden files and directories will be visible in the [EditorFileDialog]. + If [code]true[/code], hidden files and directories will be visible in the [EditorFileDialog]. This property is synchronized with [member EditorSettings.filesystem/file_dialog/show_hidden_files]. </member> <member name="title" type="String" setter="set_title" getter="get_title" overrides="Window" default=""Save a File"" /> </members> diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 1e3b1f07ee..0beb2459a3 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -70,6 +70,7 @@ <method name="get_editor_paths"> <return type="EditorPaths" /> <description> + Returns the [EditorPaths] singleton. </description> </method> <method name="get_editor_scale" qualifiers="const"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 563987e2a3..e413c526f4 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -691,11 +691,13 @@ </signal> <signal name="project_settings_changed"> <description> + Emitted when any project setting has changed. </description> </signal> <signal name="resource_saved"> <param index="0" name="resource" type="Resource" /> <description> + Emitted when the given [param resource] was saved on disc. </description> </signal> <signal name="scene_changed"> diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml index 9170c449bf..2aca19510b 100644 --- a/doc/classes/EditorProperty.xml +++ b/doc/classes/EditorProperty.xml @@ -61,6 +61,7 @@ <method name="update_property"> <return type="void" /> <description> + Forces refresh of the property display. </description> </method> </methods> diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml index dfc04c9cde..a02fd215d8 100644 --- a/doc/classes/EditorScript.xml +++ b/doc/classes/EditorScript.xml @@ -30,6 +30,7 @@ [/csharp] [/codeblocks] [b]Note:[/b] The script is run in the Editor context, which means the output is visible in the console window started with the Editor (stdout) instead of the usual Godot [b]Output[/b] dock. + [b]Note:[/b] EditorScript is [RefCounted], meaning it is destroyed when nothing references it. This can cause errors during asynchronous operations if there are no references to the script. </description> <tutorials> </tutorials> diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index 68de08f094..818dda59bc 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -363,7 +363,7 @@ The color to use for the selection box that surrounds selected nodes in the 3D editor viewport. The color's alpha channel influences the selection box's opacity. </member> <member name="editors/3d_gizmos/gizmo_colors/instantiated" type="Color" setter="" getter=""> - The color override to use for 3D editor gizmos if the [Node3D] in question is part of an instanced scene file (from the perspective of the current scene). + The color override to use for 3D editor gizmos if the [Node3D] in question is part of an instantiated scene file (from the perspective of the current scene). </member> <member name="editors/3d_gizmos/gizmo_colors/joint" type="Color" setter="" getter=""> The 3D editor gizmo color for [Joint3D]s and [PhysicalBone3D]s. @@ -677,19 +677,19 @@ If [code]true[/code], displays line length guidelines to help you keep line lengths in check. See also [member text_editor/appearance/guidelines/line_length_guideline_soft_column] and [member text_editor/appearance/guidelines/line_length_guideline_hard_column]. </member> <member name="text_editor/appearance/gutters/highlight_type_safe_lines" type="bool" setter="" getter=""> - If [code]true[/code], highlights type-safe lines by displaying their line number color with [member text_editor/theme/highlighting/safe_line_number_color] instead of [member text_editor/theme/highlighting/line_number_color]. Type-safe lines are lines of code where the type of all variables is known at compile-time. These type-safe lines will run faster in Godot 4.0 and later thanks to typed instructions. + If [code]true[/code], highlights type-safe lines by displaying their line number color with [member text_editor/theme/highlighting/safe_line_number_color] instead of [member text_editor/theme/highlighting/line_number_color]. Type-safe lines are lines of code where the type of all variables is known at compile-time. These type-safe lines may run faster thanks to typed instructions. </member> <member name="text_editor/appearance/gutters/line_numbers_zero_padded" type="bool" setter="" getter=""> If [code]true[/code], displays line numbers with zero padding (e.g. [code]007[/code] instead of [code]7[/code]). </member> <member name="text_editor/appearance/gutters/show_bookmark_gutter" type="bool" setter="" getter=""> - If [code]true[/code], displays a gutter at the left containing icons for bookmarks. + If [code]true[/code], displays icons for bookmarks in a gutter at the left. Bookmarks remain functional when this setting is disabled. </member> <member name="text_editor/appearance/gutters/show_info_gutter" type="bool" setter="" getter=""> If [code]true[/code], displays a gutter at the left containing icons for methods with signal connections. </member> <member name="text_editor/appearance/gutters/show_line_numbers" type="bool" setter="" getter=""> - If [code]true[/code], displays line numbers in the gutter at the left. + If [code]true[/code], displays line numbers in a gutter at the left. </member> <member name="text_editor/appearance/lines/code_folding" type="bool" setter="" getter=""> If [code]true[/code], displays the folding arrows next to indented code sections and allows code folding. If [code]false[/code], hides the folding arrows next to indented code sections and disallows code folding. @@ -758,7 +758,7 @@ [b]Note:[/b] You can hold down [kbd]Alt[/kbd] while using the mouse wheel to temporarily scroll 5 times faster. </member> <member name="text_editor/completion/add_type_hints" type="bool" setter="" getter=""> - If [code]true[/code], adds static typing hints such as [code]-> void[/code] and [code]: int[/code] when performing method definition autocompletion. + If [code]true[/code], adds static typing hints such as [code]-> void[/code] and [code]: int[/code] when using code autocompletion or when creating onready variables by drag and dropping nodes into the script editor while pressing the [kbd]Ctrl[/kbd] key. </member> <member name="text_editor/completion/auto_brace_complete" type="bool" setter="" getter=""> If [code]true[/code], automatically completes braces when making use of code completion. @@ -809,7 +809,7 @@ The script editor's background color. If set to a translucent color, the editor theme's base color will be visible behind. </member> <member name="text_editor/theme/highlighting/base_type_color" type="Color" setter="" getter=""> - The script editor's base type color (used for types like [Vector2], [Vector3], ...). + The script editor's base type color (used for types like [Vector2], [Vector3], [Color], ...). </member> <member name="text_editor/theme/highlighting/bookmark_color" type="Color" setter="" getter=""> The script editor's bookmark icon color (displayed in the gutter). @@ -869,10 +869,10 @@ [b]Note:[/b] When using the GDScript syntax highlighter, this is replaced by the function definition color configured in the syntax theme for function definitions (e.g. [code]func _ready():[/code]). </member> <member name="text_editor/theme/highlighting/keyword_color" type="Color" setter="" getter=""> - The script editor's non-control flow keyword color (used for keywords like [code]var[/code], [code]func[/code], some built-in methods, ...). + The script editor's non-control flow keyword color (used for keywords like [code]var[/code], [code]func[/code], [code]extends[/code], ...). </member> <member name="text_editor/theme/highlighting/line_length_guideline_color" type="Color" setter="" getter=""> - The script editor's color for the line length guideline. The "hard" line length guideline will be drawn with this color, whereas the "soft" line length guideline will be drawn with an opacity twice as low. + The script editor's color for the line length guideline. The "hard" line length guideline will be drawn with this color, whereas the "soft" line length guideline will be drawn with half of its opacity. </member> <member name="text_editor/theme/highlighting/line_number_color" type="Color" setter="" getter=""> The script editor's color for line numbers. See also [member text_editor/theme/highlighting/safe_line_number_color]. @@ -913,7 +913,7 @@ The script editor's background color for text. This should be set to a translucent color so that it can display on top of other line color modifiers such as [member text_editor/theme/highlighting/current_line_color]. </member> <member name="text_editor/theme/highlighting/user_type_color" type="Color" setter="" getter=""> - The script editor's color for user-defined types (using [code]@class_name[/code]). + The script editor's color for user-defined types (using [code]class_name[/code]). </member> <member name="text_editor/theme/highlighting/word_highlighted_color" type="Color" setter="" getter=""> The script editor's color for words highlighted by selecting them. Only visible if [member text_editor/appearance/caret/highlight_all_occurrences] is [code]true[/code]. diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml index 2ada211dab..9961e11f7d 100644 --- a/doc/classes/EditorSpinSlider.xml +++ b/doc/classes/EditorSpinSlider.xml @@ -10,13 +10,16 @@ </tutorials> <members> <member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false"> + If [code]true[/code], the slider will not draw background. </member> <member name="hide_slider" type="bool" setter="set_hide_slider" getter="is_hiding_slider" default="false"> If [code]true[/code], the slider is hidden. </member> <member name="label" type="String" setter="set_label" getter="get_label" default=""""> + The text that displays to the left of the value. </member> <member name="read_only" type="bool" setter="set_read_only" getter="is_read_only" default="false"> + If [code]true[/code], the slider can't be interacted with. </member> <member name="suffix" type="String" setter="set_suffix" getter="get_suffix" default=""""> The suffix to display after the value (in a faded color). This should generally be a plural word. You may have to use an abbreviation if the suffix is too long to be displayed. diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index 2b8663e039..821fae37a6 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -146,11 +146,13 @@ <return type="ScriptLanguage" /> <param index="0" name="index" type="int" /> <description> + Returns an instance of a [ScriptLanguage] with the given index. </description> </method> <method name="get_script_language_count"> <return type="int" /> <description> + Returns the number of available script languages. Use with [method get_script_language]. </description> </method> <method name="get_singleton" qualifiers="const"> @@ -163,6 +165,7 @@ <method name="get_singleton_list" qualifiers="const"> <return type="PackedStringArray" /> <description> + Returns a list of available global singletons. </description> </method> <method name="get_version_info" qualifiers="const"> @@ -244,6 +247,7 @@ <return type="void" /> <param index="0" name="language" type="ScriptLanguage" /> <description> + Registers a [ScriptLanguage] instance to be available with [code]ScriptServer[/code]. </description> </method> <method name="register_singleton"> @@ -251,12 +255,14 @@ <param index="0" name="name" type="StringName" /> <param index="1" name="instance" type="Object" /> <description> + Registers the given object as a singleton, globally available under [param name]. </description> </method> <method name="unregister_singleton"> <return type="void" /> <param index="0" name="name" type="StringName" /> <description> + Unregisters the singleton registered under [param name]. The singleton object is not freed. Only works with user-defined singletons created with [method register_singleton]. </description> </method> </methods> diff --git a/doc/classes/GPUParticles2D.xml b/doc/classes/GPUParticles2D.xml index f41e34c43a..4916171900 100644 --- a/doc/classes/GPUParticles2D.xml +++ b/doc/classes/GPUParticles2D.xml @@ -9,7 +9,8 @@ </description> <tutorials> <link title="Particle systems (2D)">$DOCS_URL/tutorials/2d/particle_systems_2d.html</link> - <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> + <link title="2D Particles Demo">https://godotengine.org/asset-library/asset/118</link> + <link title="2D Dodge The Creeps Demo (uses GPUParticles2D for the trail behind the player)">https://godotengine.org/asset-library/asset/515</link> </tutorials> <methods> <method name="capture_rect" qualifiers="const"> @@ -52,7 +53,7 @@ How rapidly particles in an emission cycle are emitted. If greater than [code]0[/code], there will be a gap in emissions before the next cycle begins. </member> <member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps" default="30"> - The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. + The particle system's frame rate is fixed to a value. For example, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. </member> <member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta" default="true"> If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. diff --git a/doc/classes/GPUParticles3D.xml b/doc/classes/GPUParticles3D.xml index e7b436010e..d0be4d784a 100644 --- a/doc/classes/GPUParticles3D.xml +++ b/doc/classes/GPUParticles3D.xml @@ -84,7 +84,7 @@ Time ratio between each emission. If [code]0[/code], particles are emitted continuously. If [code]1[/code], all particles are emitted simultaneously. </member> <member name="fixed_fps" type="int" setter="set_fixed_fps" getter="get_fixed_fps" default="30"> - The particle system's frame rate is fixed to a value. For instance, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. + The particle system's frame rate is fixed to a value. For example, changing the value to 2 will make the particles render at 2 frames per second. Note this does not slow down the simulation of the particle system itself. </member> <member name="fract_delta" type="bool" setter="set_fractional_delta" getter="get_fractional_delta" default="true"> If [code]true[/code], results in fractional delta calculation which has a smoother particles display effect. diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 510f14ec54..88cea6820e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -26,7 +26,7 @@ <param index="1" name="src_rect" type="Rect2i" /> <param index="2" name="dst" type="Vector2i" /> <description> - Alpha-blends [param src_rect] from [param src] image to this image at coordinates [param dst], clipped accordingly to both image bounds. This image and [param src] image [b]must[/b] have the same format. [param src_rect] with not positive size is treated as empty. + Alpha-blends [param src_rect] from [param src] image to this image at coordinates [param dst], clipped accordingly to both image bounds. This image and [param src] image [b]must[/b] have the same format. [param src_rect] with non-positive size is treated as empty. </description> </method> <method name="blend_rect_mask"> @@ -36,7 +36,7 @@ <param index="2" name="src_rect" type="Rect2i" /> <param index="3" name="dst" type="Vector2i" /> <description> - Alpha-blends [param src_rect] from [param src] image to this image using [param mask] image at coordinates [param dst], clipped accordingly to both image bounds. Alpha channels are required for both [param src] and [param mask]. [param dst] pixels and [param src] pixels will blend if the corresponding mask pixel's alpha value is not 0. This image and [param src] image [b]must[/b] have the same format. [param src] image and [param mask] image [b]must[/b] have the same size (width and height) but they can have different formats. [param src_rect] with not positive size is treated as empty. + Alpha-blends [param src_rect] from [param src] image to this image using [param mask] image at coordinates [param dst], clipped accordingly to both image bounds. Alpha channels are required for both [param src] and [param mask]. [param dst] pixels and [param src] pixels will blend if the corresponding mask pixel's alpha value is not 0. This image and [param src] image [b]must[/b] have the same format. [param src] image and [param mask] image [b]must[/b] have the same size (width and height) but they can have different formats. [param src_rect] with non-positive size is treated as empty. </description> </method> <method name="blit_rect"> @@ -45,7 +45,7 @@ <param index="1" name="src_rect" type="Rect2i" /> <param index="2" name="dst" type="Vector2i" /> <description> - Copies [param src_rect] from [param src] image to this image at coordinates [param dst], clipped accordingly to both image bounds. This image and [param src] image [b]must[/b] have the same format. [param src_rect] with not positive size is treated as empty. + Copies [param src_rect] from [param src] image to this image at coordinates [param dst], clipped accordingly to both image bounds. This image and [param src] image [b]must[/b] have the same format. [param src_rect] with non-positive size is treated as empty. </description> </method> <method name="blit_rect_mask"> @@ -55,7 +55,7 @@ <param index="2" name="src_rect" type="Rect2i" /> <param index="3" name="dst" type="Vector2i" /> <description> - Blits [param src_rect] area from [param src] image to this image at the coordinates given by [param dst], clipped accordingly to both image bounds. [param src] pixel is copied onto [param dst] if the corresponding [param mask] pixel's alpha value is not 0. This image and [param src] image [b]must[/b] have the same format. [param src] image and [param mask] image [b]must[/b] have the same size (width and height) but they can have different formats. [param src_rect] with not positive size is treated as empty. + Blits [param src_rect] area from [param src] image to this image at the coordinates given by [param dst], clipped accordingly to both image bounds. [param src] pixel is copied onto [param dst] if the corresponding [param mask] pixel's alpha value is not 0. This image and [param src] image [b]must[/b] have the same format. [param src] image and [param mask] image [b]must[/b] have the same size (width and height) but they can have different formats. [param src_rect] with non-positive size is treated as empty. </description> </method> <method name="bump_map_to_normal_map"> @@ -111,8 +111,8 @@ Copies [param src] image to this image. </description> </method> - <method name="create"> - <return type="void" /> + <method name="create" qualifiers="static"> + <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> <param index="2" name="use_mipmaps" type="bool" /> @@ -121,8 +121,8 @@ Creates an empty image of given size and format. See [enum Format] constants. If [param use_mipmaps] is [code]true[/code] then generate mipmaps for this image. See the [method generate_mipmaps]. </description> </method> - <method name="create_from_data"> - <return type="void" /> + <method name="create_from_data" qualifiers="static"> + <return type="Image" /> <param index="0" name="width" type="int" /> <param index="1" name="height" type="int" /> <param index="2" name="use_mipmaps" type="bool" /> @@ -454,6 +454,17 @@ Saves the image as a WebP (Web Picture) file to a byte array. By default it will save lossless. If [param lossy] is true, the image will be saved lossy, using the [param quality] setting between 0.0 and 1.0 (inclusive). </description> </method> + <method name="set_data"> + <return type="void" /> + <param index="0" name="width" type="int" /> + <param index="1" name="height" type="int" /> + <param index="2" name="use_mipmaps" type="bool" /> + <param index="3" name="format" type="int" enum="Image.Format" /> + <param index="4" name="data" type="PackedByteArray" /> + <description> + Overwrites data of an existing [Image]. Non-static equivalent of [method create_from_data]. + </description> + </method> <method name="set_pixel"> <return type="void" /> <param index="0" name="x" type="int" /> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 56b95fc755..89d52caa02 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -157,7 +157,7 @@ <method name="get_mouse_button_mask" qualifiers="const"> <return type="int" enum="MouseButton" /> <description> - Returns mouse buttons as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. + Returns mouse buttons as a bitmask. If multiple mouse buttons are pressed at the same time, the bits are added together. Equivalent to [method DisplayServer.mouse_get_button_state]. </description> </method> <method name="get_vector" qualifiers="const"> diff --git a/doc/classes/InputEventScreenTouch.xml b/doc/classes/InputEventScreenTouch.xml index fb50454917..346b6bf732 100644 --- a/doc/classes/InputEventScreenTouch.xml +++ b/doc/classes/InputEventScreenTouch.xml @@ -11,6 +11,9 @@ <link title="InputEvent">$DOCS_URL/tutorials/inputs/inputevent.html</link> </tutorials> <members> + <member name="double_tap" type="bool" setter="set_double_tap" getter="is_double_tap" default="false"> + If [code]true[/code], the touch's state is a double tap. + </member> <member name="index" type="int" setter="set_index" getter="get_index" default="0"> The touch index in the case of a multi-touch event. One index = one finger. </member> diff --git a/doc/classes/Light3D.xml b/doc/classes/Light3D.xml index e9ebbc0a41..60e20cd97d 100644 --- a/doc/classes/Light3D.xml +++ b/doc/classes/Light3D.xml @@ -55,6 +55,7 @@ </member> <member name="light_angular_distance" type="float" setter="set_param" getter="get_param" default="0.0"> The light's angular size in degrees. Increasing this will make shadows softer at greater distances. Only available for [DirectionalLight3D]s. For reference, the Sun from the Earth is approximately [code]0.5[/code]. + [b]Note:[/b] [member light_angular_distance] is not affected by [member Node3D.scale] (the light's scale or its parent's scale). </member> <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light3D.BakeMode" default="2"> The light's bake mode. This will affect the global illumination techniques that have an effect on the light's rendering. See [enum BakeMode]. @@ -91,6 +92,7 @@ </member> <member name="light_size" type="float" setter="set_param" getter="get_param" default="0.0"> The size of the light in Godot units. Only available for [OmniLight3D]s and [SpotLight3D]s. Increasing this value will make the light fade out slower and shadows appear blurrier. This can be used to simulate area lights to an extent. + [b]Note:[/b] [member light_size] is not affected by [member Node3D.scale] (the light's scale or its parent's scale). </member> <member name="light_specular" type="float" setter="set_param" getter="get_param" default="0.5"> The intensity of the specular blob in objects affected by the light. At [code]0[/code], the light becomes a pure diffuse light. When not baking emission, this can be used to avoid unrealistic reflections when placing lights above an emissive surface. diff --git a/doc/classes/Mesh.xml b/doc/classes/Mesh.xml index d3d5a7bfaa..facbe5fd0f 100644 --- a/doc/classes/Mesh.xml +++ b/doc/classes/Mesh.xml @@ -129,7 +129,7 @@ <method name="get_aabb" qualifiers="const"> <return type="AABB" /> <description> - Returns the smallest [AABB] enclosing this mesh in local space. Not affected by [code]custom_aabb[/code]. See also [method VisualInstance3D.get_transformed_aabb]. + Returns the smallest [AABB] enclosing this mesh in local space. Not affected by [code]custom_aabb[/code]. [b]Note:[/b] This is only implemented for [ArrayMesh] and [PrimitiveMesh]. </description> </method> diff --git a/doc/classes/MovieWriter.xml b/doc/classes/MovieWriter.xml index f2509ad2b2..1652dd52aa 100644 --- a/doc/classes/MovieWriter.xml +++ b/doc/classes/MovieWriter.xml @@ -4,7 +4,7 @@ Abstract class for non-real-time video recording encoders. </brief_description> <description> - Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] command line argument, this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities. + Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities. Godot has 2 built-in [MovieWriter]s: - AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. MJPEG does not support transparency. AVI output is currently limited to a file of 4 GB in size at most. - PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported, even if the root viewport is set to be transparent. @@ -46,7 +46,7 @@ <param index="1" name="fps" type="int" /> <param index="2" name="base_path" type="String" /> <description> - Called once before the engine starts writing video and audio data. [param movie_size] is the width and height of the video to save. [param fps] is the number of frames per second specified in the project settings or using the [code]--fixed-fps <fps>[/code] command line argument. + Called once before the engine starts writing video and audio data. [param movie_size] is the width and height of the video to save. [param fps] is the number of frames per second specified in the project settings or using the [code]--fixed-fps <fps>[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. </description> </method> <method name="_write_end" qualifiers="virtual"> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index ce2b5f2584..d8a232b3cf 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -19,7 +19,7 @@ <method name="get_aabb" qualifiers="const"> <return type="AABB" /> <description> - Returns the visibility axis-aligned bounding box in local space. See also [method VisualInstance3D.get_transformed_aabb]. + Returns the visibility axis-aligned bounding box in local space. </description> </method> <method name="get_instance_color" qualifiers="const"> diff --git a/doc/classes/MultiplayerPeer.xml b/doc/classes/MultiplayerPeer.xml index 0f57ff9e55..ba6c60a088 100644 --- a/doc/classes/MultiplayerPeer.xml +++ b/doc/classes/MultiplayerPeer.xml @@ -25,10 +25,22 @@ Returns the current state of the connection. See [enum ConnectionStatus]. </description> </method> + <method name="get_packet_channel" qualifiers="const"> + <return type="int" /> + <description> + Returns the channel over which the next available packet was received. See [method PacketPeer.get_available_packet_count]. + </description> + </method> + <method name="get_packet_mode" qualifiers="const"> + <return type="int" enum="MultiplayerPeer.TransferMode" /> + <description> + Returns the [enum MultiplayerPeer.TransferMode] the remote peer used to send the next available packet. See [method PacketPeer.get_available_packet_count]. + </description> + </method> <method name="get_packet_peer" qualifiers="const"> <return type="int" /> <description> - Returns the ID of the [MultiplayerPeer] who sent the most recent packet. + Returns the ID of the [MultiplayerPeer] who sent the next available packet. See [method PacketPeer.get_available_packet_count]. </description> </method> <method name="get_unique_id" qualifiers="const"> @@ -37,6 +49,12 @@ Returns the ID of this [MultiplayerPeer]. </description> </method> + <method name="is_server_relay_supported" qualifiers="const"> + <return type="bool" /> + <description> + Returns true if the server can act as a relay in the current configuration (i.e. if the higher level [MultiplayerAPI] should notify connected clients of other peers, and implement a relay protocol to allow communication between them). + </description> + </method> <method name="poll"> <return type="void" /> <description> diff --git a/doc/classes/NavigationAgent2D.xml b/doc/classes/NavigationAgent2D.xml index 3437e0eca4..a69491ec66 100644 --- a/doc/classes/NavigationAgent2D.xml +++ b/doc/classes/NavigationAgent2D.xml @@ -163,7 +163,7 @@ </description> </signal> <signal name="velocity_computed"> - <param index="0" name="safe_velocity" type="Vector3" /> + <param index="0" name="safe_velocity" type="Vector2" /> <description> Notifies when the collision avoidance velocity is calculated. Emitted by [method set_velocity]. Only emitted when [member avoidance_enabled] is true. </description> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index bceb285584..620895d8d8 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -529,9 +529,9 @@ <method name="move_child"> <return type="void" /> <param index="0" name="child_node" type="Node" /> - <param index="1" name="to_position" type="int" /> + <param index="1" name="to_index" type="int" /> <description> - Moves a child node to a different position (order) among the other children. Since calls, signals, etc are performed by tree order, changing the order of children nodes may be useful. If [param to_position] is negative, the index will be counted from the end. + Moves a child node to a different index (order) among the other children. Since calls, signals, etc. are performed by tree order, changing the order of children nodes may be useful. If [param to_index] is negative, the index will be counted from the end. [b]Note:[/b] Internal children can only be moved within their expected "internal range" (see [code]internal[/code] parameter in [method add_child]). </description> </method> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index 3df7ec931f..bc2570b183 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -296,6 +296,7 @@ <member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)"> Scale part of the local transformation. [b]Note:[/b] Mixed negative scales in 3D are not decomposable from the transformation matrix. Due to the way scale is represented with transformation matrices in Godot, the scale values will either be all positive or all negative. + [b]Note:[/b] Not all nodes are visually scaled by the [member scale] property. For example, [Light3D]s are not visually affected by [member scale]. </member> <member name="top_level" type="bool" setter="set_as_top_level" getter="is_set_as_top_level" default="false"> If [code]true[/code], the node will not inherit its transformations from its parent. Node transformations are only in global space. diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 9db100c9f8..022b4826ea 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -4,7 +4,7 @@ Pre-parsed scene tree path. </brief_description> <description> - A pre-parsed relative or absolute path in a scene tree, for use with [method Node.get_node] and similar functions. It can reference a node, a resource within a node, or a property of a node or resource. For instance, [code]"Path2D/PathFollow2D/Sprite2D:texture:size"[/code] would refer to the [code]size[/code] property of the [code]texture[/code] resource on the node named [code]"Sprite2D"[/code] which is a child of the other named nodes in the path. + A pre-parsed relative or absolute path in a scene tree, for use with [method Node.get_node] and similar functions. It can reference a node, a resource within a node, or a property of a node or resource. For example, [code]"Path2D/PathFollow2D/Sprite2D:texture:size"[/code] would refer to the [code]size[/code] property of the [code]texture[/code] resource on the node named [code]"Sprite2D"[/code] which is a child of the other named nodes in the path. You will usually just pass a string to [method Node.get_node] and it will be automatically converted, but you may occasionally want to parse a path ahead of time with [NodePath] or the literal syntax [code]^"path"[/code]. Exporting a [NodePath] variable will give you a node selection widget in the properties panel of the editor, which can often be useful. A [NodePath] is composed of a list of slash-separated node names (like a filesystem path) and an optional colon-separated list of "subnames" which can be resources or properties. Some examples of NodePaths include the following: diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 3aa26cbb21..0c74690924 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -4,7 +4,8 @@ Operating System functions. </brief_description> <description> - Operating System functions. OS wraps the most common functionality to communicate with the host operating system, such as the clipboard, video driver, delays, environment variables, execution of binaries, command line, etc. + Operating System functions. [OS] wraps the most common functionality to communicate with the host operating system, such as the clipboard, video driver, delays, environment variables, execution of binaries, command line, etc. + [b]Note:[/b] In Godot 4, [OS] functions related to window management were moved to the [DisplayServer] singleton. </description> <tutorials> <link title="OS Test Demo">https://godotengine.org/asset-library/asset/677</link> @@ -451,7 +452,7 @@ Returns the video adapter driver name and version for the user's currently active graphics card. The first element holds the driver name, such as [code]nvidia[/code], [code]amdgpu[/code], etc. The second element holds the driver version. For e.g. the [code]nvidia[/code] driver on a Linux/BSD platform, the version is in the format [code]510.85.02[/code]. For Windows, the driver's format is [code]31.0.15.1659[/code]. - [b]Note:[/b] This method is only supported on the platforms Linux/BSD and Windows. It returns an empty array on other platforms. + [b]Note:[/b] This method is only supported on the platforms Linux/BSD and Windows when not running in headless mode. It returns an empty array on other platforms. </description> </method> <method name="has_environment" qualifiers="const"> diff --git a/doc/classes/OmniLight3D.xml b/doc/classes/OmniLight3D.xml index ce63dbdbc1..193ae8deeb 100644 --- a/doc/classes/OmniLight3D.xml +++ b/doc/classes/OmniLight3D.xml @@ -15,6 +15,7 @@ </member> <member name="omni_range" type="float" setter="set_param" getter="get_param" default="5.0"> The light's radius. Note that the effectively lit area may appear to be smaller depending on the [member omni_attenuation] in use. No matter the [member omni_attenuation] in use, the light will never reach anything outside this radius. + [b]Note:[/b] [member omni_range] is not affected by [member Node3D.scale] (the light's scale or its parent's scale). </member> <member name="omni_shadow_mode" type="int" setter="set_shadow_mode" getter="get_shadow_mode" enum="OmniLight3D.ShadowMode" default="1"> See [enum ShadowMode]. diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml index 807a94ab24..d4050e3bd1 100644 --- a/doc/classes/ParticleProcessMaterial.xml +++ b/doc/classes/ParticleProcessMaterial.xml @@ -217,7 +217,7 @@ Maximum linear acceleration applied to each particle in the direction of motion. </member> <member name="linear_accel_min" type="float" setter="set_param_min" getter="get_param_min" default="0.0"> - Minimum equivalent of [member linear_accel_min]. + Minimum equivalent of [member linear_accel_max]. </member> <member name="orbit_velocity_curve" type="Texture2D" setter="set_param_texture" getter="get_param_texture"> Each particle's orbital velocity will vary along this [CurveTexture]. diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 23287f4de1..c99760996f 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -533,6 +533,8 @@ <param index="0" name="id" type="int" /> <description> Emitted when an item of some [param id] is pressed or its accelerator is activated. + + [b]Note:[/b] If [param id] is negative (either explicitly or due to overflow), this will return the correponding index instead. </description> </signal> <signal name="index_pressed"> diff --git a/doc/classes/PortableCompressedTexture2D.xml b/doc/classes/PortableCompressedTexture2D.xml index c7e2f2fbdc..a0492f2c07 100644 --- a/doc/classes/PortableCompressedTexture2D.xml +++ b/doc/classes/PortableCompressedTexture2D.xml @@ -22,7 +22,7 @@ <description> Initializes the compressed texture from a base image. The compression mode must be provided. If this image will be used as a normal map, the "normal map" flag is recommended, to ensure optimum quality. - If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WEBP compression quality. + If lossy compression is requested, the quality setting can optionally be provided. This maps to Lossy WebP compression quality. </description> </method> <method name="get_compression_mode" qualifiers="const"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 4b2bb3b6e9..8da73f1b7d 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -251,7 +251,7 @@ Changes to this setting will only be applied upon restarting the application. </member> <member name="application/run/disable_stdout" type="bool" setter="" getter="" default="false"> - If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] command line argument. See also [member application/run/disable_stderr]. + If [code]true[/code], disables printing to standard output. This is equivalent to starting the editor or project with the [code]--quiet[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. See also [member application/run/disable_stderr]. Changes to this setting will only be applied upon restarting the application. </member> <member name="application/run/flush_stdout_on_print" type="bool" setter="" getter="" default="false"> @@ -297,7 +297,7 @@ <member name="audio/driver/driver" type="String" setter="" getter=""> Specifies the audio driver to use. This setting is platform-dependent as each platform supports different audio drivers. If left empty, the default audio driver will be used. The [code]Dummy[/code] audio driver disables all audio playback and recording, which is useful for non-game applications as it reduces CPU usage. It also prevents the engine from appearing as an application playing audio in the OS' audio mixer. - [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] command line argument. + [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--audio-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. </member> <member name="audio/driver/enable_input" type="bool" setter="" getter="" default="false"> If [code]true[/code], microphone input will be allowed. This requires appropriate permissions to be set when exporting to Android or iOS. @@ -413,6 +413,9 @@ <member name="debug/gdscript/warnings/standalone_ternary" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that has no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement. </member> + <member name="debug/gdscript/warnings/static_called_on_instance" type="int" setter="" getter="" default="1"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly. + </member> <member name="debug/gdscript/warnings/treat_warnings_as_errors" type="bool" setter="" getter="" default="false"> If [code]true[/code], all warnings will be reported as if they are errors. </member> @@ -477,7 +480,7 @@ Print GPU profile information to standard output every second. This includes how long each frame takes the GPU to render on average, broken down into different steps of the render pipeline, such as CanvasItems, shadows, glow, etc. </member> <member name="debug/settings/stdout/verbose_stdout" type="bool" setter="" getter="" default="false"> - Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc. This can also be enabled using the [code]--verbose[/code] or [code]-v[/code] command line argument, even on an exported project. See also [method OS.is_stdout_verbose] and [method @GlobalScope.print_verbose]. + Print more information to standard output when running. It displays information such as memory leaks, which scenes and resources are being loaded, etc. This can also be enabled using the [code]--verbose[/code] or [code]-v[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url], even on an exported project. See also [method OS.is_stdout_verbose] and [method @GlobalScope.print_verbose]. </member> <member name="debug/shapes/collision/contact_color" type="Color" setter="" getter="" default="Color(1, 0.2, 0.1, 0.8)"> Color of the contact points between collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu. @@ -565,6 +568,13 @@ <member name="display/window/ios/hide_home_indicator" type="bool" setter="" getter="" default="true"> If [code]true[/code], the home indicator is hidden automatically. This only affects iOS devices without a physical home button. </member> + <member name="display/window/ios/hide_status_bar" type="bool" setter="" getter="" default="true"> + If [code]true[/code], the status bar is hidden while the app is running. + </member> + <member name="display/window/ios/suppress_ui_gesture" type="bool" setter="" getter="" default="true"> + If [code]true[/code], it will require two swipes to access iOS UI that uses gestures. + [b]Note:[/b] This setting has no effect on the home indicator if [code]hide_home_indicator[/code] is [code]true[/code]. + </member> <member name="display/window/per_pixel_transparency/allowed" type="bool" setter="" getter="" default="false"> If [code]true[/code], allows per-pixel transparency for the window background. This affects performance, so leave it on [code]false[/code] unless you need it. See also [member display/window/size/transparent] and [member rendering/transparent_background]. </member> @@ -620,7 +630,7 @@ </member> <member name="editor/movie_writer/fps" type="int" setter="" getter="" default="60"> The number of frames per second to record in the video when writing a movie. Simulation speed will adjust to always match the specified framerate, which means the engine will appear to run slower at higher [member editor/movie_writer/fps] values. Certain FPS values will require you to adjust [member editor/movie_writer/mix_rate] to prevent audio from desynchronizing over time. - This can be specified manually on the command line using the [code]--fixed-fps <fps>[/code] command line argument. + This can be specified manually on the command line using the [code]--fixed-fps <fps>[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. </member> <member name="editor/movie_writer/mix_rate" type="int" setter="" getter="" default="48000"> The audio mix rate to use in the recorded audio when writing a movie (in Hz). This can be different from [member audio/driver/mix_rate], but this value must be divisible by [member editor/movie_writer/fps] to prevent audio from desynchronizing over time. @@ -684,7 +694,8 @@ Default value for [member ScrollContainer.scroll_deadzone], which will be used for all [ScrollContainer]s unless overridden. </member> <member name="gui/common/swap_cancel_ok" type="bool" setter="" getter=""> - If [code]true[/code], swaps Cancel and OK buttons in dialogs on Windows and UWP to follow interface conventions. + If [code]true[/code], swaps [b]Cancel[/b] and [b]OK[/b] buttons in dialogs on Windows and UWP to follow interface conventions. [method DisplayServer.get_swap_cancel_ok] can be used to query whether buttons are swapped at run-time. + [b]Note:[/b] This doesn't affect native dialogs such as the ones spawned by [method DisplayServer.dialog_show]. </member> <member name="gui/common/text_edit_undo_stack_max_size" type="int" setter="" getter="" default="1024"> Maximum undo/redo history size for [TextEdit] fields. @@ -820,6 +831,13 @@ </member> <member name="input/ui_swap_input_direction" type="Dictionary" setter="" getter=""> </member> + <member name="input/ui_text_add_selection_for_next_occurrence" type="Dictionary" setter="" getter=""> + If a selection is currently active with the last caret in text fields, searches for the next occurrence of the selection, adds a caret and selects the next occurrence. + If no selection is currently active with the last caret in text fields, selects the word currently under the caret. + The action can be performed sequentially for all occurrences of the selection of the last caret and for all existing carets. + The viewport is adjusted to the latest newly added caret. + [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. + </member> <member name="input/ui_text_backspace" type="Dictionary" setter="" getter=""> Default [InputEventAction] to delete the character before the text cursor. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. @@ -974,7 +992,7 @@ </member> <member name="input/ui_text_select_word_under_caret" type="Dictionary" setter="" getter=""> If no selection is currently active, selects the word currently under the caret in text fields. If a selection is currently active, deselects the current selection. - [b]Note:[/b] Currently, this is only implemented in [TextEdit], not [LineEdit]. + [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_text_submit" type="Dictionary" setter="" getter=""> Default [InputEventAction] to submit a text field. @@ -1000,7 +1018,7 @@ </member> <member name="input_devices/pen_tablet/driver" type="String" setter="" getter=""> Specifies the tablet driver to use. If left empty, the default driver will be used. - [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] command line argument. + [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--tablet-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. </member> <member name="input_devices/pen_tablet/driver.windows" type="String" setter="" getter=""> Override for [member input_devices/pen_tablet/driver] on Windows. @@ -1065,8 +1083,8 @@ <member name="internationalization/rendering/text_driver" type="String" setter="" getter="" default=""""> Specifies the [TextServer] to use. If left empty, the default will be used. "ICU / HarfBuzz / Graphite" is the most advanced text driver, supporting right-to-left typesetting and complex scripts (for languages like Arabic, Hebrew, etc). The "Fallback" text driver does not support right-to-left typesetting and complex scripts. - [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--text-driver[/code] command line argument. - [b]Note:[/b] There is an additional [code]Dummy[/code] text driver available, which disables all text rendering and font-related functionality. This driver is not listed in the project settings, but it can be enabled when running the editor or project using the [code]--text-driver Dummy[/code] command line argument. + [b]Note:[/b] The driver in use can be overridden at runtime via the [code]--text-driver[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. + [b]Note:[/b] There is an additional [code]Dummy[/code] text driver available, which disables all text rendering and font-related functionality. This driver is not listed in the project settings, but it can be enabled when running the editor or project using the [code]--text-driver Dummy[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url]. </member> <member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1". @@ -1801,7 +1819,9 @@ Another way to combat specular aliasing is to enable [member rendering/anti_aliasing/screen_space_roughness_limiter/enabled]. </member> <member name="rendering/anti_aliasing/quality/use_debanding" type="bool" setter="" getter="" default="false"> - If [code]true[/code], uses a fast post-processing dithering filter on the default screen [Viewport] to make banding significantly less visible. In some cases, the dithering pattern may be slightly noticable. Note that this will make losslessly compressed (PNG etc.) screenshots larger. + If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS]. + In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger. + [b]Note:[/b] This property is only read when the project starts. To set debanding at run-time, set [member Viewport.use_debanding] on the root [Viewport] instead. </member> <member name="rendering/anti_aliasing/quality/use_taa" type="bool" setter="" getter="" default="false"> Enables Temporal Anti-Aliasing for the default screen [Viewport]. TAA works by jittering the camera and accumulating the images of the last rendered frames, motion vector rendering is used to account for camera and object motion. Enabling TAA can make the image blurrier, which is partially counteracted by automatically using a negative mipmap LOD bias (see [member rendering/textures/default_filters/texture_mipmap_bias]). diff --git a/doc/classes/Projection.xml b/doc/classes/Projection.xml index 5690ea5e95..602833bca5 100644 --- a/doc/classes/Projection.xml +++ b/doc/classes/Projection.xml @@ -1,8 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Projection" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + 3D projection (4x4 matrix). </brief_description> <description> + A 4x4 matrix used for 3D projective transformations. It can represent transformations such as translation, rotation, scaling, shearing, and perspective division. It consists of four [Vector4] columns. + For purely linear transformations (translation, rotation, and scale), it is recommended to use [Transform3D], as it is more performant and has a lower memory footprint. + Used internally as [Camera3D]'s projection matrix. </description> <tutorials> </tutorials> @@ -10,18 +14,21 @@ <constructor name="Projection"> <return type="Projection" /> <description> + Constructs a default-initialized [Projection] set to [constant IDENTITY]. </description> </constructor> <constructor name="Projection"> <return type="Projection" /> <param index="0" name="from" type="Projection" /> <description> + Constructs a [Projection] as a copy of the given [Projection]. </description> </constructor> <constructor name="Projection"> <return type="Projection" /> <param index="0" name="from" type="Transform3D" /> <description> + Constructs a Projection as a copy of the given [Transform3D]. </description> </constructor> <constructor name="Projection"> @@ -40,12 +47,14 @@ <return type="Projection" /> <param index="0" name="flip_y" type="bool" /> <description> + Creates a new [Projection] that projects positions from a depth range of [code]-1[/code] to [code]1[/code] to one that ranges from [code]0[/code] to [code]1[/code], and flips the projected positions vertically, according to [param flip_y]. </description> </method> <method name="create_fit_aabb" qualifiers="static"> <return type="Projection" /> <param index="0" name="aabb" type="AABB" /> <description> + Creates a new [Projection] that scales a given projection to fit around a given [AABB] in projection space. </description> </method> <method name="create_for_hmd" qualifiers="static"> @@ -59,6 +68,8 @@ <param index="6" name="z_near" type="float" /> <param index="7" name="z_far" type="float" /> <description> + Creates a new [Projection] for projecting positions onto a head-mounted display with the given X:Y aspect ratio, distance between eyes, display width, distance to lens, oversampling factor, and depth clipping planes. + [param eye] creates the projection for the left eye when set to 1, or the right eye when set to 2. </description> </method> <method name="create_frustum" qualifiers="static"> @@ -70,6 +81,7 @@ <param index="4" name="z_near" type="float" /> <param index="5" name="z_far" type="float" /> <description> + Creates a new [Projection] that projects positions in a frustum with the given clipping planes. </description> </method> <method name="create_frustum_aspect" qualifiers="static"> @@ -81,12 +93,15 @@ <param index="4" name="z_far" type="float" /> <param index="5" name="flip_fov" type="bool" default="false" /> <description> + Creates a new [Projection] that projects positions in a frustum with the given size, X:Y aspect ratio, offset, and clipping planes. + [param flip_fov] determines whether the projection's field of view is flipped over its diagonal. </description> </method> <method name="create_light_atlas_rect" qualifiers="static"> <return type="Projection" /> <param index="0" name="rect" type="Rect2" /> <description> + Creates a new [Projection] that projects positions into the given [Rect2]. </description> </method> <method name="create_orthogonal" qualifiers="static"> @@ -98,6 +113,7 @@ <param index="4" name="z_near" type="float" /> <param index="5" name="z_far" type="float" /> <description> + Creates a new [Projection] that projects positions using an orthogonal projection with the given clipping planes. </description> </method> <method name="create_orthogonal_aspect" qualifiers="static"> @@ -108,6 +124,8 @@ <param index="3" name="z_far" type="float" /> <param index="4" name="flip_fov" type="bool" default="false" /> <description> + Creates a new [Projection] that projects positions using an orthogonal projection with the given size, X:Y aspect ratio, and clipping planes. + [param flip_fov] determines whether the projection's field of view is flipped over its diagonal. </description> </method> <method name="create_perspective" qualifiers="static"> @@ -118,6 +136,8 @@ <param index="3" name="z_far" type="float" /> <param index="4" name="flip_fov" type="bool" default="false" /> <description> + Creates a new [Projection] that projects positions using a perspective projection with the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping planes. + [param flip_fov] determines whether the projection's field of view is flipped over its diagonal. </description> </method> <method name="create_perspective_hmd" qualifiers="static"> @@ -131,31 +151,40 @@ <param index="6" name="intraocular_dist" type="float" /> <param index="7" name=" convergence_dist" type="float" /> <description> + Creates a new [Projection] that projects positions using a perspective projection with the given Y-axis field of view (in degrees), X:Y aspect ratio, and clipping distances. The projection is adjusted for a head-mounted display with the given distance between eyes and distance to a point that can be focused on. + [param eye] creates the projection for the left eye when set to 1, or the right eye when set to 2. + [param flip_fov] determines whether the projection's field of view is flipped over its diagonal. </description> </method> <method name="determinant" qualifiers="const"> <return type="float" /> <description> + Returns a scalar value that is the signed factor by which areas are scaled by this matrix. If the sign is negative, the matrix flips the orientation of the area. + The determinant can be used to calculate the invertibility of a matrix or solve linear systems of equations involving the matrix, among other applications. </description> </method> <method name="flipped_y" qualifiers="const"> <return type="Projection" /> <description> + Returns a copy of this [Projection] with the signs of the values of the Y column flipped. </description> </method> <method name="get_aspect" qualifiers="const"> <return type="float" /> <description> + Returns the X:Y aspect ratio of this [Projection]'s viewport. </description> </method> <method name="get_far_plane_half_extents" qualifiers="const"> <return type="Vector2" /> <description> + Returns the dimensions of the far clipping plane of the projection, divided by two. </description> </method> <method name="get_fov" qualifiers="const"> <return type="float" /> <description> + Returns the horizontal field of view of the projection (in degrees). </description> </method> <method name="get_fovy" qualifiers="static"> @@ -163,89 +192,114 @@ <param index="0" name="fovx" type="float" /> <param index="1" name="aspect" type="float" /> <description> + Returns the vertical field of view of the projection (in degrees) associated with the given horizontal field of view (in degrees) and aspect ratio. </description> </method> <method name="get_lod_multiplier" qualifiers="const"> <return type="float" /> <description> + Returns the factor by which the visible level of detail is scaled by this [Projection]. </description> </method> <method name="get_pixels_per_meter" qualifiers="const"> <return type="int" /> <param index="0" name="for_pixel_width" type="int" /> <description> + Returns the number of pixels with the given pixel width displayed per meter, after this [Projection] is applied. </description> </method> <method name="get_projection_plane" qualifiers="const"> <return type="Plane" /> <param index="0" name="plane" type="int" /> <description> + Returns the clipping plane of this [Projection] whose index is given by [param plane]. + [param plane] should be equal to one of [constant PLANE_NEAR], [constant PLANE_FAR], [constant PLANE_LEFT], [constant PLANE_TOP], [constant PLANE_RIGHT], or [constant PLANE_BOTTOM]. </description> </method> <method name="get_viewport_half_extents" qualifiers="const"> <return type="Vector2" /> <description> + Returns the dimensions of the viewport plane that this [Projection] projects positions onto, divided by two. </description> </method> <method name="get_z_far" qualifiers="const"> <return type="float" /> <description> + Returns the distance for this [Projection] beyond which positions are clipped. </description> </method> <method name="get_z_near" qualifiers="const"> <return type="float" /> <description> + Returns the distance for this [Projection] before which positions are clipped. </description> </method> <method name="inverse" qualifiers="const"> <return type="Projection" /> <description> + Returns a [Projection] that performs the inverse of this [Projection]'s projective transformation. </description> </method> <method name="is_orthogonal" qualifiers="const"> <return type="bool" /> <description> + Returns [code]true[/code] if this [Projection] performs an orthogonal projection. </description> </method> <method name="jitter_offseted" qualifiers="const"> <return type="Projection" /> <param index="0" name="offset" type="Vector2" /> <description> + Returns a [Projection] with the X and Y values from the given [Vector2] added to the first and second values of the final column respectively. </description> </method> <method name="perspective_znear_adjusted" qualifiers="const"> <return type="Projection" /> <param index="0" name="new_znear" type="float" /> <description> + Returns a [Projection] with the near clipping distance adjusted to be [param new_znear]. + [b]Note:[/b] The original [Projection] must be a perspective projection. </description> </method> </methods> <members> <member name="w" type="Vector4" setter="" getter="" default="Vector4(0, 0, 0, 1)"> + The projection matrix's W vector (column 3). Equivalent to array index [code]3[/code]. </member> <member name="x" type="Vector4" setter="" getter="" default="Vector4(1, 0, 0, 0)"> + The projection matrix's X vector (column 0). Equivalent to array index [code]0[/code]. </member> <member name="y" type="Vector4" setter="" getter="" default="Vector4(0, 1, 0, 0)"> + The projection matrix's Y vector (column 1). Equivalent to array index [code]1[/code]. </member> <member name="z" type="Vector4" setter="" getter="" default="Vector4(0, 0, 1, 0)"> + The projection matrix's Z vector (column 2). Equivalent to array index [code]2[/code]. </member> </members> <constants> <constant name="PLANE_NEAR" value="0"> + The index value of the projection's near clipping plane. </constant> <constant name="PLANE_FAR" value="1"> + The index value of the projection's far clipping plane. </constant> <constant name="PLANE_LEFT" value="2"> + The index value of the projection's left clipping plane. </constant> <constant name="PLANE_TOP" value="3"> + The index value of the projection's top clipping plane. </constant> <constant name="PLANE_RIGHT" value="4"> + The index value of the projection's right clipping plane. </constant> <constant name="PLANE_BOTTOM" value="5"> + The index value of the projection bottom clipping plane. </constant> <constant name="IDENTITY" value="Projection(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)"> + A [Projection] with no transformation defined. When applied to other data structures, no transformation is performed. </constant> <constant name="ZERO" value="Projection(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)"> + A [Projection] with all values initialized to 0. When applied to other data structures, they will be zeroed. </constant> </constants> <operators> @@ -253,30 +307,38 @@ <return type="bool" /> <param index="0" name="right" type="Projection" /> <description> + Returns [code]true[/code] if the projections are not equal. + [b]Note:[/b] Due to floating-point precision errors, this may return [code]true[/code], even if the projections are virtually equal. An [code]is_equal_approx[/code] method may be added in a future version of Godot. </description> </operator> <operator name="operator *"> <return type="Projection" /> <param index="0" name="right" type="Projection" /> <description> + Returns a [Projection] that applies the combined transformations of this [Projection] and [param right]. </description> </operator> <operator name="operator *"> <return type="Vector4" /> <param index="0" name="right" type="Vector4" /> <description> + Projects (multiplies) the given [Vector4] by this [Projection] matrix. </description> </operator> <operator name="operator =="> <return type="bool" /> <param index="0" name="right" type="Projection" /> <description> + Returns [code]true[/code] if the projections are equal. + [b]Note:[/b] Due to floating-point precision errors, this may return [code]false[/code], even if the projections are virtually equal. An [code]is_equal_approx[/code] method may be added in a future version of Godot. </description> </operator> <operator name="operator []"> <return type="Vector4" /> <param index="0" name="index" type="int" /> <description> + Returns the column of the [Projection] with the given index. + Indices are in the following order: x, y, z, w. </description> </operator> </operators> diff --git a/doc/classes/PropertyTweener.xml b/doc/classes/PropertyTweener.xml index 75d9c919aa..78ead65cdd 100644 --- a/doc/classes/PropertyTweener.xml +++ b/doc/classes/PropertyTweener.xml @@ -27,7 +27,7 @@ Sets a custom initial value to the [PropertyTweener]. Example: [codeblock] var tween = get_tree().create_tween() - tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100) #this will move the node from position (100, 100) to (200, 100) + tween.tween_property(self, "position", Vector2(200, 100), 1).from(Vector2(100, 100)) #this will move the node from position (100, 100) to (200, 100) [/codeblock] </description> </method> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index eb85a4adb4..51c2498ea7 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -1581,6 +1581,10 @@ </constant> <constant name="LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z" value="34" enum="Limit"> </constant> + <constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_X" value="35" enum="Limit"> + </constant> + <constant name="LIMIT_MAX_VIEWPORT_DIMENSIONS_Y" value="36" enum="Limit"> + </constant> <constant name="MEMORY_TEXTURES" value="0" enum="MemoryType"> </constant> <constant name="MEMORY_BUFFERS" value="1" enum="MemoryType"> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6aa9237385..35fe7bdbd2 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -13,6 +13,7 @@ Similarly, in 2D, a canvas is needed to draw all canvas items. In 3D, all visible objects are comprised of a resource and an instance. A resource can be a mesh, a particle system, a light, or any other 3D object. In order to be visible resources must be attached to an instance using [method instance_set_base]. The instance must also be attached to the scenario using [method instance_set_scenario] in order to be visible. In 2D, all visible objects are some form of canvas item. In order to be visible, a canvas item needs to be the child of a canvas attached to a viewport, or it needs to be the child of another canvas item that is eventually attached to the canvas. + [b]Headless mode:[/b] Starting the engine with the [code]--headless[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] disables all rendering and window management functions. Most functions from [RenderingServer] will return dummy values in this case. </description> <tutorials> <link title="Optimization using Servers">$DOCS_URL/tutorials/performance/using_servers.html</link> @@ -4576,9 +4577,11 @@ </constant> <constant name="CANVAS_GROUP_MODE_DISABLED" value="0" enum="CanvasGroupMode"> </constant> - <constant name="CANVAS_GROUP_MODE_OPAQUE" value="1" enum="CanvasGroupMode"> + <constant name="CANVAS_GROUP_MODE_CLIP_ONLY" value="1" enum="CanvasGroupMode"> </constant> - <constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="2" enum="CanvasGroupMode"> + <constant name="CANVAS_GROUP_MODE_CLIP_AND_DRAW" value="2" enum="CanvasGroupMode"> + </constant> + <constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="3" enum="CanvasGroupMode"> </constant> <constant name="CANVAS_LIGHT_MODE_POINT" value="0" enum="CanvasLightMode"> </constant> diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml index 3adf10da2d..b2b8a37e39 100644 --- a/doc/classes/Resource.xml +++ b/doc/classes/Resource.xml @@ -4,7 +4,8 @@ Base class for all resources. </brief_description> <description> - Resource is the base class for all Godot-specific resource types, serving primarily as data containers. Since they inherit from [RefCounted], resources are reference-counted and freed when no longer in use. They are also cached once loaded from disk, so that any further attempts to load a resource from a given path will return the same reference (all this in contrast to a [Node], which is not reference-counted and can be instantiated from disk as many times as desired). Resources can be saved externally on disk or bundled into another object, such as a [Node] or another resource. + Resource is the base class for all Godot-specific resource types, serving primarily as data containers. Since they inherit from [RefCounted], resources are reference-counted and freed when no longer in use. They can also be nested within other resources, and saved on disk. Once loaded from disk, further attempts to load a resource by [member resource_path] returns the same reference. [PackedScene], one of the most common [Object]s in a Godot project, is also a resource, uniquely capable of storing and instantiating the [Node]s it contains as many times as desired. + In GDScript, resources can loaded from disk by their [member resource_path] using [method @GDScript.load] or [method @GDScript.preload]. [b]Note:[/b] In C#, resources will not be freed instantly after they are no longer in use. Instead, garbage collection will run periodically and will free resources that are no longer in use. This means that unused resources will linger on for a while before being removed. </description> <tutorials> @@ -15,77 +16,94 @@ <method name="_get_rid" qualifiers="virtual"> <return type="RID" /> <description> + Override this method to return a custom [RID] when [method get_rid] is called. </description> </method> <method name="duplicate" qualifiers="const"> <return type="Resource" /> <param index="0" name="subresources" type="bool" default="false" /> <description> - Duplicates the resource, returning a new resource with the exported members copied. [b]Note:[/b] To duplicate the resource the constructor is called without arguments. This method will error when the constructor doesn't have default values. - By default, sub-resources are shared between resource copies for efficiency. This can be changed by passing [code]true[/code] to the [param subresources] argument which will copy the subresources. - [b]Note:[/b] If [param subresources] is [code]true[/code], this method will only perform a shallow copy. Nested resources within subresources will not be duplicated and will still be shared. - [b]Note:[/b] When duplicating a resource, only [code]export[/code]ed properties are copied. Other properties will be set to their default value in the new resource. + Duplicates this resource, returning a new resource with its [code]export[/code]ed or [constant PROPERTY_USAGE_STORAGE] properties copied from the original. + If [param subresources] is [code]false[/code], a shallow copy is returned. Nested resources within subresources are not duplicated and are shared from the original resource. This behavior can be overriden by the [constant PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE] flag. + [b]Note:[/b] For custom resources, this method will fail if [method Object._init] has been defined with required parameters. </description> </method> <method name="emit_changed"> <return type="void" /> <description> - Emits the [signal changed] signal. - If external objects which depend on this resource should be updated, this method must be called manually whenever the state of this resource has changed (such as modification of properties). - The method is equivalent to: + Emits the [signal changed] signal. This method is called automatically for built-in resources. + [b]Note:[/b] For custom resources, it's recommended to call this method whenever a meaningful change occurs, such as a modified property. This ensures that custom [Object]s depending on the resource are properly updated. [codeblock] - emit_signal("changed") + var damage: + set(new_value): + if damage != new_value: + damage = new_value + emit_changed() [/codeblock] - [b]Note:[/b] This method is called automatically for built-in resources. </description> </method> <method name="get_local_scene" qualifiers="const"> <return type="Node" /> <description> - If [member resource_local_to_scene] is enabled and the resource was loaded from a [PackedScene] instantiation, returns the local scene where this resource's unique copy is in use. Otherwise, returns [code]null[/code]. + If [member resource_local_to_scene] is set to [code]true[/code] and the resource has been loaded from a [PackedScene] instantiation, returns the root [Node] of the scene where this resource is used. Otherwise, returns [code]null[/code]. </description> </method> <method name="get_rid" qualifiers="const"> <return type="RID" /> <description> - Returns the RID of the resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], etc) are high-level abstractions of resources stored in a server, so this function will return the original RID. + Returns the [RID] of this resource (or an empty RID). Many resources (such as [Texture2D], [Mesh], and so on) are high-level abstractions of resources stored in a specialised server ([DisplayServer], [RenderingServer], etc.), so this function will return the original [RID]. </description> </method> <method name="setup_local_to_scene"> <return type="void" /> <description> - This method is called when a resource with [member resource_local_to_scene] enabled is loaded from a [PackedScene] instantiation. Its behavior can be customized by connecting [signal setup_local_to_scene_requested] from script. - For most resources, this method performs no base logic. [ViewportTexture] performs custom logic to properly set the proxy texture and flags in the local viewport. + Emits the [signal setup_local_to_scene_requested] signal. If [member resource_local_to_scene] is set to [code]true[/code], this method is called from [method PackedScene.instantiate] by the newly duplicated resource within the scene instance. + For most resources, this method performs no logic of its own. Custom behavior can be defined by connecting [signal setup_local_to_scene_requested] from a script, [b]not[/b] by overriding this method. + [b]Example:[/b] Assign a random value to [code]health[/code] for every duplicated Resource from an instantiated scene, excluding the original. + [codeblock] + extends Resource + + var health = 0 + + func _init(): + setup_local_to_scene_requested.connect(randomize_health) + + func randomize_health(): + health = randi_range(10, 40) + [/codeblock] </description> </method> <method name="take_over_path"> <return type="void" /> <param index="0" name="path" type="String" /> <description> - Sets the path of the resource, potentially overriding an existing cache entry for this path. This differs from setting [member resource_path], as the latter would error out if another resource was already cached for the given path. + Sets the [member resource_path] to [param path], potentially overriding an existing cache entry for this path. Further attempts to load an overridden resource by path will instead return this resource. </description> </method> </methods> <members> <member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" default="false"> - If [code]true[/code], the resource will be made unique in each instance of its local scene. It can thus be modified in a scene instance without impacting other instances of that same scene. + If [code]true[/code], the resource is duplicated for each instance of all scenes using it. At run-time, the resource can be modified in one scene without affecting other instances (see [method PackedScene.instantiate]). + [b]Note:[/b] Changing this property at run-time has no effect on already created duplicate resources. </member> <member name="resource_name" type="String" setter="set_name" getter="get_name" default=""""> - The name of the resource. This is an optional identifier. If [member resource_name] is not empty, its value will be displayed to represent the current resource in the editor inspector. For built-in scripts, the [member resource_name] will be displayed as the tab name in the script editor. + An optional name for this resource. When defined, its value is displayed to represent the resource in the Inspector dock. For built-in scripts, the name is displayed as part of the tab name in the script editor. </member> <member name="resource_path" type="String" setter="set_path" getter="get_path" default=""""> - The path to the resource. In case it has its own file, it will return its filepath. If it's tied to the scene, it will return the scene's path, followed by the resource's index. + The unique path to this resource. If it has been saved to disk, the value will be its filepath. If the resource is exclusively contained within a scene, the value will be the [PackedScene]'s filepath, followed by an unique identifier. + [b]Note:[/b] Setting this property manually may fail if a resource with the same path has already been previously loaded. If necessary, use [method take_over_path]. </member> </members> <signals> <signal name="changed"> <description> - Emitted whenever the resource changes. - [b]Note:[/b] This signal is not emitted automatically for custom resources, which means that you need to create a setter and emit the signal yourself. + Emitted when the resource changes, usually when one of its properties is modified. See also [method emit_changed]. + [b]Note:[/b] This signal is not emitted automatically for properties of custom resources. If necessary, a setter needs to be created to emit the signal. </description> </signal> <signal name="setup_local_to_scene_requested"> <description> + Emitted when [method setup_local_to_scene] is called, usually by a newly duplicated resource with [member resource_local_to_scene] set to [code]true[/code]. Custom behavior can be defined by connecting this signal. </description> </signal> </signals> diff --git a/doc/classes/ResourceFormatSaver.xml b/doc/classes/ResourceFormatSaver.xml index 05bfcf3446..1f2af6d157 100644 --- a/doc/classes/ResourceFormatSaver.xml +++ b/doc/classes/ResourceFormatSaver.xml @@ -24,6 +24,15 @@ Returns whether the given resource object can be saved by this saver. </description> </method> + <method name="_recognize_path" qualifiers="virtual const"> + <return type="bool" /> + <param index="0" name="resource" type="Resource" /> + <param index="1" name="path" type="String" /> + <description> + Returns [code]true[/code] if this saver handles a given save path and [code]false[/code] otherwise. + If this method is not implemented, the default behavior returns whether the path's extension is within the ones provided by [method _get_recognized_extensions]. + </description> + </method> <method name="_save" qualifiers="virtual"> <return type="int" /> <param index="0" name="resource" type="Resource" /> diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml index 40ec8ed429..b4b14c01c6 100644 --- a/doc/classes/Script.xml +++ b/doc/classes/Script.xml @@ -4,7 +4,7 @@ A class stored as a resource. </brief_description> <description> - A class stored as a resource. A script extends the functionality of all objects that instance it. + A class stored as a resource. A script extends the functionality of all objects that instantiate it. This is the base class for all scripts and should not be used directly. Trying to create a new script with this class will result in an error. The [code]new[/code] method of a script subclass creates a new instance. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> diff --git a/doc/classes/Shape2D.xml b/doc/classes/Shape2D.xml index 34ca228795..7be0ba64d6 100644 --- a/doc/classes/Shape2D.xml +++ b/doc/classes/Shape2D.xml @@ -66,6 +66,12 @@ Draws a solid shape onto a [CanvasItem] with the [RenderingServer] API filled with the specified [param color]. The exact drawing method is specific for each shape and cannot be configured. </description> </method> + <method name="get_rect" qualifiers="const"> + <return type="Rect2" /> + <description> + Returns a [Rect2] representing the shapes boundary. + </description> + </method> </methods> <members> <member name="custom_solver_bias" type="float" setter="set_custom_solver_bias" getter="get_custom_solver_bias" default="0.0"> diff --git a/doc/classes/SkeletonProfile.xml b/doc/classes/SkeletonProfile.xml index 55d21f3224..57bdd52d9e 100644 --- a/doc/classes/SkeletonProfile.xml +++ b/doc/classes/SkeletonProfile.xml @@ -7,6 +7,7 @@ This resource is used in [EditorScenePostImport]. Some parameters are referring to bones in [Skeleton3D], [Skin], [Animation], and some other nodes are rewritten based on the parameters of [SkeletonProfile]. </description> <tutorials> + <link title="Retargeting 3D Skeletons">$DOCS_URL/tutorials/assets_pipeline/retargeting_3d_skeletons.html</link> </tutorials> <methods> <method name="find_bone" qualifiers="const"> diff --git a/doc/classes/SkeletonProfileHumanoid.xml b/doc/classes/SkeletonProfileHumanoid.xml index 11f0521718..0dbd66d8d6 100644 --- a/doc/classes/SkeletonProfileHumanoid.xml +++ b/doc/classes/SkeletonProfileHumanoid.xml @@ -6,6 +6,7 @@ A [SkeletonProfile] as a preset that is optimized for the human form. This exists for standardization, so all parameters are read-only. </description> <tutorials> + <link title="Retargeting 3D Skeletons">$DOCS_URL/tutorials/assets_pipeline/retargeting_3d_skeletons.html</link> </tutorials> <members> <member name="bone_size" type="int" setter="set_bone_size" getter="get_bone_size" overrides="SkeletonProfile" default="56" /> diff --git a/doc/classes/SpotLight3D.xml b/doc/classes/SpotLight3D.xml index 62cb55daec..28e2ef0d95 100644 --- a/doc/classes/SpotLight3D.xml +++ b/doc/classes/SpotLight3D.xml @@ -14,6 +14,7 @@ <member name="shadow_bias" type="float" setter="set_param" getter="get_param" overrides="Light3D" default="0.03" /> <member name="spot_angle" type="float" setter="set_param" getter="get_param" default="45.0"> The spotlight's angle in degrees. + [b]Note:[/b] [member spot_angle] is not affected by [member Node3D.scale] (the light's scale or its parent's scale). </member> <member name="spot_angle_attenuation" type="float" setter="set_param" getter="get_param" default="1.0"> The spotlight's angular attenuation curve. @@ -23,6 +24,7 @@ </member> <member name="spot_range" type="float" setter="set_param" getter="get_param" default="5.0"> The maximal range that can be reached by the spotlight. Note that the effectively lit area may appear to be smaller depending on the [member spot_attenuation] in use. No matter the [member spot_attenuation] in use, the light will never reach anything outside this range. + [b]Note:[/b] [member spot_range] is not affected by [member Node3D.scale] (the light's scale or its parent's scale). </member> </members> </class> diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml index bd6e5cdfa2..0f6a3168eb 100644 --- a/doc/classes/StandardMaterial3D.xml +++ b/doc/classes/StandardMaterial3D.xml @@ -4,7 +4,7 @@ Physically based rendering (PBR) material that can be applied to 3D objects. </brief_description> <description> - StandardMaterial3D's properties are inherited from [BaseMaterial3D]. + [StandardMaterial3D]'s properties are inherited from [BaseMaterial3D]. [StandardMaterial3D] uses separate textures for ambient occlusion, roughness and metallic maps. To use a single ORM map for all 3 textures, use an [ORMMaterial3D] instead. </description> <tutorials> <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 316bb923b7..320b9fd737 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -364,7 +364,7 @@ <return type="bool" /> <param index="0" name="with_prefix" type="bool" default="false" /> <description> - Returns [code]true[/code] if this string contains a valid hexadecimal number. If [param with_prefix] is [code]true[/code], then a validity of the hexadecimal number is determined by [code]0x[/code] prefix, for instance: [code]0xDEADC0DE[/code]. + Returns [code]true[/code] if this string contains a valid hexadecimal number. If [param with_prefix] is [code]true[/code], then a validity of the hexadecimal number is determined by the [code]0x[/code] prefix, for example: [code]0xDEADC0DE[/code]. </description> </method> <method name="is_valid_html_color" qualifiers="const"> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index ccec691107..d56cc9a31b 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -133,6 +133,7 @@ <description> Generates normals from vertices so you do not have to do it manually. If [param flip] is [code]true[/code], the resulting normals will be inverted. [method generate_normals] should be called [i]after[/i] generating geometry and [i]before[/i] committing the mesh using [method commit] or [method commit_to_arrays]. For correct display of normal-mapped surfaces, you will also have to generate tangents using [method generate_tangents]. [b]Note:[/b] [method generate_normals] only works if the primitive type to be set to [constant Mesh.PRIMITIVE_TRIANGLES]. + [b]Note:[/b] [method generate_normals] takes smooth groups into account. If you don't specify any smooth group for each vertex, [method generate_normals] will smooth normals for you. </description> </method> <method name="generate_tangents"> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index b1fda25ecd..879a355f25 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -70,6 +70,12 @@ Register a new gutter to this [TextEdit]. Use [param at] to have a specific gutter order. A value of [code]-1[/code] appends the gutter to the right. </description> </method> + <method name="add_selection_for_next_occurrence"> + <return type="void" /> + <description> + Adds a selection and a caret for the next occurrence of the current selection. If there is no active selection, selects word under caret. + </description> + </method> <method name="adjust_carets_after_edit"> <return type="void" /> <param index="0" name="caret" type="int" /> diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index 471c1a9040..d1dfdecbd2 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -144,6 +144,7 @@ </methods> <members> <member name="alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="0"> + Sets text alignment within the line as if the line was horizontal. </member> <member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0"> Text writing direction. diff --git a/doc/classes/Texture2D.xml b/doc/classes/Texture2D.xml index 14e89a1b74..3dd8c339b2 100644 --- a/doc/classes/Texture2D.xml +++ b/doc/classes/Texture2D.xml @@ -33,7 +33,7 @@ </method> <method name="_draw_rect_region" qualifiers="virtual const"> <return type="void" /> - <param index="0" name="tp_canvas_item" type="RID" /> + <param index="0" name="to_canvas_item" type="RID" /> <param index="1" name="rect" type="Rect2" /> <param index="2" name="src_rect" type="Rect2" /> <param index="3" name="modulate" type="Color" /> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 348184a16a..01246f0c2e 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -365,7 +365,7 @@ Show or hide the TileMap's collision shapes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show collision debug settings. </member> <member name="navigation_visibility_mode" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0"> - Show or hide the TileMap's collision shapes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. + Show or hide the TileMap's navigation meshes. If set to [constant VISIBILITY_MODE_DEFAULT], this depends on the show navigation debug settings. </member> <member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset"> The assigned [TileSet]. diff --git a/doc/classes/VSlider.xml b/doc/classes/VSlider.xml index b30349e538..488154106f 100644 --- a/doc/classes/VSlider.xml +++ b/doc/classes/VSlider.xml @@ -33,6 +33,7 @@ The background of the area below the grabber. </theme_item> <theme_item name="grabber_area_highlight" data_type="style" type="StyleBox"> + The background of the area below the grabber, to the left of the grabber. </theme_item> <theme_item name="slider" data_type="style" type="StyleBox"> The background for the whole slider. Determines the width of the [code]grabber_area[/code]. diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 81e8dd2260..1a2cdfe10e 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -183,7 +183,7 @@ <method name="is_normalized" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the vector is normalized, [code]false[/code] otherwise. + Returns [code]true[/code] if the vector is [method normalized], [code]false[/code] otherwise. </description> </method> <method name="is_zero_approx" qualifiers="const"> @@ -244,18 +244,22 @@ <method name="normalized" qualifiers="const"> <return type="Vector3" /> <description> - Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code]. + Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code]. See also [method is_normalized]. </description> </method> <method name="octahedron_decode" qualifiers="static"> <return type="Vector3" /> <param index="0" name="uv" type="Vector2" /> <description> + Returns the [Vector3] from an octahedral-compressed form created using [method octahedron_encode] (stored as a [Vector2]). </description> </method> <method name="octahedron_encode" qualifiers="const"> <return type="Vector2" /> <description> + Returns the octahedral-encoded (oct32) form of this [Vector3] as a [Vector2]. Since a [Vector2] occupies 1/3 less memory compared to [Vector3], this form of compression can be used to pass greater amounts of [method normalized] [Vector3]s without increasing storage or memory requirements. See also [method octahedron_decode]. + [b]Note:[/b] [method octahedron_encode] can only be used for [method normalized] vectors. [method octahedron_encode] does [i]not[/i] check whether this [Vector3] is normalized, and will return a value that does not decompress to the original value if the [Vector3] is not normalized. + [b]Note:[/b] Octahedral compression is [i]lossy[/i], although visual differences are rarely perceptible in real world scenarios. </description> </method> <method name="outer" qualifiers="const"> diff --git a/doc/classes/Vector4i.xml b/doc/classes/Vector4i.xml index 3eea93ce1f..e9ac5b9475 100644 --- a/doc/classes/Vector4i.xml +++ b/doc/classes/Vector4i.xml @@ -133,12 +133,20 @@ <return type="Vector4i" /> <param index="0" name="right" type="Vector4i" /> <description> + Gets the remainder of each component of the [Vector4i] with the components of the given [Vector4i]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. + [codeblock] + print(Vector4i(10, -20, 30, -40) % Vector4i(7, 8, 9, 10)) # Prints "(3, -4, 3, 0)" + [/codeblock] </description> </operator> <operator name="operator %"> <return type="Vector4i" /> <param index="0" name="right" type="int" /> <description> + Gets the remainder of each component of the [Vector4i] with the the given [int]. This operation uses truncated division, which is often not desired as it does not work well with negative numbers. Consider using [method @GlobalScope.posmod] instead if you want to handle negative numbers. + [codeblock] + print(Vector4i(10, -20, 30, -40) % 7) # Prints "(3, -6, 2, -5)" + [/codeblock] </description> </operator> <operator name="operator *"> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 998e782975..5706d098e8 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -71,6 +71,12 @@ <description> </description> </method> + <method name="get_screen_transform" qualifiers="const"> + <return type="Transform2D" /> + <description> + Returns the transform from the Viewport's coordinates to the screen coordinates of the containing window manager window. + </description> + </method> <method name="get_texture" qualifiers="const"> <return type="ViewportTexture" /> <description> @@ -300,6 +306,8 @@ If [code]true[/code], the viewport should render its background as transparent. </member> <member name="use_debanding" type="bool" setter="set_use_debanding" getter="is_using_debanding" default="false"> + If [code]true[/code], uses a fast post-processing filter to make banding significantly less visible in 3D. 2D rendering is [i]not[/i] affected by debanding unless the [member Environment.background_mode] is [constant Environment.BG_CANVAS]. See also [member ProjectSettings.rendering/anti_aliasing/quality/use_debanding]. + In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger. </member> <member name="use_occlusion_culling" type="bool" setter="set_use_occlusion_culling" getter="is_using_occlusion_culling" default="false"> If [code]true[/code], [OccluderInstance3D] nodes will be usable for occlusion culling in 3D for this viewport. For the root viewport, [member ProjectSettings.rendering/occlusion_culling/use_occlusion_culling] must be set to [code]true[/code] instead. diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml index 87de664aad..6bd64a50bb 100644 --- a/doc/classes/ViewportTexture.xml +++ b/doc/classes/ViewportTexture.xml @@ -6,6 +6,7 @@ <description> Displays the content of a [Viewport] node as a dynamic [Texture2D]. This can be used to mix controls, 2D, and 3D elements in the same scene. To create a ViewportTexture in code, use the [method Viewport.get_texture] method on the target viewport. + [b]Note:[/b] When local to scene, this texture uses [method Resource.setup_local_to_scene] to set the proxy texture and flags in the local viewport. </description> <tutorials> <link title="GUI in 3D Demo">https://godotengine.org/asset-library/asset/127</link> diff --git a/doc/classes/VisibleOnScreenEnabler2D.xml b/doc/classes/VisibleOnScreenEnabler2D.xml index 50d0c00017..8590e9cc9f 100644 --- a/doc/classes/VisibleOnScreenEnabler2D.xml +++ b/doc/classes/VisibleOnScreenEnabler2D.xml @@ -1,23 +1,30 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisibleOnScreenEnabler2D" inherits="VisibleOnScreenNotifier2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> + Automatically disables another node if not visible on screen. </brief_description> <description> + VisibleOnScreenEnabler2D detects when it is visible on screen (just like [VisibleOnScreenNotifier2D]) and automatically enables or disables the target node. The target node is disabled when [VisibleOnScreenEnabler2D] is not visible on screen (including when [member CanvasItem.visible] is [code]false[/code]), and enabled when the enabler is visible. The disabling is achieved by changing [member Node.process_mode]. </description> <tutorials> </tutorials> <members> <member name="enable_mode" type="int" setter="set_enable_mode" getter="get_enable_mode" enum="VisibleOnScreenEnabler2D.EnableMode" default="0"> + Determines how the node is enabled. Corresponds to [enum Node.ProcessMode]. Disabled node uses [constant Node.PROCESS_MODE_DISABLED]. </member> <member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath("..")"> + The path to the target node, relative to the [VisibleOnScreenEnabler2D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler2D] is inside scene tree) and every time the [VisibleOnScreenEnabler2D] enters the scene tree. If the path is invalid, nothing will happen. </member> </members> <constants> <constant name="ENABLE_MODE_INHERIT" value="0" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_INHERIT]. </constant> <constant name="ENABLE_MODE_ALWAYS" value="1" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_ALWAYS]. </constant> <constant name="ENABLE_MODE_WHEN_PAUSED" value="2" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_WHEN_PAUSED. </constant> </constants> </class> diff --git a/doc/classes/VisualInstance3D.xml b/doc/classes/VisualInstance3D.xml index 9574686506..94e8c25660 100644 --- a/doc/classes/VisualInstance3D.xml +++ b/doc/classes/VisualInstance3D.xml @@ -17,7 +17,7 @@ <method name="get_aabb" qualifiers="const"> <return type="AABB" /> <description> - Returns the [AABB] (also known as the bounding box) for this [VisualInstance3D]. See also [method get_transformed_aabb]. + Returns the [AABB] (also known as the bounding box) for this [VisualInstance3D]. </description> </method> <method name="get_base" qualifiers="const"> @@ -39,13 +39,6 @@ Returns whether or not the specified layer of the [member layers] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> - <method name="get_transformed_aabb" qualifiers="const"> - <return type="AABB" /> - <description> - Returns the transformed [AABB] (also known as the bounding box) for this [VisualInstance3D]. - Transformed in this case means the [AABB] plus the position, rotation, and scale of the [Node3D]'s [Transform3D]. See also [method get_aabb]. - </description> - </method> <method name="set_base"> <return type="void" /> <param index="0" name="base" type="RID" /> diff --git a/doc/classes/XRPose.xml b/doc/classes/XRPose.xml index 0e58fab9b3..31a484ea40 100644 --- a/doc/classes/XRPose.xml +++ b/doc/classes/XRPose.xml @@ -30,7 +30,7 @@ <member name="name" type="StringName" setter="set_name" getter="get_name" default="&"""> The name of this pose. Pose names are often driven by an action map setup by the user. Godot does suggest a number of pose names that it expects [XRInterface]s to implement: - [code]root[/code] defines a root location, often used for tracked objects that do not have further nodes. - - [code]aim[/code] defines the tip of a controller with the orientation pointing outwards, for instance: add your raycasts to this. + - [code]aim[/code] defines the tip of a controller with the orientation pointing outwards, for example: add your raycasts to this. - [code]grip[/code] defines the location where the user grips the controller - [code]skeleton[/code] defines the root location a hand mesh should be placed when using hand tracking and the animated skeleton supplied by the XR runtime. </member> @@ -46,7 +46,7 @@ No tracking information is available for this pose. </constant> <constant name="XR_TRACKING_CONFIDENCE_LOW" value="1" enum="TrackingConfidence"> - Tracking information may be inaccurate or estimated. For instance with inside out tracking this would indicate a controller may be (partially) obscured. + Tracking information may be inaccurate or estimated. For example, with inside out tracking this would indicate a controller may be (partially) obscured. </constant> <constant name="XR_TRACKING_CONFIDENCE_HIGH" value="2" enum="TrackingConfidence"> Tracking information is deemed accurate and up to date. diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml index 48b00323d3..d940ea41ac 100644 --- a/doc/classes/XRServer.xml +++ b/doc/classes/XRServer.xml @@ -30,18 +30,18 @@ <param index="1" name="keep_height" type="bool" /> <description> This is an important function to understand correctly. AR and VR platforms all handle positioning slightly differently. - For platforms that do not offer spatial tracking, our origin point (0,0,0) is the location of our HMD, but you have little control over the direction the player is facing in the real world. + For platforms that do not offer spatial tracking, our origin point (0, 0, 0) is the location of our HMD, but you have little control over the direction the player is facing in the real world. For platforms that do offer spatial tracking, our origin point depends very much on the system. For OpenVR, our origin point is usually the center of the tracking space, on the ground. For other platforms, it's often the location of the tracking camera. This method allows you to center your tracker on the location of the HMD. It will take the current location of the HMD and use that to adjust all your tracking data; in essence, realigning the real world to your player's current position in the game world. For this method to produce usable results, tracking information must be available. This often takes a few frames after starting your game. - You should call this method after a few seconds have passed. For instance, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism. + You should call this method after a few seconds have passed. For example, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism. </description> </method> <method name="find_interface" qualifiers="const"> <return type="XRInterface" /> <param index="0" name="name" type="String" /> <description> - Finds an interface by its [param name]. For instance, if your project uses capabilities of an AR/VR platform, you can find the interface for that platform by name and initialize it. + Finds an interface by its [param name]. For example, if your project uses capabilities of an AR/VR platform, you can find the interface for that platform by name and initialize it. </description> </method> <method name="get_hmd_transform"> diff --git a/doc/classes/float.xml b/doc/classes/float.xml index 9d685b9cd0..d7232bb0e9 100644 --- a/doc/classes/float.xml +++ b/doc/classes/float.xml @@ -194,7 +194,7 @@ <return type="bool" /> <param index="0" name="right" type="float" /> <description> - Returns [code]true[/code] the left float is less than the right one. + Returns [code]true[/code] if the left float is less than the right one. </description> </operator> <operator name="operator <"> @@ -208,7 +208,7 @@ <return type="bool" /> <param index="0" name="right" type="float" /> <description> - Returns [code]true[/code] the left integer is less than or equal to the right one. + Returns [code]true[/code] if the left float is less than or equal to the right one. </description> </operator> <operator name="operator <="> @@ -237,7 +237,7 @@ <return type="bool" /> <param index="0" name="right" type="float" /> <description> - Returns [code]true[/code] the left float is greater than the right one. + Returns [code]true[/code] if the left float is greater than the right one. </description> </operator> <operator name="operator >"> @@ -251,7 +251,7 @@ <return type="bool" /> <param index="0" name="right" type="float" /> <description> - Returns [code]true[/code] the left float is greater than or equal to the right one. + Returns [code]true[/code] if the left float is greater than or equal to the right one. </description> </operator> <operator name="operator >="> diff --git a/doc/classes/int.xml b/doc/classes/int.xml index 78e2e7d18f..868a8e9944 100644 --- a/doc/classes/int.xml +++ b/doc/classes/int.xml @@ -72,14 +72,14 @@ <return type="bool" /> <param index="0" name="right" type="float" /> <description> - Returns [code]true[/code] if operands are different from each other. + Returns [code]true[/code] if this [int] is not equivalent to the given [float]. </description> </operator> <operator name="operator !="> <return type="bool" /> <param index="0" name="right" type="int" /> <description> - Returns [code]true[/code] if operands are different from each other. + Returns [code]true[/code] if the integers are not equal. </description> </operator> <operator name="operator %"> @@ -262,7 +262,7 @@ <return type="bool" /> <param index="0" name="right" type="int" /> <description> - Returns [code]true[/code] the left integer is less than the right one. + Returns [code]true[/code] if the left integer is less than the right one. </description> </operator> <operator name="operator <<"> @@ -287,7 +287,7 @@ <return type="bool" /> <param index="0" name="right" type="int" /> <description> - Returns [code]true[/code] the left integer is less than or equal to the right one. + Returns [code]true[/code] if the left integer is less than or equal to the right one. </description> </operator> <operator name="operator =="> @@ -315,7 +315,7 @@ <return type="bool" /> <param index="0" name="right" type="int" /> <description> - Returns [code]true[/code] the left integer is greater than the right one. + Returns [code]true[/code] if the left integer is greater than the right one. </description> </operator> <operator name="operator >="> @@ -329,7 +329,7 @@ <return type="bool" /> <param index="0" name="right" type="int" /> <description> - Returns [code]true[/code] the left integer is greater than or equal to the right one. + Returns [code]true[/code] if the left integer is greater than or equal to the right one. </description> </operator> <operator name="operator >>"> diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 65bb98d29e..5d0edda6c5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -130,7 +130,10 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ if (syncStatus == GL_UNSIGNALED) { // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { +#ifndef WEB_ENABLED + // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms +#endif } else { // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use // Allocate a new buffer and use that. @@ -433,10 +436,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; - Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; - - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { + Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -544,9 +549,18 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou } RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - - if (material.is_null() && ci->canvas_group != nullptr) { - material = default_canvas_group_material; + if (ci->canvas_group != nullptr) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + if (!p_to_backbuffer) { + material = default_clip_children_material; + } + } else { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } } GLES3::CanvasShaderData *shader_data_cache = nullptr; @@ -1010,7 +1024,7 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran _add_to_batch(r_index, r_batch_broken); if (primitive->point_count == 4) { - // Reset base data + // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; @@ -1018,12 +1032,13 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Tran state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config for (uint32_t j = 0; j < 3; j++) { - //second half of triangle - state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; - state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; - state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + offset].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + offset].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j + offset] * base_color; state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } @@ -2074,6 +2089,26 @@ void fragment() { material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); } + { + default_clip_children_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_clip_children_shader); + + material_storage->shader_set_code(default_clip_children_shader, R"( +// Default clip children shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + COLOR.rgb = c.rgb; +} +)"); + default_clip_children_material = material_storage->material_allocate(); + material_storage->material_initialize(default_clip_children_material); + + material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); + } + default_canvas_texture = texture_storage->canvas_texture_allocate(); texture_storage->canvas_texture_initialize(default_canvas_texture); @@ -2086,6 +2121,8 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version); material_storage->material_free(default_canvas_group_material); material_storage->shader_free(default_canvas_group_shader); + material_storage->material_free(default_clip_children_material); + material_storage->shader_free(default_clip_children_shader); singleton = nullptr; glDeleteBuffers(1, &data.canvas_quad_vertices); diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 65e5f14838..aee2782b62 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -284,6 +284,8 @@ public: RID default_canvas_texture; RID default_canvas_group_material; RID default_canvas_group_shader; + RID default_clip_children_material; + RID default_clip_children_shader; typedef void Texture; diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 0836a21254..ea97dff522 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -274,15 +274,32 @@ RasterizerGLES3::~RasterizerGLES3() { void RasterizerGLES3::prepare_for_blitting_render_targets() { } -void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { +void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) { GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target); + ERR_FAIL_COND(!rt); - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + GLuint read_fbo = 0; + if (rt->view_count > 1) { + glGenFramebuffers(1, &read_fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer); + } else { + glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); + } + glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); // Flip content upside down to correct for coordinates. - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + Vector2i screen_rect_end = p_screen_rect.get_end(); + glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, + p_screen_rect.position.x, screen_rect_end.y, screen_rect_end.x, p_screen_rect.position.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (read_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &read_fbo); + } } // is this p_screen useless in a multi window environment? @@ -293,7 +310,7 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc RID rid_rt = blit.render_target; Rect2 dst_rect = blit.dst_rect; - _blit_render_target_to_screen(rid_rt, p_screen, dst_rect); + _blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0); } } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 431515fd0d..d7d26685b4 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -68,7 +68,7 @@ protected: RasterizerCanvasGLES3 *canvas = nullptr; RasterizerSceneGLES3 *scene = nullptr; - void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect); + void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer); public: RendererUtilities *get_utilities() { return utilities; } diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index d6486801e7..6e9d4e5963 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -1189,8 +1189,8 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const distance = 1.0; } - uint32_t indices; - surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices); + uint32_t indices = 0; + surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices); /* if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); @@ -1273,6 +1273,19 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix); GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix); + if (p_render_data->view_count > 1) { + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + projection = correction * p_render_data->view_projection[v]; + GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_ubo.projection_matrix_view[v]); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_ubo.inv_projection_matrix_view[v]); + + scene_state.multiview_ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x; + scene_state.multiview_ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y; + scene_state.multiview_ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z; + scene_state.multiview_ubo.eye_offset[v][3] = 0.0; + } + } + scene_state.ubo.directional_light_count = p_render_data->directional_light_count; scene_state.ubo.z_far = p_render_data->z_far; @@ -1374,6 +1387,15 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); + + if (p_render_data->view_count > 1) { + if (scene_state.multiview_buffer == 0) { + glGenBuffers(1, &scene_state.multiview_buffer); + } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } } // Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer @@ -1916,8 +1938,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLES3::SceneShaderData *prev_shader = nullptr; GeometryInstanceGLES3 *prev_inst = nullptr; SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR; + SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized - SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized. + // @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it. + uint32_t base_spec_constants = 0; + + if (p_render_data->view_count > 1) { + base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW; + } switch (p_pass_mode) { case PASS_MODE_COLOR: @@ -1957,8 +1985,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, continue; } - //uint32_t base_spec_constants = p_params->spec_constant_base_flags; - GLES3::SceneShaderData *shader; GLES3::SceneMaterialData *material_data; void *mesh_surface; @@ -2128,7 +2154,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } if (prev_shader != shader || prev_variant != instance_variant) { - material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant); + material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants); float opaque_prepass_threshold = 0.0; if constexpr (p_pass_mode == PASS_MODE_DEPTH) { opaque_prepass_threshold = 0.99; @@ -2136,7 +2162,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, opaque_prepass_threshold = 0.1; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, base_spec_constants); prev_shader = shader; prev_variant = instance_variant; @@ -2144,21 +2170,21 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) { // Rebind the light indices. - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant); - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant, base_spec_constants); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant, base_spec_constants); if (inst->omni_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); } if (inst->spot_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); } prev_inst = inst; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants); if (inst->instance_count > 0) { // Using MultiMesh. // Bind instance buffers. diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 3895620228..3a759425e2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -74,6 +74,7 @@ enum SceneUniformLocation { SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SCENE_MULTIVIEW_UNIFORM_LOCATION, }; enum SkyUniformLocation { @@ -90,6 +91,8 @@ enum { SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, SPEC_CONSTANT_DISABLE_FOG = 4, + SPEC_CONSTANT_USE_RADIANCE_MAP = 5, + SPEC_CONSTANT_USE_MULTIVIEW = 6, }; struct RenderDataGLES3 { @@ -343,6 +346,13 @@ private: }; static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + struct MultiviewUBO { + float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4]; + }; + static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes"); + struct TonemapUBO { float exposure = 1.0; float white = 1.0; @@ -353,6 +363,8 @@ private: UBO ubo; GLuint ubo_buffer = 0; + MultiviewUBO multiview_ubo; + GLuint multiview_buffer = 0; GLuint tonemap_buffer = 0; bool used_depth_prepass = false; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 033f10dbc5..2ff7f72180 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -142,7 +142,7 @@ RID ShaderGLES3::version_create() { return version_owner.make_rid(version); } -void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) { +void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) { #ifdef GLES_OVER_GL builder.append("#version 330\n"); builder.append("#define USE_GLES_OVER_GL\n"); @@ -171,6 +171,24 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant } builder.append("\n"); //make sure defines begin at newline + // Insert multiview extension loading, because it needs to appear before + // any non-preprocessor code (like the "precision highp..." lines below). + builder.append("#ifdef USE_MULTIVIEW\n"); + builder.append("#if defined(GL_OVR_multiview2)\n"); + builder.append("#extension GL_OVR_multiview2 : require\n"); + builder.append("#elif defined(GL_OVR_multiview)\n"); + builder.append("#extension GL_OVR_multiview : require\n"); + builder.append("#endif\n"); + if (p_stage_type == StageType::STAGE_TYPE_VERTEX) { + builder.append("layout(num_views=2) in;\n"); + } + builder.append("#define ViewIndex gl_ViewID_OVR\n"); + builder.append("#define MAX_VIEWS 2\n"); + builder.append("#else\n"); + builder.append("#define ViewIndex 0\n"); + builder.append("#define MAX_VIEWS 1\n"); + builder.append("#endif\n"); + // Default to highp precision unless specified otherwise. builder.append("precision highp float;\n"); builder.append("precision highp int;\n"); @@ -180,8 +198,9 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("precision highp sampler2DArray;\n"); #endif - for (uint32_t i = 0; i < p_template.chunks.size(); i++) { - const StageTemplate::Chunk &chunk = p_template.chunks[i]; + const StageTemplate &stage_template = stage_templates[p_stage_type]; + for (uint32_t i = 0; i < stage_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = stage_template.chunks[i]; switch (chunk.type) { case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) @@ -224,7 +243,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //vertex stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization); spec.vert_id = glCreateShader(GL_VERTEX_SHADER); String builder_string = builder.as_string(); @@ -272,7 +291,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //fragment stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization); spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER); String builder_string = builder.as_string(); @@ -413,7 +432,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "vertex"; @@ -425,7 +444,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver //fragment stage { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "fragment"; diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 2b72549b5b..760b5e5ddb 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -153,7 +153,7 @@ private: StageTemplate stage_templates[STAGE_TYPE_MAX]; - void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization); + void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization); void _add_stage(const char *p_code, StageType p_stage_type); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 22a11cdc29..23db41802e 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -211,7 +211,9 @@ void main() { #include "canvas_uniforms_inc.glsl" #include "stdlib_inc.glsl" +#ifndef DISABLE_LIGHTING uniform sampler2D atlas_texture; //texunit:-2 +#endif // DISABLE_LIGHTING //uniform sampler2D shadow_atlas_texture; //texunit:-3 uniform sampler2D screen_texture; //texunit:-4 uniform sampler2D sdf_texture; //texunit:-5 diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index 43d275205f..dd5ebecb1a 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -82,6 +82,7 @@ layout(std140) uniform CanvasData { //ubo:0 uint pad2; }; +#ifndef DISABLE_LIGHTING #define LIGHT_FLAGS_BLEND_MASK uint(3 << 16) #define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16) #define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16) @@ -114,7 +115,7 @@ struct Light { layout(std140) uniform LightData { //ubo:2 Light light_array[MAX_LIGHTS]; }; - +#endif // DISABLE_LIGHTING layout(std140) uniform DrawDataInstances { //ubo:3 DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index efd6036ba9..ed176c7829 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -16,6 +16,7 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_MULTIVIEW = false #[vertex] @@ -153,6 +154,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + uniform highp mat4 world_transform; #ifdef USE_LIGHTMAP @@ -250,8 +260,14 @@ void main() { #if defined(OVERRIDE_POSITION) highp vec4 position; #endif - highp mat4 projection_matrix = scene_data.projection_matrix; - highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix; + +#ifdef USE_MULTIVIEW + mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; +#else + mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; +#endif //USE_MULTIVIEW #ifdef USE_INSTANCING vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w)); @@ -339,7 +355,6 @@ void main() { /* clang-format off */ #[fragment] - // Default to SPECULAR_SCHLICK_GGX. #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON) #define SPECULAR_SCHLICK_GGX @@ -463,6 +478,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + /* clang-format off */ #GLOBALS @@ -530,8 +554,13 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) -uniform highp sampler2D screen_texture; // texunit:-5 +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray depth_buffer; // texunit:-6 +uniform highp sampler2DArray screen_texture; // texunit:-5 +#else uniform highp sampler2D depth_buffer; // texunit:-6 +uniform highp sampler2D screen_texture; // texunit:-5 +#endif uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; @@ -884,7 +913,11 @@ vec4 fog_process(vec3 vertex) { void main() { //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; +#ifdef USE_MULTIVIEW + vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz); +#else vec3 view = -normalize(vertex_interp); +#endif vec3 albedo = vec3(1.0); vec3 backlight = vec3(0.0); vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0); @@ -1096,9 +1129,15 @@ void main() { #if defined(CUSTOM_IRRADIANCE_USED) ambient_light = mix(ambient_light, custom_irradiance.rgb, custom_irradiance.a); #endif // CUSTOM_IRRADIANCE_USED - ambient_light *= albedo.rgb; - ambient_light *= ao; + { +#if defined(AMBIENT_LIGHT_DISABLED) + ambient_light = vec3(0.0, 0.0, 0.0); +#else + ambient_light *= albedo.rgb; + ambient_light *= ao; +#endif // AMBIENT_LIGHT_DISABLED + } // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index a478cf9170..0b769e77f2 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -44,7 +44,11 @@ in vec2 uv_interp; layout(location = 0) out vec4 frag_color; +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray source; //texunit:0 +#else uniform highp sampler2D source; //texunit:0 +#endif #if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) #define USING_GLOW // only use glow when at least one glow level is selected @@ -191,10 +195,17 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { const float FXAA_REDUCE_MUL = (1.0 / 8.0); const float FXAA_SPAN_MAX = 8.0; +#ifdef USE_MULTIVIEW + vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; +#else vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; +#endif vec3 rgbM = color; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); @@ -219,8 +230,13 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { dir * rcpDirMin)) * pixel_size; +#ifdef USE_MULTIVIEW + vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); +#else vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); +#endif float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) { @@ -231,7 +247,11 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { } void main() { +#ifdef USE_MULTIVIEW + vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0); +#else vec4 color = textureLod(source, uv_interp, 0.0); +#endif #ifdef USE_FXAA color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 242c1ce0a9..943df3b156 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -34,6 +34,15 @@ #include "core/config/project_settings.h" #include "core/templates/vector.h" +#ifdef ANDROID_ENABLED +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <GLES3/gl3platform.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + using namespace GLES3; #define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF @@ -85,6 +94,7 @@ Config::Config() { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &max_viewport_size); glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment); @@ -98,6 +108,16 @@ Config::Config() { anisotropic_level = MIN(float(1 << int(ProjectSettings::get_singleton()->get("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); } + multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#ifdef ANDROID_ENABLED + if (multiview_supported) { + eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); + if (eglFramebufferTextureMultiviewOVR == nullptr) { + multiview_supported = false; + } + } +#endif + force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index fe18345775..d4b38acd18 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -44,6 +44,10 @@ #include OPENGL_INCLUDE_H #endif +#ifdef ANDROID_ENABLED +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +#endif + namespace GLES3 { class Config { @@ -60,6 +64,7 @@ public: int max_texture_image_units = 0; int max_texture_size = 0; int max_uniform_buffer_size = 0; + int max_viewport_size = 0; int max_renderable_elements = 0; int max_renderable_lights = 0; int max_lights_per_object = 0; @@ -82,6 +87,11 @@ public: bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + bool multiview_supported = false; +#ifdef ANDROID_ENABLED + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; +#endif + static Config *get_singleton() { return singleton; }; Config(); diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index f5241e33ab..724ad57d64 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -89,7 +89,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = v & 1 ? 1 : 0; gui[1] = v & 2 ? 1 : 0; } @@ -116,7 +116,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -145,7 +145,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy } } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -728,7 +728,7 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; - *gui = value[0].boolean ? 1 : 0; + gui[0] = value[0].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC2: { uint32_t *gui = (uint32_t *)data; @@ -2314,7 +2314,7 @@ void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) { global_shader_uniforms.instance_buffer_pos.erase(p_instance); } -void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) { +void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) { if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) { return; //just not allocated, ignore } @@ -2324,7 +2324,9 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i return; //again, not allocated, ignore } ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); - ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + + Variant::Type value_type = p_value.get_type(); + ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { ShaderLanguage::TYPE_MAX, //nil @@ -2350,9 +2352,24 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i ShaderLanguage::TYPE_VEC4 //color }; - ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()]; + ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX; + if (value_type == Variant::INT && p_flags_count > 0) { + switch (p_flags_count) { + case 1: + datatype = ShaderLanguage::TYPE_BVEC2; + break; + case 2: + datatype = ShaderLanguage::TYPE_BVEC3; + break; + case 3: + datatype = ShaderLanguage::TYPE_BVEC4; + break; + } + } else { + datatype = datatype_from_value[value_type]; + } - ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported pos += p_index; diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 6504c7748c..24d9a0fee1 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -530,7 +530,7 @@ public: virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override; virtual void global_shader_parameters_instance_free(RID p_instance) override; - virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override; GLuint global_shader_parameters_get_uniform_buffer() const; diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 22d84eba93..11ce31856d 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -309,12 +309,48 @@ RS::BlendShapeMode MeshStorage::mesh_get_blend_shape_mode(RID p_mesh) const { } void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->vertex_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->attribute_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->attribute_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->skin_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->skin_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 74f5800795..a31db24f2d 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -325,13 +325,12 @@ public: return s->index_count ? s->index_count : s->vertex_count; } - _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const { + _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); int32_t current_lod = -1; - if (r_index_count) { - *r_index_count = s->index_count; - } + r_index_count = s->index_count; + for (uint32_t i = 0; i < s->lod_count; i++) { float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold; if (screen_size > p_mesh_lod_threshold) { @@ -342,9 +341,7 @@ public: if (current_lod == -1) { return 0; } else { - if (r_index_count) { - *r_index_count = s->lods[current_lod].index_count; - } + r_index_count = s->lods[current_lod].index_count; return current_lod + 1; } } diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 9123984dc7..b8e4530f56 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -33,12 +33,17 @@ #include "render_scene_buffers_gles3.h" #include "texture_storage.h" +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#endif + RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); //internal_size.x = p_internal_size.x; // ignore for now //internal_size.y = p_internal_size.y; @@ -50,7 +55,7 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte //msaa = p_msaa; //screen_space_aa = p_screen_space_aa; //use_debanding = p_use_debanding; - //view_count = p_view_count; + view_count = p_view_count; free_render_buffer_data(); @@ -62,24 +67,43 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - glBindTexture(GL_TEXTURE_2D, rt->color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + if (view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, view_count); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + } glGenTextures(1, &depth_texture); - glBindTexture(GL_TEXTURE_2D, depth_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, depth_texture); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_2D, depth_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); + if (view_count > 1 && config->multiview_supported) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0, 0, view_count); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); + } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); if (status != GL_FRAMEBUFFER_COMPLETE) { diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index ad0d2032b0..dbedbd22c3 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -56,7 +56,7 @@ public: RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; //bool use_debanding = false; - //uint32_t view_count = 1; + uint32_t view_count = 1; bool is_transparent = false; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 48f460f995..524ff14cc9 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -34,6 +34,10 @@ #include "config.h" #include "drivers/gles3/effects/copy_effects.h" +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#endif + using namespace GLES3; TextureStorage *TextureStorage::singleton = nullptr; @@ -59,9 +63,7 @@ TextureStorage::TextureStorage() { { //create default textures { // White Textures - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1, 1, 1, 1)); image->generate_mipmaps(); @@ -90,9 +92,7 @@ TextureStorage::TextureStorage() { } { // black - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 1)); image->generate_mipmaps(); @@ -116,9 +116,7 @@ TextureStorage::TextureStorage() { } { // transparent black - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 0)); image->generate_mipmaps(); @@ -127,9 +125,7 @@ TextureStorage::TextureStorage() { } { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0.5, 0.5, 1, 1)); image->generate_mipmaps(); @@ -138,9 +134,7 @@ TextureStorage::TextureStorage() { } { - Ref<Image> image; - image.instantiate(); - image->create(4, 4, true, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(1.0, 0.5, 1, 1)); image->generate_mipmaps(); @@ -730,8 +724,7 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { } void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) { - // only 1 layer so far - texture_set_data(p_texture, p_image); + texture_set_data(p_texture, p_image, p_layer); #ifdef TOOLS_ENABLED Texture *tex = texture_owner.get_or_null(p_texture); @@ -745,9 +738,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); @@ -756,9 +747,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -777,9 +766,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, Re void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -833,9 +820,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); + Ref<Image> image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (texture->format != texture->real_format) { image->convert(texture->format); @@ -1030,7 +1015,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, img->resize_to_po2(false); } - GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D; + GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target; Vector<uint8_t> read = img->get_data(); @@ -1087,7 +1072,11 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]); } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + if (texture->target == GL_TEXTURE_2D_ARRAY) { + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 0, format, type, &read[ofs]); + } else { + glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]); + } } tsize += size; @@ -1443,6 +1432,8 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { return; } + Config *config = Config::get_singleton(); + rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2; rt->color_format = GL_RGBA; rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; @@ -1464,17 +1455,29 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { // color glGenTextures(1, &rt->color); - glBindTexture(GL_TEXTURE_2D, rt->color); - - glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + if (rt->view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + if (rt->view_count > 1 && config->multiview_supported) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -1493,8 +1496,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { texture->format = rt->image_format; texture->real_format = rt->image_format; - texture->type = Texture::TYPE_2D; - texture->target = GL_TEXTURE_2D; + if (rt->view_count > 1 && config->multiview_supported) { + texture->type = Texture::TYPE_LAYERED; + texture->target = GL_TEXTURE_2D_ARRAY; + texture->layers = rt->view_count; + } else { + texture->type = Texture::TYPE_2D; + texture->target = GL_TEXTURE_2D; + texture->layers = 1; + } texture->gl_format_cache = rt->color_format; texture->gl_type_cache = GL_UNSIGNED_BYTE; texture->gl_internal_format_cache = rt->color_internal_format; @@ -1637,13 +1647,14 @@ void TextureStorage::render_target_set_size(RID p_render_target, int p_width, in RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); - if (p_width == rt->size.x && p_height == rt->size.y) { + if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) { return; } _clear_render_target(rt); rt->size = Size2i(p_width, p_height); + rt->view_count = p_view_count; _update_render_target(rt); } diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 39a74236e5..dbe39428ac 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -324,6 +324,7 @@ private: struct RenderTarget { Point2i position = Point2i(0, 0); Size2i size = Size2i(0, 0); + uint32_t view_count = 1; int mipmap_count = 1; RID self; GLuint fbo = 0; diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 6e91f38050..0a04f0d4d0 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -356,4 +356,13 @@ String Utilities::get_video_adapter_api_version() const { return (const char *)glGetString(GL_VERSION); } +Size2i Utilities::get_maximum_viewport_size() const { + Config *config = Config::get_singleton(); + if (!config) { + return Size2i(); + } + + return Size2i(config->max_viewport_size, config->max_viewport_size); +} + #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index e054f2f816..936ac35cc9 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -150,6 +150,8 @@ public: virtual String get_video_adapter_vendor() const override; virtual RenderingDevice::DeviceType get_video_adapter_type() const override; virtual String get_video_adapter_api_version() const override; + + virtual Size2i get_maximum_viewport_size() const override; }; } // namespace GLES3 diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index bc4bb3782b..79641464d8 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -119,7 +119,7 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); //print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height)); - p_image->create(png_img.width, png_img.height, false, dest_format, buffer); + p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer); return OK; } diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 72c9a80a94..de29363556 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -9686,6 +9686,10 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const { return limits.maxComputeWorkGroupSize[1]; case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: + return limits.maxViewportDimensions[0]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y: + return limits.maxViewportDimensions[1]; case LIMIT_SUBGROUP_SIZE: { VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); return subgroup_capabilities.size; diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index f51fd2a6cf..9ee5a67471 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -717,9 +717,12 @@ Error VulkanContext::_check_capabilities() { VkPhysicalDeviceProperties2 physicalDeviceProperties{}; void *nextptr = nullptr; - subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nextptr; - nextptr = &subgroupProperties; + if (!(vulkan_major == 1 && vulkan_minor == 0)) { + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nextptr; + + nextptr = &subgroupProperties; + } if (multiview_capabilities.is_supported) { multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; @@ -1807,18 +1810,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set. VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - }; - for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { - if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { - compositeAlpha = compositeAlphaFlags[i]; - break; + + if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { + // Find a supported composite alpha mode - one of these is guaranteed to be set. + VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + }; + + for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { + if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { + compositeAlpha = compositeAlphaFlags[i]; + break; + } } } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 5c4ef35f1c..8a2d23d32d 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -6204,7 +6204,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { if (do_bake && !animation->track_is_compressed(i)) { Animation::InterpolationType it = animation->track_get_interpolation_type(i); if (it == Animation::INTERPOLATION_NEAREST) { - continue; // Nearest and Angle interpolation cannot be baked. + continue; // Nearest interpolation cannot be baked. } // Special case for angle interpolation. diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index a882275375..dfa2b8e70f 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -308,9 +308,7 @@ void EditorProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 8e7135f1c5..8552c8e9ef 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -298,9 +298,7 @@ void EditorVisualProfiler::_update_plot() { } } - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, graph_image); + Ref<Image> img = Image::create_from_data(w, h, false, Image::FORMAT_RGBA8, graph_image); if (reset_texture) { if (graph_texture.is_null()) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index bf50efc4f9..8ee82888f3 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1618,7 +1618,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { // which would result in an invalid texture. if (c3d == 0 && c2d == 0) { img.instantiate(); - img->create(1, 1, false, Image::FORMAT_RGB8); + img->initialize_data(1, 1, false, Image::FORMAT_RGB8); } else if (c3d < c2d) { Ref<ViewportTexture> viewport_texture = scene_root->get_texture(); if (viewport_texture->get_width() > 0 && viewport_texture->get_height() > 0) { diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 332e47dc52..899fa69be1 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -90,7 +90,9 @@ void EditorPropertyText::update_property() { String s = get_edited_object()->get(get_edited_property()); updating = true; if (text->get_text() != s) { + int caret = text->get_caret_column(); text->set_text(s); + text->set_caret_column(caret); } text->set_editable(!is_read_only()); updating = false; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index d98ad117d7..7cc42f4d24 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -510,7 +510,7 @@ void EditorPropertyArray::_notification(int p_what) { change_type->add_separator(); change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX); - if (Object::cast_to<Button>(button_add_item)) { + if (button_add_item) { button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } } break; @@ -1205,7 +1205,7 @@ void EditorPropertyDictionary::_notification(int p_what) { change_type->add_separator(); change_type->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Remove Item"), Variant::VARIANT_MAX); - if (Object::cast_to<Button>(button_add_item)) { + if (button_add_item) { button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } } break; @@ -1431,7 +1431,7 @@ void EditorPropertyLocalizableString::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { - if (Object::cast_to<Button>(button_add_item)) { + if (button_add_item) { button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } } break; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 01816b7205..5545dc4ee2 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -958,6 +958,7 @@ EditorResourcePicker::EditorResourcePicker(bool p_hide_assign_button_controls) { preview_rect->set_offset(SIDE_TOP, 1); preview_rect->set_offset(SIDE_BOTTOM, -1); preview_rect->set_offset(SIDE_RIGHT, -1); + preview_rect->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); assign_button->add_child(preview_rect); } diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index 14e482432a..e4c35a5b81 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -59,8 +59,9 @@ void EventListenerLineEdit::gui_input(const Ref<InputEvent> &p_event) { // First event will be an event which is used to focus this control - i.e. a mouse click, or a tab press. // Ignore the first one so that clicking into the LineEdit does not override the current event. // Ignore is reset to true when the control is unfocused. - if (ignore) { - ignore = false; + // This class also specially handles grab_focus() calls. + if (ignore_next_event) { + ignore_next_event = false; return; } @@ -85,7 +86,7 @@ void EventListenerLineEdit::_on_focus() { } void EventListenerLineEdit::_on_unfocus() { - ignore = true; + ignore_next_event = true; set_placeholder(TTR("Filter by event...")); } @@ -109,6 +110,12 @@ int EventListenerLineEdit::get_allowed_input_types() const { return allowed_input_types; } +void EventListenerLineEdit::grab_focus() { + // If we grab focus through code, we don't need to ignore the first event! + ignore_next_event = false; + Control::grab_focus(); +} + void EventListenerLineEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { diff --git a/editor/event_listener_line_edit.h b/editor/event_listener_line_edit.h index 5b415e8a2d..c4cd5e4511 100644 --- a/editor/event_listener_line_edit.h +++ b/editor/event_listener_line_edit.h @@ -44,7 +44,7 @@ class EventListenerLineEdit : public LineEdit { GDCLASS(EventListenerLineEdit, LineEdit) int allowed_input_types = INPUT_KEY | INPUT_MOUSE_BUTTON | INPUT_JOY_BUTTON | INPUT_JOY_MOTION; - bool ignore = true; + bool ignore_next_event = true; bool share_keycodes = false; Ref<InputEvent> event; @@ -67,6 +67,8 @@ public: void set_allowed_input_types(int input_types); int get_allowed_input_types() const; + void grab_focus(); + public: EventListenerLineEdit(); }; diff --git a/editor/icons/Line.svg b/editor/icons/Line.svg new file mode 100644 index 0000000000..c7b1f8a701 --- /dev/null +++ b/editor/icons/Line.svg @@ -0,0 +1 @@ +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 8-8" fill="none" stroke="#ffffff" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp index 7cf35c519b..c54b10842f 100644 --- a/editor/import/collada.cpp +++ b/editor/import/collada.cpp @@ -848,6 +848,8 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam CurveData &curvedata = state.curve_data_map[p_id]; curvedata.name = p_name; + String closed = parser.get_attribute_value_safe("closed").to_lower(); + curvedata.closed = closed == "true" || closed == "1"; COLLADA_PRINT("curve name: " + p_name); diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 8d44329022..6890a46193 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -1090,6 +1090,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres c->set_point_tilt(i, tilts->array[i]); } } + if (cd.closed && pc > 1) { + Vector3 pos = c->get_point_position(0); + Vector3 in = c->get_point_in(0); + Vector3 out = c->get_point_out(0); + c->add_point(pos, in, out, -1); + } curve_cache[ng->source] = c; path->set_curve(c); diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 9dafa47c1c..2acb3a6b4e 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -135,7 +135,7 @@ String ResourceImporterLayeredTexture::get_preset_name(int p_idx) const { } void ResourceImporterLayeredTexture::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless (PNG),Lossy (WebP),Video RAM (S3TC/ETC/BPTC),Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Lossless,Lossy,VRAM Compressed,VRAM Uncompressed,Basis Universal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0)); @@ -186,9 +186,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons int mm_d = MAX(1, d >> 1); for (int i = 0; i < mm_d; i++) { - Ref<Image> mm; - mm.instantiate(); - mm->create(mm_w, mm_h, false, p_images[0]->get_format()); + Ref<Image> mm = Image::create_empty(mm_w, mm_h, false, p_images[0]->get_format()); Vector3 pos; pos.z = float(i) * float(d) / float(mm_d) + 0.5; for (int x = 0; x < mm_w; x++) { diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 498e9f2964..5f64330453 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -474,7 +474,7 @@ Transform3D ResourceImporterScene::get_collision_shapes_transform(const M &p_opt } if (p_options.has(SNAME("primitive/rotation"))) { - transform.basis.set_euler((p_options[SNAME("primitive/rotation")].operator Vector3() / 180.0) * Math_PI); + transform.basis = Basis::from_euler(p_options[SNAME("primitive/rotation")].operator Vector3() * (Math_PI / 180.0)); } } return transform; diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index 9171f04f42..bf22a9377e 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -273,9 +273,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file EditorAtlasPacker::chart_pack(charts, atlas_width, atlas_height); //blit the atlas - Ref<Image> new_atlas; - new_atlas.instantiate(); - new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); + Ref<Image> new_atlas = Image::create_empty(atlas_width, atlas_height, false, Image::FORMAT_RGBA8); for (int i = 0; i < pack_data_files.size(); i++) { PackData &pack_data = pack_data_files.write[i]; diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index ed9a5898fb..c577c61db7 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -533,6 +533,10 @@ String InputEventConfigurationDialog::_get_device_string(int p_device) const { void InputEventConfigurationDialog::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_VISIBILITY_CHANGED: { + event_listener->grab_focus(); + } break; + case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { input_list_search->set_right_icon(input_list_search->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index a48990779b..b4737976d8 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -128,14 +128,11 @@ void AnimationPlayerEditor::_notification(int p_what) { { Ref<Image> autoplay_img = autoplay_icon->get_image(); Ref<Image> reset_img = reset_icon->get_image(); - Ref<Image> autoplay_reset_img; Size2 icon_size = autoplay_img->get_size(); - autoplay_reset_img.instantiate(); - autoplay_reset_img->create(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format()); + Ref<Image> autoplay_reset_img = Image::create_empty(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format()); autoplay_reset_img->blit_rect(autoplay_img, Rect2i(Point2i(), icon_size), Point2i()); autoplay_reset_img->blit_rect(reset_img, Rect2i(Point2i(), icon_size), Point2i(icon_size.x, 0)); - autoplay_reset_icon.instantiate(); - autoplay_reset_icon->set_image(autoplay_reset_img); + autoplay_reset_icon = ImageTexture::create_from_image(autoplay_reset_img); } stop->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 8d1df0b32c..434e6a92e3 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -805,7 +805,7 @@ Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, cons img_ref.instantiate(); Image &im = **img_ref; - im.create(thumbnail_size, thumbnail_size / 2, false, Image::FORMAT_RGBA8); + im.initialize_data(thumbnail_size, thumbnail_size / 2, false, Image::FORMAT_RGBA8); Color bg_color(0.1, 0.1, 0.1, 1.0); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 59b8f31720..836f76ac4f 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -210,9 +210,7 @@ Ref<Texture2D> EditorBitmapPreviewPlugin::generate(const Ref<Resource> &p_from, } } - Ref<Image> img; - img.instantiate(); - img->create(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data); + Ref<Image> img = Image::create_from_data(bm->get_size().width, bm->get_size().height, false, Image::FORMAT_L8, data); if (img->is_compressed()) { if (img->decompress() != OK) { @@ -483,10 +481,8 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const Ref<Resource> &p_from, int line = 0; int col = 0; - Ref<Image> img; - img.instantiate(); int thumbnail_size = MAX(p_size.x, p_size.y); - img->create(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(thumbnail_size, thumbnail_size, false, Image::FORMAT_RGBA8); Color bg_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/background_color"); Color keyword_color = EditorSettings::get_singleton()->get("text_editor/theme/highlighting/keyword_color"); @@ -660,9 +656,7 @@ Ref<Texture2D> EditorAudioStreamPreviewPlugin::generate(const Ref<Resource> &p_f //post_process_preview(img); - Ref<Image> image; - image.instantiate(); - image->create(w, h, false, Image::FORMAT_RGB8, img); + Ref<Image> image = Image::create_from_data(w, h, false, Image::FORMAT_RGB8, img); return ImageTexture::create_from_image(image); } diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index ba11479714..58256ae549 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -154,7 +154,7 @@ void EditorPropertyFontMetaOverride::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - if (Object::cast_to<Button>(button_add)) { + if (button_add) { button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } } break; @@ -549,7 +549,7 @@ void EditorPropertyOTFeatures::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - if (Object::cast_to<Button>(button_add)) { + if (button_add) { button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } } break; diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index 1b2afe2151..891b9efa4f 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -299,7 +299,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGF, texdata); + img->set_data(w, h, false, Image::FORMAT_RGF, texdata); pm->set_emission_point_texture(ImageTexture::create_from_image(img)); pm->set_emission_point_count(vpc); @@ -315,7 +315,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8, colordata); + img->set_data(w, h, false, Image::FORMAT_RGBA8, colordata); pm->set_emission_color_texture(ImageTexture::create_from_image(img)); } @@ -335,7 +335,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGF, normdata); + img->set_data(w, h, false, Image::FORMAT_RGF, normdata); pm->set_emission_normal_texture(ImageTexture::create_from_image(img)); } else { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 4d3ffdc12b..3c9d93f13a 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2435,6 +2435,7 @@ void Node3DEditorViewport::_notification(int p_what) { bool vp_visible = is_visible_in_tree(); set_process(vp_visible); + set_physics_process(vp_visible); if (vp_visible) { orthogonal = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL)); @@ -2658,6 +2659,21 @@ void Node3DEditorViewport::_notification(int p_what) { } } break; + case NOTIFICATION_PHYSICS_PROCESS: { + if (!update_preview_node) { + return; + } + if (preview_node->is_inside_tree()) { + preview_node_pos = _get_instance_position(preview_node_viewport_pos); + Transform3D preview_gl_transform = Transform3D(Basis(), preview_node_pos); + preview_node->set_global_transform(preview_gl_transform); + if (!preview_node->is_visible()) { + preview_node->show(); + } + } + update_preview_node = false; + } break; + case NOTIFICATION_ENTER_TREE: { surface->connect("draw", callable_mp(this, &Node3DEditorViewport::_draw)); surface->connect("gui_input", callable_mp(this, &Node3DEditorViewport::_sinput)); @@ -2970,7 +2986,7 @@ void Node3DEditorViewport::_menu_option(int p_option) { Transform3D xform; if (orthogonal) { xform = sp->get_global_transform(); - xform.basis.set_euler(camera_transform.basis.get_euler()); + xform.basis = Basis::from_euler(camera_transform.basis.get_euler()); } else { xform = camera_transform; xform.scale_basis(sp->get_scale()); @@ -3765,7 +3781,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo if (child) { AABB child_bounds = _calculate_spatial_bounds(child, false); - if (bounds.size == Vector3() && Object::cast_to<Node3D>(p_parent)) { + if (bounds.size == Vector3() && p_parent) { bounds = child_bounds; } else { bounds.merge_with(child_bounds); @@ -3773,7 +3789,7 @@ AABB Node3DEditorViewport::_calculate_spatial_bounds(const Node3D *p_parent, boo } } - if (bounds.size == Vector3() && !Object::cast_to<Node3D>(p_parent)) { + if (bounds.size == Vector3() && !p_parent) { bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4)); } @@ -4029,7 +4045,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po gl_transform = parent_node3d->get_global_gizmo_transform(); } - gl_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); + gl_transform.origin = spatial_editor->snap_point(preview_node_pos); gl_transform.basis *= node3d->get_transform().basis; editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_global_transform", gl_transform); @@ -4093,7 +4109,9 @@ void Node3DEditorViewport::_perform_drop_data() { } } -bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { +bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + preview_node_viewport_pos = p_point; + bool can_instantiate = false; if (!preview_node->is_inside_tree() && spatial_editor->get_preview_material().is_null()) { @@ -4157,6 +4175,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant } if (can_instantiate) { _create_preview_node(files); + preview_node->hide(); } } } else { @@ -4166,8 +4185,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant } if (can_instantiate) { - Transform3D gl_transform = Transform3D(Basis(), _get_instance_position(p_point)); - preview_node->set_global_transform(gl_transform); + update_preview_node = true; return true; } @@ -6932,6 +6950,10 @@ HashSet<RID> _get_physics_bodies_rid(Node *node) { } void Node3DEditor::snap_selected_nodes_to_floor() { + do_snap_selected_nodes_to_floor = true; +} + +void Node3DEditor::_snap_selected_nodes_to_floor() { const List<Node *> &selection = editor_selection->get_selected_node_list(); Dictionary snap_data; @@ -6969,9 +6991,10 @@ void Node3DEditor::snap_selected_nodes_to_floor() { } } if (!found_valid_shape && vi.size()) { - AABB aabb = (*vi.begin())->get_transformed_aabb(); + VisualInstance3D *begin = *vi.begin(); + AABB aabb = begin->get_global_transform().xform(begin->get_aabb()); for (const VisualInstance3D *I : vi) { - aabb.merge_with(I->get_transformed_aabb()); + aabb.merge_with(I->get_global_transform().xform(I->get_aabb())); } Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5); from = aabb.position + size; @@ -7229,6 +7252,13 @@ void Node3DEditor::_notification(int p_what) { tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false); } } break; + + case NOTIFICATION_PHYSICS_PROCESS: { + if (do_snap_selected_nodes_to_floor) { + _snap_selected_nodes_to_floor(); + do_snap_selected_nodes_to_floor = false; + } + } } } @@ -7585,7 +7615,7 @@ void Node3DEditor::_preview_settings_changed() { { // preview sun Transform3D t; - t.basis = Basis(Vector3(sun_rotation.x, sun_rotation.y, 0)); + t.basis = Basis::from_euler(Vector3(sun_rotation.x, sun_rotation.y, 0)); preview_sun->set_transform(t); sun_direction->queue_redraw(); preview_sun->set_param(Light3D::PARAM_ENERGY, sun_energy->get_value()); @@ -8349,10 +8379,12 @@ void Node3DEditorPlugin::make_visible(bool p_visible) { if (p_visible) { spatial_editor->show(); spatial_editor->set_process(true); + spatial_editor->set_physics_process(true); } else { spatial_editor->hide(); spatial_editor->set_process(false); + spatial_editor->set_physics_process(false); } } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index c76f534c22..7dbe153efd 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -193,6 +193,9 @@ private: void _menu_option(int p_option); void _set_auto_orthogonal(); Node3D *preview_node = nullptr; + bool update_preview_node = false; + Point2 preview_node_viewport_pos; + Vector3 preview_node_pos; AABB *preview_bounds = nullptr; Vector<String> selected_files; AcceptDialog *accept = nullptr; @@ -413,7 +416,7 @@ private: bool _create_instance(Node *parent, String &path, const Point2 &p_point); void _perform_drop_data(); - bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; + bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); void _project_settings_changed(); @@ -720,6 +723,9 @@ private: void _selection_changed(); void _refresh_menu_icons(); + bool do_snap_selected_nodes_to_floor = false; + void _snap_selected_nodes_to_floor(); + // Preview Sun and Environment uint32_t world_env_count = 0; @@ -767,7 +773,7 @@ private: WorldEnvironment *preview_environment = nullptr; bool preview_env_dangling = false; Ref<Environment> environment; - Ref<CameraAttributesPhysical> camera_attributes; + Ref<CameraAttributesPractical> camera_attributes; Ref<ProceduralSkyMaterial> sky_material; bool sun_environ_updating = false; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 58a0160010..7d7ef9245f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1598,9 +1598,24 @@ static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const return nullptr; } -void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { - const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; +static String _quote_drop_data(const String &str) { + // This function prepares a string for being "dropped" into the script editor. + // The string can be a resource path, node path or property name. + + const bool using_single_quotes = EDITOR_GET("text_editor/completion/use_single_quotes"); + + String escaped = str.c_escape(); + // If string is double quoted, there is no need to escape single quotes. + // We can revert the extra escaping added in c_escape(). + if (!using_single_quotes) { + escaped = escaped.replace("\\'", "\'"); + } + + return escaped.quote(using_single_quotes ? "'" : "\""); +} + +void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { Dictionary d = p_data; CodeEdit *te = code_editor->get_text_editor(); @@ -1638,9 +1653,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data } if (preload) { - text_to_drop += "preload(" + String(files[i]).c_escape().quote(quote_style) + ")"; + text_to_drop += "preload(" + _quote_drop_data(String(files[i])) + ")"; } else { - text_to_drop += String(files[i]).c_escape().quote(quote_style); + text_to_drop += _quote_drop_data(String(files[i])); } } @@ -1686,7 +1701,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data } for (const String &segment : path.split("/")) { if (!segment.is_valid_identifier()) { - path = path.c_escape().quote(quote_style); + path = _quote_drop_data(path); break; } } @@ -1721,7 +1736,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data for (const String &segment : path.split("/")) { if (!segment.is_valid_identifier()) { - path = path.c_escape().quote(quote_style); + path = _quote_drop_data(path); break; } } @@ -1737,7 +1752,9 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data if (d.has("type") && String(d["type"]) == "obj_property") { te->remove_secondary_carets(); - const String text_to_drop = String(d["property"]).c_escape().quote(quote_style); + // It is unclear whether properties may contain single or double quotes. + // Assume here that double-quotes may not exist. We are escaping single-quotes if necessary. + const String text_to_drop = _quote_drop_data(String(d["property"])); te->set_caret_line(row); te->set_caret_column(col); @@ -1978,18 +1995,6 @@ void ScriptTextEditor::_enable_code_editor() { add_child(connection_info_dialog); - edit_hb->add_child(search_menu); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); - search_menu->get_popup()->add_separator(); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES); - search_menu->get_popup()->add_separator(); - search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/contextual_help"), HELP_CONTEXTUAL); - search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); - edit_hb->add_child(edit_menu); edit_menu->connect("about_to_popup", callable_mp(this, &ScriptTextEditor::_prepare_edit_menu)); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO); @@ -2000,38 +2005,73 @@ void ScriptTextEditor::_enable_code_editor() { edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE); edit_menu->get_popup()->add_separator(); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_select_all"), EDIT_SELECT_ALL); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION); + edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/evaluate_selection"), EDIT_EVALUATE); edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); + { + PopupMenu *sub_menu = memnew(PopupMenu); + sub_menu->set_name("line_menu"); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_up"), EDIT_MOVE_LINE_UP); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/move_down"), EDIT_MOVE_LINE_DOWN); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent"), EDIT_INDENT); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unindent"), EDIT_UNINDENT); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/delete_line"), EDIT_DELETE_LINE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT); + sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); + edit_menu->get_popup()->add_child(sub_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Line"), "line_menu"); + } + { + PopupMenu *sub_menu = memnew(PopupMenu); + sub_menu->set_name("folding_menu"); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_fold_line"), EDIT_TOGGLE_FOLD_LINE); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/fold_all_lines"), EDIT_FOLD_ALL_LINES); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/unfold_all_lines"), EDIT_UNFOLD_ALL_LINES); + sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); + edit_menu->get_popup()->add_child(sub_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Folding"), "folding_menu"); + } edit_menu->get_popup()->add_separator(); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/duplicate_selection"), EDIT_DUPLICATE_SELECTION); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_text_completion_query"), EDIT_COMPLETE); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/evaluate_selection"), EDIT_EVALUATE); edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/trim_trailing_whitespace"), EDIT_TRIM_TRAILING_WHITESAPCE); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); - edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/auto_indent"), EDIT_AUTO_INDENT); + { + PopupMenu *sub_menu = memnew(PopupMenu); + sub_menu->set_name("indent_menu"); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_spaces"), EDIT_CONVERT_INDENT_TO_SPACES); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/convert_indent_to_tabs"), EDIT_CONVERT_INDENT_TO_TABS); + sub_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/auto_indent"), EDIT_AUTO_INDENT); + sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); + edit_menu->get_popup()->add_child(sub_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Indentation"), "indent_menu"); + } edit_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); edit_menu->get_popup()->add_separator(); - - edit_menu->get_popup()->add_child(convert_case); - edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case"); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE); - convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE); - convert_case->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); - + { + PopupMenu *sub_menu = memnew(PopupMenu); + sub_menu->set_name("convert_case"); + sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE); + sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE); + sub_menu->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE); + sub_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); + edit_menu->get_popup()->add_child(sub_menu); + edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case"); + } edit_menu->get_popup()->add_child(highlighter_menu); edit_menu->get_popup()->add_submenu_item(TTR("Syntax Highlighter"), "highlighter_menu"); highlighter_menu->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_change_syntax_highlighter)); + edit_hb->add_child(search_menu); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find"), SEARCH_FIND); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_next"), SEARCH_FIND_NEXT); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE); + search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace_in_files"), REPLACE_IN_FILES); + search_menu->get_popup()->add_separator(); + search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/contextual_help"), HELP_CONTEXTUAL); + search_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option)); + _load_theme_settings(); edit_hb->add_child(goto_menu); @@ -2106,9 +2146,6 @@ ScriptTextEditor::ScriptTextEditor() { edit_menu->set_switch_on_hover(true); edit_menu->set_shortcut_context(this); - convert_case = memnew(PopupMenu); - convert_case->set_name("convert_case"); - highlighter_menu = memnew(PopupMenu); highlighter_menu->set_name("highlighter_menu"); @@ -2153,7 +2190,6 @@ ScriptTextEditor::~ScriptTextEditor() { memdelete(color_panel); memdelete(edit_hb); memdelete(edit_menu); - memdelete(convert_case); memdelete(highlighter_menu); memdelete(search_menu); memdelete(goto_menu); diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index fb02e5c7c4..c165295a8e 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -79,7 +79,6 @@ class ScriptTextEditor : public ScriptEditorBase { PopupMenu *breakpoints_menu = nullptr; PopupMenu *highlighter_menu = nullptr; PopupMenu *context_menu = nullptr; - PopupMenu *convert_case = nullptr; GotoLineDialog *goto_line_dialog = nullptr; ScriptEditorQuickOpen *quick_open = nullptr; diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index be382759f5..4aed7a92a2 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -29,7 +29,6 @@ /*************************************************************************/ #include "texture_editor_plugin.h" - #include "editor/editor_scale.h" TextureRect *TexturePreview::get_texture_display() { @@ -123,6 +122,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { add_child(checkerboard); texture_display = memnew(TextureRect); + texture_display->set_texture_filter(TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); texture_display->set_texture(p_texture); texture_display->set_anchors_preset(TextureRect::PRESET_FULL_RECT); texture_display->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index d7e08db954..167c6d169b 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -47,9 +47,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla merged_mapping.clear(); if (p_atlas_sources.size() >= 2) { - Ref<Image> output_image; - output_image.instantiate(); - output_image->create(1, 1, false, Image::FORMAT_RGBA8); + Ref<Image> output_image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8); // Compute the new texture region size. Vector2i new_texture_region_size; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index dd16d4ffea..b9436e17f0 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -460,7 +460,7 @@ void TileMapEditorTilesPlugin::_update_theme() { source_sort_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Sort"), SNAME("EditorIcons"))); select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons"))); paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons"))); + line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Line"), SNAME("EditorIcons"))); rect_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons"))); bucket_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons"))); @@ -2348,27 +2348,27 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_path_o } HashMap<Vector2i, TileMapCell> output; - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - if (painted_set.has(E.key)) { + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { // Paint a random tile with the correct terrain for the painted path. - output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); } else { // Avoids updating the painted path from the output if the new pattern is the same as before. - bool keep_old = false; - TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key); + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = tile_map->get_cell(tile_map_layer, kv.key); if (cell.source_id != TileSet::INVALID_SOURCE) { TileSetSource *source = *tile_set->get_source(cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { // Get tile data. TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - if (tile_data && tile_data->get_terrains_pattern() == E.value) { - keep_old = true; + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); } } } - if (!keep_old) { - output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + if (in_map_terrain_pattern != kv.value) { + output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); } } } @@ -2395,24 +2395,28 @@ HashMap<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrain_patter } HashMap<Vector2i, TileMapCell> output; - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - if (painted_set.has(E.key)) { + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { // Paint a random tile with the correct terrain for the painted path. - output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); } else { // Avoids updating the painted path from the output if the new pattern is the same as before. - TileMapCell cell = tile_map->get_cell(tile_map_layer, E.key); + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = tile_map->get_cell(tile_map_layer, kv.key); if (cell.source_id != TileSet::INVALID_SOURCE) { TileSetSource *source = *tile_set->get_source(cell.source_id); TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); if (atlas_source) { // Get tile data. TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - if (tile_data && !(tile_data->get_terrains_pattern() == E.value)) { - output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); } } } + if (in_map_terrain_pattern != kv.value) { + output[kv.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + } } } return output; @@ -3277,7 +3281,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() { void TileMapEditorTerrainsPlugin::_update_theme() { paint_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons"))); + line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Line"), SNAME("EditorIcons"))); rect_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons"))); bucket_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons"))); diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 364c3b7246..6fdc9a80e8 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -66,7 +66,9 @@ void TilesEditorPlugin::_thread() { pattern_preview_sem.wait(); pattern_preview_mutex.lock(); - if (pattern_preview_queue.size()) { + if (pattern_preview_queue.size() == 0) { + pattern_preview_mutex.unlock(); + } else { QueueItem item = pattern_preview_queue.front()->get(); pattern_preview_queue.pop_front(); pattern_preview_mutex.unlock(); @@ -130,8 +132,6 @@ void TilesEditorPlugin::_thread() { item.callback.callp(args_ptr, 2, r, error); viewport->queue_delete(); - } else { - pattern_preview_mutex.unlock(); } } } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 8355f64fe5..d9f8d28067 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -5087,23 +5087,23 @@ VisualShaderEditor::VisualShaderEditor() { const String &compare_func_desc = TTR("Returns the boolean result of the %s comparison between two parameters."); - add_options.push_back(AddOption("Equal", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("GreaterThan", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("GreaterThanEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("Equal (==)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Equal (==)")), { VisualShaderNodeCompare::FUNC_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("GreaterThan (>)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than (>)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("GreaterThanEqual (>=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Greater Than or Equal (>=)")), { VisualShaderNodeCompare::FUNC_GREATER_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); add_options.push_back(AddOption("If", "Conditional/Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("IsInf", "Conditional/Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_INF }, VisualShaderNode::PORT_TYPE_BOOLEAN)); add_options.push_back(AddOption("IsNaN", "Conditional/Functions", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between NaN and a scalar parameter."), { VisualShaderNodeIs::FUNC_IS_NAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("LessThan", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("LessThanEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("NotEqual", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("Switch", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Switch2D", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("SwitchBool", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); - add_options.push_back(AddOption("SwitchFloat", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("SwitchInt", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("SwitchTransform", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_TRANSFORM }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - - add_options.push_back(AddOption("Compare", "Conditional/Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("LessThan (<)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than (<)")), { VisualShaderNodeCompare::FUNC_LESS_THAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("LessThanEqual (<=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Less Than or Equal (<=)")), { VisualShaderNodeCompare::FUNC_LESS_THAN_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("NotEqual (!=)", "Conditional/Functions", "VisualShaderNodeCompare", vformat(compare_func_desc, TTR("Not Equal (!=)")), { VisualShaderNodeCompare::FUNC_NOT_EQUAL }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("Switch (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 3D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Switch2D (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated 2D vector if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("SwitchBool (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated boolean if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_BOOLEAN }, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("SwitchFloat (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated floating-point scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_FLOAT }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SwitchInt (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated integer scalar if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_INT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("SwitchTransform (==)", "Conditional/Functions", "VisualShaderNodeSwitch", TTR("Returns an associated transform if the provided boolean value is true or false."), { VisualShaderNodeSwitch::OP_TYPE_TRANSFORM }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("Compare (==)", "Conditional/Common", "VisualShaderNodeCompare", TTR("Returns the boolean result of the comparison between two parameters."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN)); add_options.push_back(AddOption("Is", "Conditional/Common", "VisualShaderNodeIs", TTR("Returns the boolean result of the comparison between INF (or NaN) and a scalar parameter."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN)); add_options.push_back(AddOption("BooleanConstant", "Conditional/Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), {}, VisualShaderNode::PORT_TYPE_BOOLEAN)); @@ -5308,7 +5308,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("EmitParticle", "Particles", "VisualShaderNodeParticleEmit", "", {}, -1, TYPE_FLAGS_PROCESS | TYPE_FLAGS_PROCESS_CUSTOM | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("ParticleAccelerator", "Particles", "VisualShaderNodeParticleAccelerator", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_PROCESS, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("ParticleRandomness", "Particles", "VisualShaderNodeParticleRandomness", "", {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); - add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles/Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("MultiplyByAxisAngle (*)", "Particles/Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", TTR("A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("BoxEmitter", "Particles/Emitters", "VisualShaderNodeParticleBoxEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); add_options.push_back(AddOption("MeshEmitter", "Particles/Emitters", "VisualShaderNodeParticleMeshEmitter", "", {}, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES)); @@ -5359,10 +5359,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Max", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the greater of two values."), { VisualShaderNodeFloatOp::OP_MAX }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Min", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the lesser of two values."), { VisualShaderNodeFloatOp::OP_MIN }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Mix", "Scalar/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two scalars."), { VisualShaderNodeMix::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("MultiplyAdd", "Scalar/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), { VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Negate", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Negate", "Scalar/Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeIntFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("OneMinus", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), { VisualShaderNodeFloatFunc::FUNC_ONEMINUS }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Scalar/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on scalars."), { VisualShaderNodeMultiplyAdd::OP_TYPE_SCALAR }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Negate (*-1)", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Negate (*-1)", "Scalar/Functions", "VisualShaderNodeIntFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeIntFunc::FUNC_NEGATE }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("OneMinus (1-)", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 - scalar"), { VisualShaderNodeFloatFunc::FUNC_ONEMINUS }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Pow", "Scalar/Functions", "VisualShaderNodeFloatOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeFloatOp::OP_POW }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Radians", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeFloatFunc::FUNC_RADIANS }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Reciprocal", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("1.0 / scalar"), { VisualShaderNodeFloatFunc::FUNC_RECIPROCAL }, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -5381,21 +5381,21 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("TanH", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Returns the hyperbolic tangent of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TANH }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Trunc", "Scalar/Functions", "VisualShaderNodeFloatFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeFloatFunc::FUNC_TRUNC }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Add", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), { VisualShaderNodeFloatOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Add", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), { VisualShaderNodeIntOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("BitwiseAND", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_AND }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("BitwiseLeftShift", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("BitwiseOR", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_OR }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("BitwiseRightShift", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("BitwiseXOR", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_XOR }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("Divide", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), { VisualShaderNodeFloatOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Divide", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), { VisualShaderNodeIntOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("Multiply", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Multiply", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), { VisualShaderNodeIntOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("Add (+)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), { VisualShaderNodeFloatOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Add (+)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), { VisualShaderNodeIntOp::OP_ADD }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("BitwiseAND (&)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_AND }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("BitwiseLeftShift (<<)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("BitwiseOR (|)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), { VisualShaderNodeIntOp::OP_BITWISE_OR }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("BitwiseRightShift (>>)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("BitwiseXOR (^)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), { VisualShaderNodeIntOp::OP_BITWISE_XOR }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("Divide (/)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), { VisualShaderNodeFloatOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Divide (/)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), { VisualShaderNodeIntOp::OP_DIV }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("Multiply (*)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Multiply (*)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Multiplies two integer scalars."), { VisualShaderNodeIntOp::OP_MUL }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); add_options.push_back(AddOption("Remainder", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Returns the remainder of the two floating-point scalars."), { VisualShaderNodeFloatOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Remainder", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Returns the remainder of the two integer scalars."), { VisualShaderNodeIntOp::OP_MOD }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); - add_options.push_back(AddOption("Subtract", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), { VisualShaderNodeFloatOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR)); - add_options.push_back(AddOption("Subtract", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), { VisualShaderNodeIntOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); + add_options.push_back(AddOption("Subtract (-)", "Scalar/Operators", "VisualShaderNodeFloatOp", TTR("Subtracts two floating-point scalars."), { VisualShaderNodeFloatOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Subtract (-)", "Scalar/Operators", "VisualShaderNodeIntOp", TTR("Subtracts two integer scalars."), { VisualShaderNodeIntOp::OP_SUB }, VisualShaderNode::PORT_TYPE_SCALAR_INT)); add_options.push_back(AddOption("FloatConstant", "Scalar/Variables", "VisualShaderNodeFloatConstant", TTR("Scalar floating-point constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("IntConstant", "Scalar/Variables", "VisualShaderNodeIntConstant", TTR("Scalar integer constant."), {}, VisualShaderNode::PORT_TYPE_SCALAR_INT)); @@ -5452,12 +5452,12 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Inverse", "Transform/Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), { VisualShaderNodeTransformFunc::FUNC_INVERSE }, VisualShaderNode::PORT_TYPE_TRANSFORM)); add_options.push_back(AddOption("Transpose", "Transform/Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), { VisualShaderNodeTransformFunc::FUNC_TRANSPOSE }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("Add", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), { VisualShaderNodeTransformOp::OP_ADD }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("Divide", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), { VisualShaderNodeTransformOp::OP_A_DIV_B }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("Multiply", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("MultiplyComp", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("Subtract", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM)); - add_options.push_back(AddOption("TransformVectorMult", "Transform/Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Add (+)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Sums two transforms."), { VisualShaderNodeTransformOp::OP_ADD }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Divide (/)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Divides two transforms."), { VisualShaderNodeTransformOp::OP_A_DIV_B }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Multiply (*)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Multiplies two transforms."), { VisualShaderNodeTransformOp::OP_AxB }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("MultiplyComp (*)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Performs per-component multiplication of two transforms."), { VisualShaderNodeTransformOp::OP_AxB_COMP }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Subtract (-)", "Transform/Operators", "VisualShaderNodeTransformOp", TTR("Subtracts two transforms."), { VisualShaderNodeTransformOp::OP_A_MINUS_B }, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformVectorMult (*)", "Transform/Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("TransformConstant", "Transform/Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM)); add_options.push_back(AddOption("TransformParameter", "Transform/Variables", "VisualShaderNodeTransformParameter", TTR("Transform parameter."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM)); @@ -5573,21 +5573,21 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("MixS", "Vector/Functions", "VisualShaderNodeMix", TTR("Linear interpolation between two vectors using scalar."), { VisualShaderNodeMix::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("MultiplyAdd", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Negate", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("MultiplyAdd (a * b + c)", "Vector/Functions", "VisualShaderNodeMultiplyAdd", TTR("Performs a fused multiply-add operation (a * b + c) on vectors."), { VisualShaderNodeMultiplyAdd::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Negate (*-1)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Normalize", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), { VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("OneMinus", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Pow", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("OneMinus (1-)", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), { VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Pow (^)", "Vector/Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), { VisualShaderNodeVectorOp::OP_POW, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Radians", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), { VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); @@ -5632,9 +5632,9 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_2D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_3D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("StepS", "Vector/Functions", "VisualShaderNodeStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), { VisualShaderNodeStep::OP_TYPE_VECTOR_4D_SCALAR }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); - add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); - add_options.push_back(AddOption("Sum", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); + add_options.push_back(AddOption("Sum (+)", "Vector/Functions", "VisualShaderNodeDerivativeFunc", TTR("(Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), { VisualShaderNodeDerivativeFunc::FUNC_SUM, VisualShaderNodeDerivativeFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D, TYPE_FLAGS_FRAGMENT | TYPE_FLAGS_LIGHT, -1, true)); add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Tan", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); @@ -5645,21 +5645,21 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Trunc", "Vector/Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), { VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Add", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Divide", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Multiply", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 2D vector to 2D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 3D vector to 3D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Add (+)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Adds 4D vector to 4D vector."), { VisualShaderNodeVectorOp::OP_ADD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Divide (/)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Divides 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_DIV, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 2D vector by 2D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 3D vector by 3D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Multiply (*)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Multiplies 4D vector by 4D vector."), { VisualShaderNodeVectorOp::OP_MUL, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 2D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 3D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Remainder", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two 4D vectors."), { VisualShaderNodeVectorOp::OP_MOD, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); - add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); - add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); - add_options.push_back(AddOption("Subtract", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 4D vector from 4D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); + add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 2D vector from 2D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_2D }, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 3D vector from 3D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("Subtract (-)", "Vector/Operators", "VisualShaderNodeVectorOp", TTR("Subtracts 4D vector from 4D vector."), { VisualShaderNodeVectorOp::OP_SUB, VisualShaderNodeVectorOp::OP_TYPE_VECTOR_4D }, VisualShaderNode::PORT_TYPE_VECTOR_4D)); add_options.push_back(AddOption("Vector2Constant", "Vector/Variables", "VisualShaderNodeVec2Constant", TTR("2D vector constant."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); add_options.push_back(AddOption("Vector2Parameter", "Vector/Variables", "VisualShaderNodeVec2Parameter", TTR("2D vector parameter."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); diff --git a/main/main.cpp b/main/main.cpp index 08a9b4c310..dbaf42b3e4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1808,6 +1808,8 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph "0,33200,1,or_greater")); // No negative numbers GLOBAL_DEF("display/window/ios/hide_home_indicator", true); + GLOBAL_DEF("display/window/ios/hide_status_bar", true); + GLOBAL_DEF("display/window/ios/suppress_ui_gesture", true); GLOBAL_DEF("input_devices/pointing/ios/touch_delay", 0.15); ProjectSettings::get_singleton()->set_custom_property_info("input_devices/pointing/ios/touch_delay", PropertyInfo(Variant::FLOAT, @@ -1954,9 +1956,15 @@ Error Main::setup2(Thread::ID p_main_tid_override) { { String display_driver = DisplayServer::get_create_function_name(display_driver_idx); + Vector2i *window_position = nullptr; + Vector2i position = init_custom_pos; + if (init_use_custom_pos) { + window_position = &position; + } + // rendering_driver now held in static global String in main and initialized in setup() Error err; - display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err); + display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, err); if (err != OK || display_server == nullptr) { // We can't use this display server, try other ones as fallback. // Skip headless (always last registered) because that's not what users @@ -1965,7 +1973,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { if (i == display_driver_idx) { continue; // Don't try the same twice. } - display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_size, err); + display_server = DisplayServer::create(i, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, err); if (err == OK && display_server != nullptr) { break; } @@ -2127,7 +2135,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { } else { // Create a 1×1 transparent image. This will effectively hide the splash image. boot_logo.instantiate(); - boot_logo->create(1, 1, false, Image::FORMAT_RGBA8); + boot_logo->initialize_data(1, 1, false, Image::FORMAT_RGBA8); boot_logo->set_pixel(0, 0, Color(0, 0, 0, 0)); } diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index e80d453df7..155f7809b0 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -253,7 +253,7 @@ static Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size }; image.instantiate(); - image->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); + image->set_data(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata); return image; } diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index cc21ed28e8..681b3e74bc 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -156,7 +156,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, if (p_color_buffer == nullptr || color_table_size == 0) { // regular pixels - p_image->create(width, height, false, Image::FORMAT_RGBA8, data); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, data); } else { // data is in indexed format, extend it @@ -194,7 +194,7 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, dest += 4; } - p_image->create(width, height, false, Image::FORMAT_RGBA8, extended_data); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, extended_data); } } return err; diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_macos.mm index 0b9696a3e9..0e61dde8e9 100644 --- a/modules/camera/camera_macos.mm +++ b/modules/camera/camera_macos.mm @@ -158,7 +158,7 @@ memcpy(w, dataY, new_width * new_height); img[0].instantiate(); - img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); + img[0]->set_data(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]); } { @@ -177,7 +177,7 @@ ///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion img[1].instantiate(); - img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); + img[1]->set_data(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]); } // set our texture... diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index 7e92d667b3..06f8f5a403 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -64,9 +64,10 @@ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1"> - The physics layers this CSG shape scans for collisions. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. + The physics layers this CSG shape scans for collisions. Only effective if [member use_collision] is [code]true[/code]. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="collision_priority" type="float" setter="set_collision_priority" getter="get_collision_priority" default="1.0"> + The priority used to solve colliding when occurring penetration. Only effective if [member use_collision] is [code]true[/code]. The higher the priority is, the lower the penetration into the object will be. This can for example be used to prevent the player from breaking through the boundaries of a level. </member> <member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0"> The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent. @@ -75,7 +76,7 @@ Snap makes the mesh snap to a given distance so that the faces of two meshes can be perfectly aligned. A lower value results in greater precision but may be harder to adjust. </member> <member name="use_collision" type="bool" setter="set_use_collision" getter="is_using_collision" default="false"> - Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. + Adds a collision shape to the physics engine for our CSG shape. This will always act like a static body. Note that the collision shape is still active even if the CSG shape itself is hidden. See also [member collision_mask] and [member collision_priority]. </member> </members> <constants> diff --git a/modules/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp index 3322ff0a1b..89705e4ee0 100644 --- a/modules/cvtt/image_compress_cvtt.cpp +++ b/modules/cvtt/image_compress_cvtt.cpp @@ -230,7 +230,7 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann h = MAX(h / 2, 1); } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } void image_decompress_cvtt(Image *p_image) { @@ -339,5 +339,5 @@ void image_decompress_cvtt(Image *p_image) { w >>= 1; h >>= 1; } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); } diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp index a0dbd07b10..2b9221bc43 100644 --- a/modules/denoise/lightmap_denoiser.cpp +++ b/modules/denoise/lightmap_denoiser.cpp @@ -51,7 +51,7 @@ Ref<Image> LightmapDenoiserOIDN::denoise_image(const Ref<Image> &p_image) { return p_image; } - img->create(img->get_width(), img->get_height(), false, img->get_format(), data); + img->set_data(img->get_width(), img->get_height(), false, img->get_format(), data); return img; } diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 5181ae76ce..f02fc893fd 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -77,8 +77,5 @@ <member name="host" type="ENetConnection" setter="" getter="get_host"> The underlying [ENetConnection] created after [method create_client] and [method create_server]. </member> - <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> - Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. - </member> </members> </class> diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml index 67760ba5e8..52f45b2338 100644 --- a/modules/enet/doc_classes/ENetPacketPeer.xml +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -80,7 +80,7 @@ <return type="void" /> <param index="0" name="ping_interval" type="int" /> <description> - Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. + Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes. The default ping interval is [code]500[/code] milliseconds. </description> </method> <method name="reset"> @@ -115,33 +115,43 @@ <param index="2" name="deceleration" type="int" /> <description> Configures throttle parameter for a peer. - Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]). + Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probability by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]). When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent. - When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. + When the throttle has a value of [code]0[/code], all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent. Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions. </description> </method> </methods> <constants> <constant name="STATE_DISCONNECTED" value="0" enum="PeerState"> + The peer is disconnected. </constant> <constant name="STATE_CONNECTING" value="1" enum="PeerState"> + The peer is currently attempting to connect. </constant> <constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState"> + The peer has acknowledged the connection request. </constant> <constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState"> + The peer is currently connecting. </constant> <constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState"> + The peer has successfully connected, but is not ready to communicate with yet ([constant STATE_CONNECTED]). </constant> <constant name="STATE_CONNECTED" value="5" enum="PeerState"> + The peer is currently connected and ready to communicate with. </constant> <constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState"> + The peer is slated to disconnect after it has no more outgoing packets to send. </constant> <constant name="STATE_DISCONNECTING" value="7" enum="PeerState"> + The peer is currently disconnecting. </constant> <constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState"> + The peer has acknowledged the disconnection request. </constant> <constant name="STATE_ZOMBIE" value="9" enum="PeerState"> + The peer has lost connection, but is not considered truly disconnected (as the peer didn't acknowledge the disconnection request). </constant> <constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic"> Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE]. @@ -150,6 +160,7 @@ Packet loss variance. </constant> <constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic"> + The time at which packet loss statistics were last updated (in milliseconds since the connection started). The interval for packet loss statistics updates is 10 seconds, and at least one packet must have been sent since the last statistics update. </constant> <constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic"> Mean packet round trip time for reliable packets. @@ -164,24 +175,31 @@ Variance of the last trip time recorded. </constant> <constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic"> + The peer's current throttle status. </constant> <constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic"> + The maximum number of unreliable packets that should not be dropped. This value is always greater than or equal to [code]1[/code]. The initial value is equal to [constant PACKET_THROTTLE_SCALE]. </constant> <constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic"> + Internal value used to increment the packet throttle counter. The value is hardcoded to [code]7[/code] and cannot be changed. You probably want to look at [constant PEER_PACKET_THROTTLE_ACCELERATION] instead. </constant> <constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic"> + The time at which throttle statistics were last updated (in milliseconds since the connection started). The interval for throttle statistics updates is [constant PEER_PACKET_THROTTLE_INTERVAL]. </constant> <constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic"> + The throttle's acceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]more[/i] often. The default value is [code]2[/code]. </constant> <constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic"> + The throttle's deceleration factor. Higher values will make ENet adapt to fluctuating network conditions faster, causing unrelaible packets to be sent [i]less[/i] often. The default value is [code]2[/code]. </constant> <constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic"> + The interval over which the lowest mean round trip time should be measured for use by the throttle mechanism (in milliseconds). The default value is [code]5000[/code]. </constant> <constant name="PACKET_LOSS_SCALE" value="65536"> The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS]. </constant> <constant name="PACKET_THROTTLE_SCALE" value="32"> - The reference value for throttle configuration. See [method throttle_configure]. + The reference value for throttle configuration. The default value is [code]32[/code]. See [method throttle_configure]. </constant> <constant name="FLAG_RELIABLE" value="1"> Mark the packet to be sent as reliable. diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp index e7728f4aec..c93db3b972 100644 --- a/modules/enet/enet_multiplayer_peer.cpp +++ b/modules/enet/enet_multiplayer_peer.cpp @@ -44,6 +44,22 @@ int ENetMultiplayerPeer::get_packet_peer() const { return incoming_packets.front()->get().from; } +MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_packet_mode() const { + ERR_FAIL_COND_V_MSG(!_is_active(), TRANSFER_MODE_RELIABLE, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V(incoming_packets.size() == 0, TRANSFER_MODE_RELIABLE); + return incoming_packets.front()->get().transfer_mode; +} + +int ENetMultiplayerPeer::get_packet_channel() const { + ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active."); + ERR_FAIL_COND_V(incoming_packets.size() == 0, 1); + int ch = incoming_packets.front()->get().channel; + if (ch >= SYSCH_MAX) { // First 2 channels are reserved. + return ch - SYSCH_MAX + 1; + } + return 0; +} + Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) { ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active."); set_refuse_new_connections(false); @@ -114,6 +130,22 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) { return OK; } +void ENetMultiplayerPeer::_store_packet(int32_t p_source, ENetConnection::Event &p_event) { + Packet packet; + packet.packet = p_event.packet; + packet.channel = p_event.channel_id; + packet.from = p_source; + if (p_event.packet->flags & ENET_PACKET_FLAG_RELIABLE) { + packet.transfer_mode = TRANSFER_MODE_RELIABLE; + } else if (p_event.packet->flags & ENET_PACKET_FLAG_UNSEQUENCED) { + packet.transfer_mode = TRANSFER_MODE_UNRELIABLE; + } else { + packet.transfer_mode = TRANSFER_MODE_UNRELIABLE_ORDERED; + } + packet.packet->referenceCount++; + incoming_packets.push_back(packet); +} + bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) { switch (p_type) { case ENetConnection::EVENT_CONNECT: { @@ -131,9 +163,6 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, peers[id] = p_event.peer; emit_signal(SNAME("peer_connected"), id); - if (server_relay) { - _notify_peers(id, true); - } return false; } case ENetConnection::EVENT_DISCONNECT: { @@ -145,50 +174,11 @@ bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, emit_signal(SNAME("peer_disconnected"), id); peers.erase(id); - if (server_relay) { - _notify_peers(id, false); - } return false; } case ENetConnection::EVENT_RECEIVE: { - if (p_event.channel_id == SYSCH_CONFIG) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Only server can send config messages"); - } else { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - uint32_t source = decode_uint32(&p_event.packet->data[0]); - int target = decode_uint32(&p_event.packet->data[4]); - - uint32_t id = p_event.peer->get_meta(SNAME("_net_id")); - // Someone is cheating and trying to fake the source! - if (source != id) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!"); - } - - Packet packet; - packet.packet = p_event.packet; - packet.channel = p_event.channel_id; - packet.from = id; - - // Even if relaying is disabled, these targets are valid as incoming packets. - if (target == 1 || target == 0 || target < -1) { - packet.packet->referenceCount++; - incoming_packets.push_back(packet); - } - - if (server_relay && target != 1) { - packet.packet->referenceCount++; - _relay(source, target, p_event.channel_id, p_event.packet); - packet.packet->referenceCount--; - _destroy_unused(p_event.packet); - } - // Destroy packet later - } + int32_t source = p_event.peer->get_meta(SNAME("_net_id")); + _store_packet(source, p_event); return false; } default: @@ -215,44 +205,7 @@ bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type, return true; } case ENetConnection::EVENT_RECEIVE: { - if (p_event.channel_id == SYSCH_CONFIG) { - // Config message - if (p_event.packet->dataLength != 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V(false); - } - - int msg = decode_uint32(&p_event.packet->data[0]); - int id = decode_uint32(&p_event.packet->data[4]); - - switch (msg) { - case SYSMSG_ADD_PEER: { - peers[id] = Ref<ENetPacketPeer>(); - emit_signal(SNAME("peer_connected"), id); - - } break; - case SYSMSG_REMOVE_PEER: { - peers.erase(id); - emit_signal(SNAME("peer_disconnected"), id); - } break; - } - _destroy_unused(p_event.packet); - } else { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - uint32_t source = decode_uint32(&p_event.packet->data[0]); - Packet packet; - packet.packet = p_event.packet; - packet.from = source; - packet.channel = p_event.channel_id; - - packet.packet->referenceCount++; - incoming_packets.push_back(packet); - // Destroy packet later - } + _store_packet(1, p_event); return false; } default: @@ -273,18 +226,7 @@ bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, EN hosts.erase(p_peer_id); return true; case ENetConnection::EVENT_RECEIVE: { - if (p_event.packet->dataLength < 8) { - _destroy_unused(p_event.packet); - ERR_FAIL_V_MSG(false, "Invalid packet size"); - } - - Packet packet; - packet.packet = p_event.packet; - packet.from = p_peer_id; - packet.channel = p_event.channel_id; - - packet.packet->referenceCount++; - incoming_packets.push_back(packet); + _store_packet(p_peer_id, p_event); return false; } break; default: @@ -376,6 +318,10 @@ bool ENetMultiplayerPeer::is_server() const { return active_mode == MODE_SERVER; } +bool ENetMultiplayerPeer::is_server_relay_supported() const { + return active_mode == MODE_SERVER || active_mode == MODE_CLIENT; +} + void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) { if (!_is_active()) { return; @@ -422,8 +368,8 @@ Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_si current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - *r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]); - r_buffer_size = current_packet.packet->dataLength - 8; + *r_buffer = (const uint8_t *)(current_packet.packet->data); + r_buffer_size = current_packet.packet->dataLength; return OK; } @@ -457,15 +403,13 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size } #ifdef DEBUG_ENABLED - if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) { - WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU)); + if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size > ENET_HOST_DEFAULT_MTU) { + WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size, ENET_HOST_DEFAULT_MTU)); } #endif - ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags); - encode_uint32(unique_id, &packet->data[0]); // Source ID - encode_uint32(target_peer, &packet->data[4]); // Dest ID - memcpy(&packet->data[8], p_buffer, p_buffer_size); + ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size, packet_flags); + memcpy(&packet->data[0], p_buffer, p_buffer_size); if (is_server()) { if (target_peer == 0) { @@ -548,16 +492,6 @@ void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) { MultiplayerPeer::set_refuse_new_connections(p_enabled); } -void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) { - ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active."); - - server_relay = p_enabled; -} - -bool ENetMultiplayerPeer::is_server_relay_enabled() const { - return server_relay; -} - Ref<ENetConnection> ENetMultiplayerPeer::get_host() const { ERR_FAIL_COND_V(!_is_active(), nullptr); ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr); @@ -577,70 +511,6 @@ void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) { } } -void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) { - if (p_to == 0) { - // Re-send to everyone but sender :| - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_from) { - continue; - } - - E.value->send(p_channel, p_packet); - } - } else if (p_to < 0) { - // Re-send to everyone but excluded and sender. - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded - continue; - } - - E.value->send(p_channel, p_packet); - } - } else { - // To someone else, specifically - ERR_FAIL_COND(!peers.has(p_to)); - ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags); - peers[p_to]->send(p_channel, packet); - } -} - -void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) { - if (p_connected) { - ERR_FAIL_COND(!peers.has(p_id)); - // Someone connected, notify all the peers available. - Ref<ENetPacketPeer> peer = peers[p_id]; - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]); - encode_uint32(p_id, &packet->data[4]); - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_id) { - continue; - } - // Send new peer to existing peer. - E.value->send(SYSCH_CONFIG, packet); - // Send existing peer to new peer. - // This packet will be automatically destroyed by ENet after send. - ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]); - encode_uint32(E.key, &packet2->data[4]); - peer->send(SYSCH_CONFIG, packet2); - } - _destroy_unused(packet); - } else { - // Server just received a client disconnect and is in relay mode, notify everyone else. - ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE); - encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]); - encode_uint32(p_id, &packet->data[4]); - for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) { - if (E.key == p_id) { - continue; - } - E.value->send(SYSCH_CONFIG, packet); - } - _destroy_unused(packet); - } -} - void ENetMultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0)); @@ -649,12 +519,9 @@ void ENetMultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100)); ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip); - ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled); - ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled); ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host); ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host"); } diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h index 3152068d46..85692e8eee 100644 --- a/modules/enet/enet_multiplayer_peer.h +++ b/modules/enet/enet_multiplayer_peer.h @@ -47,10 +47,9 @@ private: }; enum { - SYSCH_CONFIG = 0, - SYSCH_RELIABLE = 1, - SYSCH_UNRELIABLE = 2, - SYSCH_MAX = 3 + SYSCH_RELIABLE = 0, + SYSCH_UNRELIABLE = 1, + SYSCH_MAX = 2 }; enum Mode { @@ -66,8 +65,6 @@ private: int target_peer = 0; - bool server_relay = true; - ConnectionStatus connection_status = CONNECTION_DISCONNECTED; HashMap<int, Ref<ENetConnection>> hosts; @@ -77,18 +74,18 @@ private: ENetPacket *packet = nullptr; int from = 0; int channel = 0; + TransferMode transfer_mode = TRANSFER_MODE_RELIABLE; }; List<Packet> incoming_packets; Packet current_packet; + void _store_packet(int32_t p_source, ENetConnection::Event &p_event); void _pop_current_packet(); bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event); bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event); bool _parse_mesh_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event, int p_peer_id); - void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet); - void _notify_peers(int p_id, bool p_connected); void _destroy_unused(ENetPacket *p_packet); _FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; } @@ -99,10 +96,15 @@ protected: public: virtual void set_target_peer(int p_peer) override; + virtual int get_packet_peer() const override; + virtual TransferMode get_packet_mode() const override; + virtual int get_packet_channel() const override; virtual void poll() override; virtual bool is_server() const override; + virtual bool is_server_relay_supported() const override; + // Overridden so we can instrument the DTLSServer when needed. virtual void set_refuse_new_connections(bool p_enabled) override; @@ -125,8 +127,6 @@ public: void disconnect_peer(int p_peer, bool now = false); void set_bind_ip(const IPAddress &p_ip); - void set_server_relay_enabled(bool p_enabled); - bool is_server_relay_enabled() const; Ref<ENetConnection> get_host() const; Ref<ENetPacketPeer> get_peer(int p_id) const; diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp index 7d5557d197..3d66b27556 100644 --- a/modules/etcpak/image_compress_etcpak.cpp +++ b/modules/etcpak/image_compress_etcpak.cpp @@ -233,7 +233,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua } // Replace original image with compressed one. - r_img->create(width, height, mipmaps, target_format, dest_data); + r_img->set_data(width, height, mipmaps, target_format, dest_data); print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time))); } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index bc44479f93..c8eda53a2d 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -18,13 +18,11 @@ <param index="2" name="b8" type="int" /> <param index="3" name="a8" type="int" default="255" /> <description> - Returns a color constructed from integer red, green, blue, and alpha channels. Each channel should have 8 bits of information ranging from 0 to 255. - [code]r8[/code] red channel - [code]g8[/code] green channel - [code]b8[/code] blue channel - [code]a8[/code] alpha channel + Returns a [Color] constructed from red ([param r8]), green ([param g8]), blue ([param b8]), and optionally alpha ([param a8]) integer channels, each divided by [code]255.0[/code] for their final value. [codeblock] - red = Color8(255, 0, 0) + var red = Color8(255, 0, 0) # Same as Color(1, 0, 0) + var dark_blue = Color8(0, 0, 51) # Same as Color(0, 0, 0.2). + var my_color = Color8(306, 255, 0, 102) # Same as Color(1.2, 1, 0, 0.4). [/codeblock] </description> </method> @@ -33,9 +31,9 @@ <param index="0" name="condition" type="bool" /> <param index="1" name="message" type="String" default="""" /> <description> - Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users. - [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode. - The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. It must be a static string, so format strings can't be used. You can use this to provide additional details about why the assertion failed. + Asserts that the [param condition] is [code]true[/code]. If the [param condition] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users. + An optional [param message] can be shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed. + [b]Warning:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode. [codeblock] # Imagine we always want speed to be between 0 and 20. var speed = -10 @@ -50,7 +48,7 @@ <return type="String" /> <param index="0" name="char" type="int" /> <description> - Returns a character as a String of the given Unicode code point (which is compatible with ASCII code). + Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code). [codeblock] a = char(65) # a is "A" a = char(65 + 32) # a is "a" @@ -63,14 +61,14 @@ <param index="0" name="what" type="Variant" /> <param index="1" name="type" type="int" /> <description> - Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the [enum Variant.Type] values. + Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values. [codeblock] - a = Vector2(1, 0) - # Prints 1 - print(a.length()) - a = convert(a, TYPE_STRING) - # Prints 6 as "(1, 0)" is 6 characters - print(a.length()) + var a = [4, 2.5, 1.2] + print(a is Array) # Prints true + + var b = convert(a, TYPE_PACKED_BYTE_ARRAY) + print(b) # Prints [4, 2, 1] + print(b is Array) # Prints false [/codeblock] </description> </method> @@ -78,7 +76,7 @@ <return type="Object" /> <param index="0" name="dictionary" type="Dictionary" /> <description> - Converts a [param dictionary] (previously created with [method inst_to_dict]) back to an Object instance. Useful for deserializing. + Converts a [param dictionary] (created with [method inst_to_dict]) back to an Object instance. Can be useful for deserializing. </description> </method> <method name="get_stack"> @@ -95,19 +93,19 @@ func bar(): print(get_stack()) [/codeblock] - would print + Starting from [code]_ready()[/code], [code]bar()[/code] would print: [codeblock] [{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}] [/codeblock] - [b]Note:[/b] [method get_stack] only works if the running instance is connected to a debugging server (i.e. an editor instance). [method get_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. - [b]Note:[/b] Not supported for calling from threads. Instead, this will return an empty array. + [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method get_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will return an empty array. </description> </method> <method name="inst_to_dict"> <return type="Dictionary" /> <param index="0" name="instance" type="Object" /> <description> - Returns the passed [param instance] converted to a Dictionary (useful for serializing). + Returns the passed [param instance] converted to a Dictionary. Can be useful for serializing. [codeblock] var foo = "bar" func _ready(): @@ -126,11 +124,13 @@ <return type="int" /> <param index="0" name="var" type="Variant" /> <description> - Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc. - [b]Note:[/b] Generates a fatal error if Variant can not provide a length. + Returns the length of the given Variant [param var]. The length can be the character count of a [String], the element count of any array type or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped. [codeblock] a = [1, 2, 3, 4] len(a) # Returns 4 + + b = "Hello!" + len(b) # Returns 6 [/codeblock] </description> </method> @@ -138,25 +138,25 @@ <return type="Resource" /> <param index="0" name="path" type="String" /> <description> - Loads a resource from the filesystem located at [code]path[/code]. The resource is loaded on the method call (unless it's referenced already elsewhere, e.g. in another script or in the scene), which might cause slight delay, especially when loading scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload]. + Returns a [Resource] from the filesystem located at the absolute [param path]. Unless it's already referenced elsewhere (such as in another script or in the scene), the resource is loaded from disk on function call, which might cause a slight delay, especially when loading large scenes. To avoid unnecessary delays when loading something multiple times, either store the resource in a variable or use [method preload]. [b]Note:[/b] Resource paths can be obtained by right-clicking on a resource in the FileSystem dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. [codeblock] - # Load a scene called main located in the root of the project directory and cache it in a variable. + # Load a scene called "main" located in the root of the project directory and cache it in a variable. var main = load("res://main.tscn") # main will contain a PackedScene resource. [/codeblock] - [b]Important:[/b] The path must be absolute, a local path will just return [code]null[/code]. - This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. - [b]Note:[/b] You have to import the files into the engine first to load them using [method load]. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. + [b]Important:[/b] The path must be absolute. A relative path will always return [code]null[/code]. + This function is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios. + [b]Note:[/b] Files have to be imported into the engine first to load them using this function. If you want to load [Image]s at run-time, you may use [method Image.load]. If you want to import audio files, you can use the snippet described in [member AudioStreamMP3.data]. </description> </method> <method name="preload"> <return type="Resource" /> <param index="0" name="path" type="String" /> <description> - Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load]. + Returns a [Resource] from the filesystem located at [param path]. During run-time, the resource is loaded when the script is being parsed. This function effectively acts as a reference to that resource. Note that this function requires [param path] to be a constant [String]. If you want to load a resource from a dynamic/variable path, use [method load]. [b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script. [codeblock] - # Instance a scene. + # Create instance of a scene. var diamond = preload("res://diamond.tscn").instantiate() [/codeblock] </description> @@ -165,24 +165,24 @@ <return type="void" /> <description> Like [method @GlobalScope.print], but includes the current stack frame when running with the debugger turned on. - Output in the console would look something like this: + The output in the console may look like the following: [codeblock] Test print - At: res://test.gd:15:_process() + At: res://test.gd:15:_process() [/codeblock] - [b]Note:[/b] Not supported for calling from threads. Instead of the stack frame, this will print the thread ID. + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID. </description> </method> <method name="print_stack"> <return type="void" /> <description> Prints a stack trace at the current code location. See also [method get_stack]. - Output in the console would look something like this: + The output in the console may look like the following: [codeblock] Frame 0 - res://test.gd:16 in function '_process' [/codeblock] - [b]Note:[/b] [method print_stack] only works if the running instance is connected to a debugging server (i.e. an editor instance). [method print_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. - [b]Note:[/b] Not supported for calling from threads. Instead of the stack trace, this will print the thread ID. + [b]Note:[/b] This function only works if the running instance is connected to a debugging server (i.e. an editor instance). [method print_stack] will not work in projects exported in release mode, or in projects exported in debug mode if not connected to a debugging server. + [b]Note:[/b] Calling this function from a [Thread] is not supported. Doing so will instead print the thread ID. </description> </method> <method name="range" qualifiers="vararg"> @@ -229,7 +229,7 @@ <method name="str" qualifiers="vararg"> <return type="String" /> <description> - Converts one or more arguments to string in the best way possible. + Converts one or more arguments to a [String] in the best way possible. [codeblock] var a = [10, 20, 30] var b = str(a); @@ -242,7 +242,7 @@ <return type="bool" /> <param index="0" name="type" type="StringName" /> <description> - Returns whether the given [Object]-derived class exists in [ClassDB]. Note that [Variant] data types are not registered in [ClassDB]. + Returns [code]true[/code] if the given [Object]-derived class exists in [ClassDB]. Note that [Variant] data types are not registered in [ClassDB]. [codeblock] type_exists("Sprite2D") # Returns true type_exists("NonExistentClass") # Returns false @@ -259,11 +259,11 @@ </constant> <constant name="INF" value="inf"> Positive floating-point infinity. This is the result of floating-point division when the divisor is [code]0.0[/code]. For negative infinity, use [code]-INF[/code]. Dividing by [code]-0.0[/code] will result in negative infinity if the numerator is positive, so dividing by [code]0.0[/code] is not the same as dividing by [code]-0.0[/code] (despite [code]0.0 == -0.0[/code] returning [code]true[/code]). - [b]Note:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead. + [b]Warning:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead. </constant> <constant name="NAN" value="nan"> "Not a Number", an invalid floating-point value. [constant NAN] has special properties, including that it is not equal to itself ([code]NAN == NAN[/code] returns [code]false[/code]). It is output by some invalid operations, such as dividing floating-point [code]0.0[/code] by [code]0.0[/code]. - [b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead. + [b]Warning:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead. </constant> </constants> <annotations> @@ -294,7 +294,7 @@ <annotation name="@export_color_no_alpha"> <return type="void" /> <description> - Export a [Color] property without an alpha (fixed as [code]1.0[/code]). + Export a [Color] property without transparency (its alpha fixed as [code]1.0[/code]). See also [constant PROPERTY_HINT_COLOR_NO_ALPHA]. [codeblock] @export_color_no_alpha var modulate_color: Color @@ -320,7 +320,7 @@ [codeblock] @export_enum("Rebecca", "Mary", "Leah") var character_name: String @export_enum("Warrior", "Magician", "Thief") var character_class: int - @export_enum("Walking:30", "Running:60", "Riding:200") var character_speed: int + @export_enum("Slow:30", "Average:60", "Very Fast:200") var character_speed: int [/codeblock] </description> </annotation> @@ -451,7 +451,7 @@ <description> Define a new group for the following exported properties. This helps to organize properties in the Inspector dock. Groups can be added with an optional [param prefix], which would make group to only consider properties that have this prefix. The grouping will break on the first property that doesn't have a prefix. The prefix is also removed from the property's name in the Inspector dock. If no [param prefix] is provided, the every following property is added to the group. The group ends when then next group or category is defined. You can also force end a group by using this annotation with empty strings for parameters, [code]@export_group("", "")[/code]. - Groups cannot be nested, use [annotation @export_subgroup] to add subgroups to your groups. + Groups cannot be nested, use [annotation @export_subgroup] to add subgroups within groups. See also [constant PROPERTY_USAGE_GROUP]. [codeblock] @export_group("My Properties") @@ -473,7 +473,7 @@ Export a [String] property with a large [TextEdit] widget instead of a [LineEdit]. This adds support for multiline content and makes it easier to edit large amount of text stored in the property. See also [constant PROPERTY_HINT_MULTILINE_TEXT]. [codeblock] - @export_multiline var character_bio + @export_multiline var character_biography [/codeblock] </description> </annotation> @@ -547,11 +547,11 @@ <return type="void" /> <param index="0" name="icon_path" type="String" /> <description> - Add a custom icon to the current script. The icon is displayed in the Scene dock for every node that the script is attached to. For named classes the icon is also displayed in various editor dialogs. + Add a custom icon to the current script. After loading an icon at [param icon_path], the icon is displayed in the Scene dock for every node that the script is attached to. For named classes, the icon is also displayed in various editor dialogs. [codeblock] @icon("res://path/to/class/icon.svg") [/codeblock] - [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported yet. + [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. </description> </annotation> <annotation name="@onready"> @@ -590,7 +590,7 @@ <return type="void" /> <param index="0" name="warning" type="String" /> <description> - Mark the following statement to ignore the specified warning. See [url=$DOCS_URL/tutorials/scripting/gdscript/warning_system.html]GDScript warning system[/url]. + Mark the following statement to ignore the specified [param warning]. See [url=$DOCS_URL/tutorials/scripting/gdscript/warning_system.html]GDScript warning system[/url]. [codeblock] func test(): print("hello") diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 578e7a34f3..8246c96c15 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -4,7 +4,7 @@ A script implemented in the GDScript programming language. </brief_description> <description> - A script implemented in the GDScript programming language. The script extends the functionality of all objects that instance it. + A script implemented in the GDScript programming language. The script extends the functionality of all objects that instantiate it. [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 0a9dad04c7..b4da9c1224 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1050,6 +1050,11 @@ Error GDScript::load_byte_code(const String &p_path) { return ERR_COMPILATION_FAILED; } +void GDScript::set_path(const String &p_path, bool p_take_over) { + Script::set_path(p_path, p_take_over); + this->path = p_path; +} + Error GDScript::load_source_code(const String &p_path) { Vector<uint8_t> sourcef; Error err; diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 0a010e5ad0..72ad890fbc 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -222,6 +222,7 @@ public: virtual Error reload(bool p_keep_state = false) override; + virtual void set_path(const String &p_path, bool p_take_over = false) override; void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too... Error load_source_code(const String &p_path); Error load_byte_code(const String &p_path); diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index b3ca8ff00a..898e4eb1a6 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2552,6 +2552,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) { parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); } + + if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) { + parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string()); + } #endif // DEBUG_ENABLED call_type = return_type; @@ -3222,7 +3226,13 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } p_preload->resolved_path = p_preload->resolved_path.simplify_path(); if (!ResourceLoader::exists(p_preload->resolved_path)) { - push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path); + Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); + + if (file_check->file_exists(p_preload->resolved_path)) { + push_error(vformat(R"(Preload file "%s" has no resource loaders (unrecognized file extension).)", p_preload->resolved_path), p_preload->path); + } else { + push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path); + } } else { // TODO: Don't load if validating: use completion cache. p_preload->resource = ResourceLoader::load(p_preload->resolved_path); diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 1cae7bdfac..a0c107aa53 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -155,6 +155,10 @@ String GDScriptWarning::get_message() const { case INT_ASSIGNED_TO_ENUM: { return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type."; } + case STATIC_CALLED_ON_INSTANCE: { + CHECK_SYMBOLS(2); + return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]); + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -215,6 +219,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "EMPTY_FILE", "SHADOWED_GLOBAL_IDENTIFIER", "INT_ASSIGNED_TO_ENUM", + "STATIC_CALLED_ON_INSTANCE", }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index a639e7b44e..7e4e975510 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -78,6 +78,7 @@ public: EMPTY_FILE, // A script file is empty. SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable. INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting. + STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. WARNING_MAX, }; diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd new file mode 100644 index 0000000000..29d8501b78 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd @@ -0,0 +1,11 @@ +class Player: + var x = 3 + +func test(): + # These should not emit a warning. + var _player = Player.new() + print(String.num_uint64(8589934592)) # 2 ^ 33 + + # This should emit a warning. + var some_string = String() + print(some_string.num_uint64(8589934592)) # 2 ^ 33 diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out new file mode 100644 index 0000000000..3933a35178 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out @@ -0,0 +1,7 @@ +GDTEST_OK +>> WARNING +>> Line: 11 +>> STATIC_CALLED_ON_INSTANCE +>> The function 'num_uint64()' is a static function but was called from an instance. Instead, it should be directly called from the type: 'String.num_uint64()'. +8589934592 +8589934592 diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp index b1c2140039..53ec0fde34 100644 --- a/modules/glslang/register_types.cpp +++ b/modules/glslang/register_types.cpp @@ -53,7 +53,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage }; int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100 - bool check_subgroup_support = true; // assume we support subgroups glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2; glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5; @@ -63,7 +62,6 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage if (capabilities->version_major == 1 && capabilities->version_minor == 0) { ClientVersion = glslang::EShTargetVulkan_1_0; TargetVersion = glslang::EShTargetSpv_1_0; - check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0 } else if (capabilities->version_major == 1 && capabilities->version_minor == 1) { ClientVersion = glslang::EShTargetVulkan_1_1; TargetVersion = glslang::EShTargetSpv_1_3; @@ -88,7 +86,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage shader.setEnvClient(glslang::EShClientVulkan, ClientVersion); shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion); - if (check_subgroup_support) { + { uint32_t stage_bit = 1 << p_stage; uint32_t subgroup_in_shaders = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS)); diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 4d1aa89ac9..8e48066623 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -9,6 +9,25 @@ <tutorials> <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link> </tutorials> + <methods> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> + </methods> <members> <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1"> </member> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 56f3a70631..d0740cf7ca 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -20,6 +20,14 @@ <description> </description> </method> + <method name="get_additional_data"> + <return type="Variant" /> + <param index="0" name="extension_name" type="StringName" /> + <description> + Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the return value can be anything you set. If nothing was set, the return value is null. + </description> + </method> <method name="get_animation_player"> <return type="AnimationPlayer" /> <param index="0" name="idx" type="int" /> @@ -120,6 +128,15 @@ <description> </description> </method> + <method name="set_additional_data"> + <return type="void" /> + <param index="0" name="extension_name" type="StringName" /> + <param index="1" name="additional_data" type="Variant" /> + <description> + Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless. + The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want. + </description> + </method> <method name="set_animations"> <return type="void" /> <param index="0" name="animations" type="GLTFAnimation[]" /> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 6700b6de0a..cb148463a7 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3484,7 +3484,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) { height = albedo_texture->get_height(); width = albedo_texture->get_width(); } - orm_image->create(width, height, false, Image::FORMAT_RGBA8); + orm_image->initialize_data(width, height, false, Image::FORMAT_RGBA8); if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) { ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS); } @@ -3860,13 +3860,11 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Re if (r_spec_gloss->diffuse_img.is_null()) { return; } - Ref<Image> rm_img; - rm_img.instantiate(); bool has_roughness = false; bool has_metal = false; p_material->set_roughness(1.0f); p_material->set_metallic(1.0f); - rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); + Ref<Image> rm_img = Image::create_empty(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8); r_spec_gloss->spec_gloss_img->decompress(); if (r_spec_gloss->diffuse_img.is_valid()) { r_spec_gloss->diffuse_img->decompress(); diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp index 60192c67e6..ac5665e396 100644 --- a/modules/gltf/gltf_state.cpp +++ b/modules/gltf/gltf_state.cpp @@ -87,6 +87,8 @@ void GLTFState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations); ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations); ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFState::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int @@ -358,3 +360,11 @@ String GLTFState::get_base_path() { void GLTFState::set_base_path(String p_base_path) { base_path = p_base_path; } + +Variant GLTFState::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFState::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index afe7e82010..e24017b0fd 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -97,6 +97,7 @@ class GLTFState : public Resource { HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton; HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin; + Dictionary additional_data; protected: static void _bind_methods(); @@ -191,6 +192,9 @@ public: AnimationPlayer *get_animation_player(int idx); + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); + //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) { // this->scene_nodes = p_scene_nodes; //} diff --git a/modules/gltf/structures/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp index 86280603fa..6fd36f93b7 100644 --- a/modules/gltf/structures/gltf_node.cpp +++ b/modules/gltf/structures/gltf_node.cpp @@ -57,6 +57,8 @@ void GLTFNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children); ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light); ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light); + ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data); + ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data); ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int @@ -176,3 +178,11 @@ GLTFLightIndex GLTFNode::get_light() { void GLTFNode::set_light(GLTFLightIndex p_light) { light = p_light; } + +Variant GLTFNode::get_additional_data(const StringName &p_extension_name) { + return additional_data[p_extension_name]; +} + +void GLTFNode::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) { + additional_data[p_extension_name] = p_additional_data; +} diff --git a/modules/gltf/structures/gltf_node.h b/modules/gltf/structures/gltf_node.h index 1a57ea32e2..90a4fa99ed 100644 --- a/modules/gltf/structures/gltf_node.h +++ b/modules/gltf/structures/gltf_node.h @@ -53,6 +53,7 @@ private: Vector3 scale = Vector3(1, 1, 1); Vector<int> children; GLTFLightIndex light = -1; + Dictionary additional_data; protected: static void _bind_methods(); @@ -96,6 +97,9 @@ public: GLTFLightIndex get_light(); void set_light(GLTFLightIndex p_light); + + Variant get_additional_data(const StringName &p_extension_name); + void set_additional_data(const StringName &p_extension_name, Variant p_additional_data); }; #endif // GLTF_NODE_H diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp index 6f0bc16a26..457b263e16 100644 --- a/modules/hdr/image_loader_hdr.cpp +++ b/modules/hdr/image_loader_hdr.cpp @@ -140,7 +140,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField } } - p_image->create(width, height, false, Image::FORMAT_RGBE9995, imgdata); + p_image->set_data(width, height, false, Image::FORMAT_RGBE9995, imgdata); return OK; } diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp index ce20ac9060..d465467cf9 100644 --- a/modules/jpg/image_loader_jpegd.cpp +++ b/modules/jpg/image_loader_jpegd.cpp @@ -99,7 +99,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p fmt = Image::FORMAT_RGB8; } - p_image->create(image_width, image_height, false, fmt, data); + p_image->set_data(image_width, image_height, false, fmt, data); return OK; } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 8785f327db..9111827c1b 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -251,15 +251,11 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ } for (int i = 0; i < atlas_slices; i++) { - Ref<Image> albedo; - albedo.instantiate(); - albedo->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8); + Ref<Image> albedo = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8); albedo->set_as_black(); albedo_images.write[i] = albedo; - Ref<Image> emission; - emission.instantiate(); - emission->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH); + Ref<Image> emission = Image::create_empty(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH); emission->set_as_black(); emission_images.write[i] = emission; } @@ -478,9 +474,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i grid_usage.write[j] = count > 0 ? 255 : 0; } - Ref<Image> img; - img.instantiate(); - img->create(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage); + Ref<Image> img = Image::create_from_data(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage); img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png"); } #endif @@ -660,9 +654,7 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade #ifdef DEBUG_TEXTURES for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->convert(Image::FORMAT_RGBA8); img->save_png("res://5_dilated_" + itos(i) + ".png"); } @@ -778,7 +770,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d panorama_tex->convert(Image::FORMAT_RGBAF); } else { panorama_tex.instantiate(); - panorama_tex->create(8, 8, false, Image::FORMAT_RGBAF); + panorama_tex->initialize_data(8, 8, false, Image::FORMAT_RGBAF); panorama_tex->fill(Color(0, 0, 0, 1)); } @@ -953,13 +945,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(position_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s); img->save_exr("res://1_position_" + itos(i) + ".exr", false); s = rd->texture_get_data(normal_tex, i); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + img->set_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://1_normal_" + itos(i) + ".exr", false); } #endif @@ -1182,9 +1172,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices; i++) { Vector<uint8_t> s = rd->texture_get_data(light_source_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false); } #endif @@ -1415,14 +1403,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d #if 0 for (int i = 0; i < probe_positions.size(); i++) { - Ref<Image> img; - img.instantiate(); - img->create(6, 4, false, Image::FORMAT_RGB8); + Ref<Image> img = Image::create_empty(6, 4, false, Image::FORMAT_RGB8); for (int j = 0; j < 6; j++) { Vector<uint8_t> s = rd->texture_get_data(lightprobe_tex, i * 6 + j); - Ref<Image> img2; - img2.instantiate(); - img2->create(2, 2, false, Image::FORMAT_RGBAF, s); + Ref<Image> img2 = Image::create_from_data(2, 2, false, Image::FORMAT_RGBAF, s); img2->convert(Image::FORMAT_RGB8); img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2)); } @@ -1449,9 +1433,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d if (denoiser.is_valid()) { for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); Ref<Image> denoised = denoiser->denoise_image(img); if (denoised != img) { @@ -1484,9 +1466,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false); } #endif @@ -1640,9 +1620,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->save_exr("res://5_blendseams" + itos(i) + ".exr", false); } #endif @@ -1652,9 +1630,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) { Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i); - Ref<Image> img; - img.instantiate(); - img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); + Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->convert(Image::FORMAT_RGBH); //remove alpha bake_textures.push_back(img); } @@ -1667,9 +1643,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d #ifdef DEBUG_TEXTURES { - Ref<Image> img2; - img2.instantiate(); - img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data); + Ref<Image> img2 = Image::create_from_data(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data); img2->save_exr("res://6_lightprobes.exr", false); } #endif diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs index 7ec3f88e5d..19fdd51dab 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotPluginsInitializerGenerator.cs @@ -56,7 +56,7 @@ namespace GodotPlugins.Game } "; - context.AddSource("GodotPlugins.Game_Generated", + context.AddSource("GodotPlugins.Game.generated", SourceText.From(source, Encoding.UTF8)); } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 1ee31eb1a9..d915eeac0b 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -87,7 +87,7 @@ namespace Godot.SourceGenerators bool isInnerClass = symbol.ContainingType != null; string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptMethods_Generated"; + + "_ScriptMethods.generated"; var source = new StringBuilder(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs index e8a9e28d0c..ccfb405d26 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs @@ -96,8 +96,8 @@ namespace Godot.SourceGenerators string.Empty; bool hasNamespace = classNs.Length != 0; - var uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptPath_Generated"; + string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() + + "_ScriptPath.generated"; var source = new StringBuilder(); @@ -126,7 +126,7 @@ namespace Godot.SourceGenerators source.Append("\n}\n"); } - context.AddSource(uniqueHint.ToString(), SourceText.From(source.ToString(), Encoding.UTF8)); + context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8)); } private static void AddScriptTypesAssemblyAttr(GeneratorExecutionContext context, @@ -157,7 +157,7 @@ namespace Godot.SourceGenerators sourceBuilder.Append("})]\n"); - context.AddSource("AssemblyScriptTypes_Generated", + context.AddSource("AssemblyScriptTypes.generated", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index b331e2e794..1198c633d9 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -73,7 +73,7 @@ namespace Godot.SourceGenerators bool isInnerClass = symbol.ContainingType != null; string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptProperties_Generated"; + + "_ScriptProperties.generated"; var source = new StringBuilder(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 65dadcb801..98b9745c16 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -73,7 +73,7 @@ namespace Godot.SourceGenerators bool isInnerClass = symbol.ContainingType != null; string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptPropertyDefVal_Generated"; + + "_ScriptPropertyDefVal.generated"; var source = new StringBuilder(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index a40220565f..11e0a6fa21 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -73,7 +73,7 @@ namespace Godot.SourceGenerators bool isInnerClass = symbol.ContainingType != null; string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptSerialization_Generated"; + + "_ScriptSerialization.generated"; var source = new StringBuilder(); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index ea7cc3fe38..4e443ce26e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -82,7 +82,7 @@ namespace Godot.SourceGenerators bool isInnerClass = symbol.ContainingType != null; string uniqueHint = symbol.FullQualifiedName().SanitizeQualifiedNameForUniqueHint() - + "_ScriptSignals_Generated"; + + "_ScriptSignals.generated"; var source = new StringBuilder(); diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index d29e0d69ab..3be8dd87c0 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -3119,9 +3119,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() { for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) { StringName enum_proxy_cname = E.key; String enum_proxy_name = enum_proxy_cname.operator String(); - if (itype.find_property_by_proxy_name(enum_proxy_cname)) { - // We have several conflicts between enums and PascalCase properties, - // so we append 'Enum' to the enum name in those cases. + if (itype.find_property_by_proxy_name(enum_proxy_name) || itype.find_method_by_proxy_name(enum_proxy_name) || itype.find_signal_by_proxy_name(enum_proxy_name)) { + // In case the enum name conflicts with other PascalCase members, + // we append 'Enum' to the enum name in those cases. + // We have several conflicts between enums and PascalCase properties. enum_proxy_name += "Enum"; enum_proxy_cname = StringName(enum_proxy_name); } @@ -3170,7 +3171,15 @@ bool BindingsGenerator::_populate_object_type_interfaces() { int64_t *value = class_info->constant_map.getptr(StringName(constant_name)); ERR_FAIL_NULL_V(value, false); - ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value); + String constant_proxy_name = snake_to_pascal_case(constant_name, true); + + if (itype.find_property_by_proxy_name(constant_proxy_name) || itype.find_method_by_proxy_name(constant_proxy_name) || itype.find_signal_by_proxy_name(constant_proxy_name)) { + // In case the constant name conflicts with other PascalCase members, + // we append 'Constant' to the constant name in those cases. + constant_proxy_name += "Constant"; + } + + ConstantInterface iconstant(constant_name, constant_proxy_name, *value); iconstant.const_doc = nullptr; for (int i = 0; i < itype.class_doc->constants.size(); i++) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs index 13070c8033..664b2e0f34 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs @@ -292,6 +292,18 @@ namespace Godot } /// <summary> + /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and + /// a <paramref name="point"/> on the plane. + /// </summary> + /// <param name="normal">The normal of the plane, must be normalized.</param> + /// <param name="point">The point on the plane.</param> + public Plane(Vector3 normal, Vector3 point) + { + _normal = normal; + D = _normal.Dot(point); + } + + /// <summary> /// Constructs a <see cref="Plane"/> from the three points, given in clockwise order. /// </summary> /// <param name="v1">The first point.</param> diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml index 62bb396d15..a7b89f58ac 100644 --- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml +++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml @@ -42,6 +42,10 @@ The root path to use for RPCs and replication. Instead of an absolute path, a relative path will be used to find the node upon which the RPC should be executed. This effectively allows to have different branches of the scene tree to be managed by different MultiplayerAPI, allowing for example to run both client and server in the same scene. </member> + <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> + Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. + [b]Note:[/b] Support for this feature may depend on the current [MultiplayerPeer] configuration. See [method MultiplayerPeer.is_server_relay_supported]. + </member> </members> <signals> <signal name="peer_packet"> diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index 9755f426d5..95857392c7 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -118,6 +118,10 @@ void MultiplayerSynchronizer::set_net_id(uint32_t p_net_id) { } bool MultiplayerSynchronizer::update_outbound_sync_time(uint64_t p_msec) { + if (last_sync_msec == p_msec) { + // last_sync_msec has been updated on this frame. + return true; + } if (p_msec >= last_sync_msec + interval_msec) { last_sync_msec = p_msec; return true; diff --git a/modules/multiplayer/scene_cache_interface.cpp b/modules/multiplayer/scene_cache_interface.cpp index b3b642f815..7df9b95b30 100644 --- a/modules/multiplayer/scene_cache_interface.cpp +++ b/modules/multiplayer/scene_cache_interface.cpp @@ -105,8 +105,7 @@ void SceneCacheInterface::process_simplify_path(int p_from, const uint8_t *p_pac multiplayer_peer->set_transfer_channel(0); multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); - multiplayer_peer->set_target_peer(p_from); - multiplayer_peer->put_packet(packet.ptr(), packet.size()); + multiplayer->send_command(p_from, packet.ptr(), packet.size()); } void SceneCacheInterface::process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -162,10 +161,9 @@ Error SceneCacheInterface::_send_confirm_path(Node *p_node, NodePath p_path, Pat Error err = OK; for (int peer_id : p_peers) { - multiplayer_peer->set_target_peer(peer_id); multiplayer_peer->set_transfer_channel(0); multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); - err = multiplayer_peer->put_packet(packet.ptr(), packet.size()); + err = multiplayer->send_command(peer_id, packet.ptr(), packet.size()); ERR_FAIL_COND_V(err != OK, err); // Insert into confirmed, but as false since it was not confirmed. psc->confirmed_peers.insert(peer_id, false); diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index 3fc1eef366..f94f8ef658 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -67,12 +67,20 @@ Error SceneMultiplayer::poll() { const uint8_t *packet; int len; + int channel = multiplayer_peer->get_packet_channel(); + MultiplayerPeer::TransferMode mode = multiplayer_peer->get_packet_mode(); + Error err = multiplayer_peer->get_packet(&packet, len); ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error getting packet! %d", err)); - remote_sender_id = sender; - _process_packet(sender, packet, len); - remote_sender_id = 0; + if (len && (packet[0] & CMD_MASK) == NETWORK_COMMAND_SYS) { + // Sys messages are processed separately since they might call _process_packet themselves. + _process_sys(sender, packet, len, mode, channel); + } else { + remote_sender_id = sender; + _process_packet(sender, packet, len); + remote_sender_id = 0; + } if (!multiplayer_peer.is_valid()) { return OK; // It's also possible that a packet or RPC caused a disconnection, so also check here. @@ -86,6 +94,7 @@ void SceneMultiplayer::clear() { connected_peers.clear(); packet_cache.clear(); cache->clear(); + relay_buffer->clear(); } void SceneMultiplayer::set_root_path(const NodePath &p_path) { @@ -166,10 +175,123 @@ void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int case NETWORK_COMMAND_SYNC: { replicator->on_sync_receive(p_from, p_packet, p_packet_len); } break; + default: { + ERR_FAIL_MSG("Invalid network command from " + itos(p_from)); + } break; + } +} + +Error SceneMultiplayer::send_command(int p_to, const uint8_t *p_packet, int p_packet_len) { + if (server_relay && get_unique_id() != 1 && p_to != 1 && multiplayer_peer->is_server_relay_supported()) { + // Send relay packet. + relay_buffer->seek(0); + relay_buffer->put_u8(NETWORK_COMMAND_SYS); + relay_buffer->put_u8(SYS_COMMAND_RELAY); + relay_buffer->put_32(p_to); // Set the destination. + relay_buffer->put_data(p_packet, p_packet_len); + multiplayer_peer->set_target_peer(1); + const Vector<uint8_t> data = relay_buffer->get_data_array(); + return multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); + } + if (p_to < 0) { + for (const int &pid : connected_peers) { + if (pid == -p_to) { + continue; + } + multiplayer_peer->set_target_peer(pid); + multiplayer_peer->put_packet(p_packet, p_packet_len); + } + return OK; + } else { + multiplayer_peer->set_target_peer(p_to); + return multiplayer_peer->put_packet(p_packet, p_packet_len); + } +} + +void SceneMultiplayer::_process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel) { + ERR_FAIL_COND_MSG(p_packet_len < SYS_CMD_SIZE, "Invalid packet received. Size too small."); + uint8_t sys_cmd_type = p_packet[1]; + int32_t peer = int32_t(decode_uint32(&p_packet[2])); + switch (sys_cmd_type) { + case SYS_COMMAND_ADD_PEER: { + ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); + _add_peer(peer); + } break; + case SYS_COMMAND_DEL_PEER: { + ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported() || get_unique_id() == 1 || p_from != 1); + _del_peer(peer); + } break; + case SYS_COMMAND_RELAY: { + ERR_FAIL_COND(!server_relay || !multiplayer_peer->is_server_relay_supported()); + ERR_FAIL_COND(p_packet_len < SYS_CMD_SIZE + 1); + const uint8_t *packet = p_packet + SYS_CMD_SIZE; + int len = p_packet_len - SYS_CMD_SIZE; + bool should_process = false; + if (get_unique_id() == 1) { // I am the server. + // Direct messages to server should not go through relay. + ERR_FAIL_COND(peer > 0 && !connected_peers.has(peer)); + // Send relay packet. + relay_buffer->seek(0); + relay_buffer->put_u8(NETWORK_COMMAND_SYS); + relay_buffer->put_u8(SYS_COMMAND_RELAY); + relay_buffer->put_32(p_from); // Set the source. + relay_buffer->put_data(packet, len); + const Vector<uint8_t> data = relay_buffer->get_data_array(); + multiplayer_peer->set_transfer_mode(p_mode); + multiplayer_peer->set_transfer_channel(p_channel); + if (peer > 0) { + multiplayer_peer->set_target_peer(peer); + multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); + } else { + for (const int &P : connected_peers) { + // Not to sender, nor excluded. + if (P == p_from || (peer < 0 && P != -peer)) { + continue; + } + multiplayer_peer->set_target_peer(P); + multiplayer_peer->put_packet(data.ptr(), relay_buffer->get_position()); + } + } + if (peer == 0 || peer == -1) { + should_process = true; + peer = p_from; // Process as the source. + } + } else { + ERR_FAIL_COND(p_from != 1); // Bug. + should_process = true; + } + if (should_process) { + remote_sender_id = peer; + _process_packet(peer, packet, len); + remote_sender_id = 0; + } + } break; + default: { + ERR_FAIL(); + } } } void SceneMultiplayer::_add_peer(int p_id) { + if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { + // Notify others of connection, and send connected peers to newly connected one. + uint8_t buf[SYS_CMD_SIZE]; + buf[0] = NETWORK_COMMAND_SYS; + buf[1] = SYS_COMMAND_ADD_PEER; + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + for (const int &P : connected_peers) { + // Send new peer to already connected. + encode_uint32(p_id, &buf[2]); + multiplayer_peer->set_target_peer(P); + multiplayer_peer->put_packet(buf, sizeof(buf)); + // Send already connected to new peer. + encode_uint32(P, &buf[2]); + multiplayer_peer->set_target_peer(p_id); + multiplayer_peer->put_packet(buf, sizeof(buf)); + } + } + connected_peers.insert(p_id); cache->on_peer_change(p_id, true); replicator->on_peer_change(p_id, true); @@ -177,6 +299,23 @@ void SceneMultiplayer::_add_peer(int p_id) { } void SceneMultiplayer::_del_peer(int p_id) { + if (server_relay && get_unique_id() == 1 && multiplayer_peer->is_server_relay_supported()) { + // Notify others of disconnection. + uint8_t buf[SYS_CMD_SIZE]; + buf[0] = NETWORK_COMMAND_SYS; + buf[1] = SYS_COMMAND_DEL_PEER; + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + encode_uint32(p_id, &buf[2]); + for (const int &P : connected_peers) { + if (P == p_id) { + continue; + } + multiplayer_peer->set_target_peer(P); + multiplayer_peer->put_packet(buf, sizeof(buf)); + } + } + replicator->on_peer_change(p_id, false); cache->on_peer_change(p_id, false); connected_peers.erase(p_id); @@ -209,11 +348,9 @@ Error SceneMultiplayer::send_bytes(Vector<uint8_t> p_data, int p_to, Multiplayer packet_cache.write[0] = NETWORK_COMMAND_RAW; memcpy(&packet_cache.write[1], &r[0], p_data.size()); - multiplayer_peer->set_target_peer(p_to); multiplayer_peer->set_transfer_channel(p_channel); multiplayer_peer->set_transfer_mode(p_mode); - - return multiplayer_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); + return send_command(p_to, packet_cache.ptr(), p_data.size() + 1); } void SceneMultiplayer::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { @@ -303,6 +440,15 @@ Error SceneMultiplayer::object_configuration_remove(Object *p_obj, Variant p_con return ERR_INVALID_PARAMETER; } +void SceneMultiplayer::set_server_relay_enabled(bool p_enabled) { + ERR_FAIL_COND_MSG(multiplayer_peer.is_valid() && multiplayer_peer->get_connection_status() != MultiplayerPeer::CONNECTION_DISCONNECTED, "Cannot change the server relay option while the multiplayer peer is active."); + server_relay = p_enabled; +} + +bool SceneMultiplayer::is_server_relay_enabled() const { + return server_relay; +} + void SceneMultiplayer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_path", "path"), &SceneMultiplayer::set_root_path); ClassDB::bind_method(D_METHOD("get_root_path"), &SceneMultiplayer::get_root_path); @@ -311,17 +457,22 @@ void SceneMultiplayer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &SceneMultiplayer::is_refusing_new_connections); ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &SceneMultiplayer::set_allow_object_decoding); ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &SceneMultiplayer::is_object_decoding_allowed); + ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &SceneMultiplayer::set_server_relay_enabled); + ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &SceneMultiplayer::is_server_relay_enabled); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &SceneMultiplayer::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_path"), "set_root_path", "get_root_path"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled"); + ADD_PROPERTY_DEFAULT("refuse_new_connections", false); ADD_SIGNAL(MethodInfo("peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); } SceneMultiplayer::SceneMultiplayer() { + relay_buffer.instantiate(); replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this))); rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this))); cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this))); diff --git a/modules/multiplayer/scene_multiplayer.h b/modules/multiplayer/scene_multiplayer.h index a99cca7b21..06d7b2c7a7 100644 --- a/modules/multiplayer/scene_multiplayer.h +++ b/modules/multiplayer/scene_multiplayer.h @@ -49,6 +49,17 @@ public: NETWORK_COMMAND_SPAWN, NETWORK_COMMAND_DESPAWN, NETWORK_COMMAND_SYNC, + NETWORK_COMMAND_SYS, + }; + + enum SysCommands { + SYS_COMMAND_ADD_PEER, + SYS_COMMAND_DEL_PEER, + SYS_COMMAND_RELAY, + }; + + enum { + SYS_CMD_SIZE = 6, // Command + sys command + peer_id (+ optional payload). }; // For each command, the 4 MSB can contain custom flags, as defined by subsystems. @@ -74,6 +85,8 @@ private: NodePath root_path; bool allow_object_decoding = false; + bool server_relay = true; + Ref<StreamPeerBuffer> relay_buffer; Ref<SceneCacheInterface> cache; Ref<SceneReplicationInterface> replicator; @@ -84,6 +97,7 @@ protected: void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); + void _process_sys(int p_from, const uint8_t *p_packet, int p_packet_len, MultiplayerPeer::TransferMode p_mode, int p_channel); void _add_peer(int p_id); void _del_peer(int p_id); @@ -111,6 +125,7 @@ public: void set_root_path(const NodePath &p_path); NodePath get_root_path() const; + Error send_command(int p_to, const uint8_t *p_packet, int p_packet_len); // Used internally to relay packets when needed. Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0); String get_rpc_md5(const Object *p_obj); @@ -123,6 +138,9 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; + void set_server_relay_enabled(bool p_enabled); + bool is_server_relay_enabled() const; + Ref<SceneCacheInterface> get_path_cache() { return cache; } #ifdef DEBUG_ENABLED diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index df9985916b..da425076a4 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -261,11 +261,11 @@ Error SceneReplicationInterface::_update_sync_visibility(int p_peer, Multiplayer if (p_peer == 0) { for (KeyValue<int, PeerInfo> &E : peers_info) { // Might be visible to this specific peer. - is_visible = is_visible || p_sync->is_visible_to(E.key); - if (is_visible == E.value.sync_nodes.has(sid)) { + bool is_visible_to_peer = is_visible || p_sync->is_visible_to(E.key); + if (is_visible_to_peer == E.value.sync_nodes.has(sid)) { continue; } - if (is_visible) { + if (is_visible_to_peer) { E.value.sync_nodes.insert(sid); } else { E.value.sync_nodes.erase(sid); @@ -370,10 +370,9 @@ Error SceneReplicationInterface::_send_raw(const uint8_t *p_buffer, int p_size, #endif Ref<MultiplayerPeer> peer = multiplayer->get_multiplayer_peer(); - peer->set_target_peer(p_peer); peer->set_transfer_channel(0); peer->set_transfer_mode(p_reliable ? MultiplayerPeer::TRANSFER_MODE_RELIABLE : MultiplayerPeer::TRANSFER_MODE_UNRELIABLE); - return peer->put_packet(p_buffer, p_size); + return multiplayer->send_command(p_peer, p_buffer, p_size); } Error SceneReplicationInterface::_make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len) { diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index 65090b9316..acc113c901 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -412,8 +412,7 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con if (has_all_peers) { // They all have verified paths, so send fast. - peer->set_target_peer(p_to); // To all of you. - peer->put_packet(packet_cache.ptr(), ofs); // A message with love. + multiplayer->send_command(p_to, packet_cache.ptr(), ofs); } else { // Unreachable because the node ID is never compressed if the peers doesn't know it. CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); @@ -438,16 +437,14 @@ void SceneRPCInterface::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, con bool confirmed = multiplayer->get_path_cache()->is_cache_confirmed(from_path, P); - peer->set_target_peer(P); // To this one specifically. - if (confirmed) { // This one confirmed path, so use id. encode_uint32(psc_id, &(packet_cache.write[1])); - peer->put_packet(packet_cache.ptr(), ofs); + multiplayer->send_command(P, packet_cache.ptr(), ofs); } else { // This one did not confirm path yet, so use entire path (sorry!). encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); // Offset to path and flag. - peer->put_packet(packet_cache.ptr(), ofs + path_len); + multiplayer->send_command(P, packet_cache.ptr(), ofs + path_len); } } } diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 23d60c4866..8c785d7591 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -176,9 +176,7 @@ Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradi int width = p_image->get_width(); int height = p_image->get_height(); - Ref<Image> new_image; - new_image.instantiate(); - new_image->create(width, height, false, Image::FORMAT_RGBA8); + Ref<Image> new_image = Image::create_empty(width, height, false, Image::FORMAT_RGBA8); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 5ac167ad98..b5978ab134 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -96,6 +96,7 @@ env_openxr.add_source_files(module_obj, "extensions/openxr_composition_layer_dep env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_hand_tracking_extension.cpp") env_openxr.add_source_files(module_obj, "extensions/openxr_fb_passthrough_extension_wrapper.cpp") +env_openxr.add_source_files(module_obj, "extensions/openxr_fb_display_refresh_rate_extension.cpp") env.modules_sources += module_obj diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp index 185e44c29d..123f860ce9 100644 --- a/modules/openxr/action_map/openxr_action_map.cpp +++ b/modules/openxr/action_map/openxr_action_map.cpp @@ -224,7 +224,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our interaction profiles Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -236,7 +236,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Vive controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -257,7 +257,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our WMR controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/microsoft/motion_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -280,7 +280,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Meta touch controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/oculus/touch_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -305,7 +305,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Valve index controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/valve/index_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -333,7 +333,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our HP MR controller profile profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/hp/mixed_reality_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -356,7 +356,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Samsung Odyssey controller profile, // Note that this controller is only identified specifically on WMR, on SteamVR this is identified as a normal WMR controller. profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/samsung/odyssey_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -379,7 +379,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Vive Cosmos controller profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_cosmos_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -403,7 +403,7 @@ void OpenXRActionMap::create_default_action_sets() { // Note, Vive Focus 3 currently is not yet supported as a stand alone device // however HTC currently has a beta OpenXR runtime in testing we may support in the near future profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_focus3_controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); @@ -427,7 +427,7 @@ void OpenXRActionMap::create_default_action_sets() { // Create our Huawei controller profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/huawei/controller"); - profile->add_new_binding(default_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); + profile->add_new_binding(default_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(aim_pose, "/user/hand/left/input/aim/pose,/user/hand/right/input/aim/pose"); profile->add_new_binding(grip_pose, "/user/hand/left/input/grip/pose,/user/hand/right/input/grip/pose"); profile->add_new_binding(palm_pose, "/user/hand/left/input/palm_ext/pose,/user/hand/right/input/palm_ext/pose"); diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 25bf496de9..f089fd066e 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -10,6 +10,19 @@ <tutorials> <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> + <methods> + <method name="get_available_display_refresh_rates" qualifiers="const"> + <return type="Array" /> + <description> + Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized. + </description> + </method> + </methods> + <members> + <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> + The display refresh rate for the current HMD. Only functional if this feature is supported by the OpenXR runtime and after the interface has been initialized. + </member> + </members> <signals> <signal name="pose_recentered"> <description> diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp new file mode 100644 index 0000000000..c0bbaea5b4 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* openxr_fb_display_refresh_rate_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "openxr_fb_display_refresh_rate_extension.h" + +OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::singleton = nullptr; + +OpenXRDisplayRefreshRateExtension *OpenXRDisplayRefreshRateExtension::get_singleton() { + return singleton; +} + +OpenXRDisplayRefreshRateExtension::OpenXRDisplayRefreshRateExtension(OpenXRAPI *p_openxr_api) : + OpenXRExtensionWrapper(p_openxr_api) { + singleton = this; + + // Extensions we use for our hand tracking. + request_extensions[XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME] = &display_refresh_rate_ext; +} + +OpenXRDisplayRefreshRateExtension::~OpenXRDisplayRefreshRateExtension() { + display_refresh_rate_ext = false; +} + +void OpenXRDisplayRefreshRateExtension::on_instance_created(const XrInstance p_instance) { + if (display_refresh_rate_ext) { + EXT_INIT_XR_FUNC(xrEnumerateDisplayRefreshRatesFB); + EXT_INIT_XR_FUNC(xrGetDisplayRefreshRateFB); + EXT_INIT_XR_FUNC(xrRequestDisplayRefreshRateFB); + } +} + +void OpenXRDisplayRefreshRateExtension::on_instance_destroyed() { + display_refresh_rate_ext = false; +} + +float OpenXRDisplayRefreshRateExtension::get_refresh_rate() const { + float refresh_rate = 0.0; + + if (display_refresh_rate_ext) { + float rate; + XrResult result = xrGetDisplayRefreshRateFB(openxr_api->get_session(), &rate); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rate [", openxr_api->get_error_string(result), "]"); + } else { + refresh_rate = rate; + } + } + + return refresh_rate; +} + +void OpenXRDisplayRefreshRateExtension::set_refresh_rate(float p_refresh_rate) { + if (display_refresh_rate_ext) { + XrResult result = xrRequestDisplayRefreshRateFB(openxr_api->get_session(), p_refresh_rate); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to set refresh rate [", openxr_api->get_error_string(result), "]"); + } + } +} + +Array OpenXRDisplayRefreshRateExtension::get_available_refresh_rates() const { + Array arr; + XrResult result; + + if (display_refresh_rate_ext) { + uint32_t display_refresh_rate_count = 0; + result = xrEnumerateDisplayRefreshRatesFB(openxr_api->get_session(), 0, &display_refresh_rate_count, nullptr); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rates count [", openxr_api->get_error_string(result), "]"); + } + + if (display_refresh_rate_count > 0) { + float *display_refresh_rates = (float *)memalloc(sizeof(float) * display_refresh_rate_count); + if (display_refresh_rates == nullptr) { + print_line("OpenXR: Failed to obtain refresh rates memory buffer [", openxr_api->get_error_string(result), "]"); + return arr; + } + + result = xrEnumerateDisplayRefreshRatesFB(openxr_api->get_session(), display_refresh_rate_count, &display_refresh_rate_count, display_refresh_rates); + if (XR_FAILED(result)) { + print_line("OpenXR: Failed to obtain refresh rates count [", openxr_api->get_error_string(result), "]"); + memfree(display_refresh_rates); + return arr; + } + + for (uint32_t i = 0; i < display_refresh_rate_count; i++) { + float refresh_rate = display_refresh_rates[i]; + arr.push_back(Variant(refresh_rate)); + } + + memfree(display_refresh_rates); + } + } + + return arr; +} diff --git a/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h new file mode 100644 index 0000000000..dcd52fe4d1 --- /dev/null +++ b/modules/openxr/extensions/openxr_fb_display_refresh_rate_extension.h @@ -0,0 +1,70 @@ +/*************************************************************************/ +/* openxr_fb_display_refresh_rate_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H +#define OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H + +// This extension gives us access to the possible display refresh rates +// supported by the HMD. +// While this is an FB extension it has been adopted by most runtimes and +// will likely become core in the near future. + +#include "../openxr_api.h" +#include "../util.h" + +#include "openxr_extension_wrapper.h" + +class OpenXRDisplayRefreshRateExtension : public OpenXRExtensionWrapper { +public: + static OpenXRDisplayRefreshRateExtension *get_singleton(); + + OpenXRDisplayRefreshRateExtension(OpenXRAPI *p_openxr_api); + virtual ~OpenXRDisplayRefreshRateExtension() override; + + virtual void on_instance_created(const XrInstance p_instance) override; + virtual void on_instance_destroyed() override; + + float get_refresh_rate() const; + void set_refresh_rate(float p_refresh_rate); + + Array get_available_refresh_rates() const; + +private: + static OpenXRDisplayRefreshRateExtension *singleton; + + bool display_refresh_rate_ext = false; + + // OpenXR API call wrappers + EXT_PROTO_XRRESULT_FUNC4(xrEnumerateDisplayRefreshRatesFB, (XrSession), session, (uint32_t), displayRefreshRateCapacityInput, (uint32_t *), displayRefreshRateCountOutput, (float *), displayRefreshRates); + EXT_PROTO_XRRESULT_FUNC2(xrGetDisplayRefreshRateFB, (XrSession), session, (float *), display_refresh_rate); + EXT_PROTO_XRRESULT_FUNC2(xrRequestDisplayRefreshRateFB, (XrSession), session, (float), display_refresh_rate); +}; + +#endif // OPENXR_FB_DISPLAY_REFRESH_RATE_EXTENSION_H diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp index 88cc7c061c..4d996e6283 100644 --- a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp +++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp @@ -69,6 +69,34 @@ bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &eve bool OpenXRHTCViveTrackerExtension::is_path_supported(const String &p_path) { if (p_path == "/interaction_profiles/htc/vive_tracker_htcx") { return available; + } else if (p_path == "/user/vive_tracker_htcx/role/handheld_object") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_foot") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_foot") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_shoulder") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_shoulder") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_elbow") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_elbow") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/left_knee") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/right_knee") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/waist") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/chest") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/chest") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/camera") { + return available; + } else if (p_path == "/user/vive_tracker_htcx/role/keyboard") { + return available; } // Not a path under this extensions control, so we return true; diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 8a67462613..1ff1dac512 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -50,6 +50,7 @@ #endif #include "extensions/openxr_composition_layer_depth_extension.h" +#include "extensions/openxr_fb_display_refresh_rate_extension.h" #include "extensions/openxr_fb_passthrough_extension_wrapper.h" #include "extensions/openxr_hand_tracking_extension.h" #include "extensions/openxr_htc_vive_tracker_extension.h" @@ -443,12 +444,12 @@ bool OpenXRAPI::load_supported_view_configuration_views(XrViewConfigurationType for (uint32_t i = 0; i < view_count; i++) { print_verbose("OpenXR: Found supported view configuration view"); - print_verbose(String(" - width: ") + view_configuration_views[i].maxImageRectWidth); - print_verbose(String(" - height: ") + view_configuration_views[i].maxImageRectHeight); - print_verbose(String(" - sample count: ") + view_configuration_views[i].maxSwapchainSampleCount); - print_verbose(String(" - recommended render width: ") + view_configuration_views[i].recommendedImageRectWidth); - print_verbose(String(" - recommended render height: ") + view_configuration_views[i].recommendedImageRectHeight); - print_verbose(String(" - recommended render sample count: ") + view_configuration_views[i].recommendedSwapchainSampleCount); + print_verbose(String(" - width: ") + itos(view_configuration_views[i].maxImageRectWidth)); + print_verbose(String(" - height: ") + itos(view_configuration_views[i].maxImageRectHeight)); + print_verbose(String(" - sample count: ") + itos(view_configuration_views[i].maxSwapchainSampleCount)); + print_verbose(String(" - recommended render width: ") + itos(view_configuration_views[i].recommendedImageRectWidth)); + print_verbose(String(" - recommended render height: ") + itos(view_configuration_views[i].recommendedImageRectHeight)); + print_verbose(String(" - recommended render sample count: ") + itos(view_configuration_views[i].recommendedSwapchainSampleCount)); } return true; @@ -1748,6 +1749,31 @@ void OpenXRAPI::end_frame() { } } +float OpenXRAPI::get_display_refresh_rate() const { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext) { + return drrext->get_refresh_rate(); + } + + return 0.0; +} + +void OpenXRAPI::set_display_refresh_rate(float p_refresh_rate) { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext != nullptr) { + drrext->set_refresh_rate(p_refresh_rate); + } +} + +Array OpenXRAPI::get_available_display_refresh_rates() const { + OpenXRDisplayRefreshRateExtension *drrext = OpenXRDisplayRefreshRateExtension::get_singleton(); + if (drrext != nullptr) { + return drrext->get_available_refresh_rates(); + } + + return Array(); +} + OpenXRAPI::OpenXRAPI() { // OpenXRAPI is only constructed if OpenXR is enabled. singleton = this; @@ -1817,6 +1843,7 @@ OpenXRAPI::OpenXRAPI() { register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this))); register_extension_wrapper(memnew(OpenXRHandTrackingExtension(this))); register_extension_wrapper(memnew(OpenXRFbPassthroughExtensionWrapper(this))); + register_extension_wrapper(memnew(OpenXRDisplayRefreshRateExtension(this))); } OpenXRAPI::~OpenXRAPI() { diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index bd69432dcb..5dce749351 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -84,8 +84,6 @@ private: bool ext_vive_focus3_available = false; bool ext_huawei_controller_available = false; - bool is_path_supported(const String &p_path); - // composition layer providers Vector<OpenXRCompositionLayerProvider *> composition_layer_providers; @@ -302,6 +300,7 @@ public: void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity); bool xr_result(XrResult result, const char *format, Array args = Array()) const; + bool is_path_supported(const String &p_path); static bool openxr_is_enabled(bool p_check_run_in_editor = true); static OpenXRAPI *get_singleton(); @@ -336,6 +335,11 @@ public: void post_draw_viewport(RID p_render_target); void end_frame(); + // Display refresh rate + float get_display_refresh_rate() const; + void set_display_refresh_rate(float p_refresh_rate); + Array get_available_display_refresh_rates() const; + // action map String get_default_action_map_resource_name(); diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index 68414ae84e..bdf437b0b7 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -41,6 +41,13 @@ void OpenXRInterface::_bind_methods() { ADD_SIGNAL(MethodInfo("session_focussed")); ADD_SIGNAL(MethodInfo("session_visible")); ADD_SIGNAL(MethodInfo("pose_recentered")); + + // Display refresh rate + ClassDB::bind_method(D_METHOD("get_display_refresh_rate"), &OpenXRInterface::get_display_refresh_rate); + ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); + + ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); } StringName OpenXRInterface::get_name() const { @@ -147,24 +154,25 @@ void OpenXRInterface::_load_action_map() { Ref<OpenXRAction> xr_action = actions[j]; PackedStringArray toplevel_paths = xr_action->get_toplevel_paths(); - Vector<Tracker *> trackers_new; + Vector<Tracker *> trackers_for_action; for (int k = 0; k < toplevel_paths.size(); k++) { - Tracker *tracker = find_tracker(toplevel_paths[k], true); - if (tracker) { - trackers_new.push_back(tracker); + // Only check for our tracker if our path is supported. + if (openxr_api->is_path_supported(toplevel_paths[k])) { + Tracker *tracker = find_tracker(toplevel_paths[k], true); + if (tracker) { + trackers_for_action.push_back(tracker); + } } } - Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers); - if (action) { - // we link our actions back to our trackers so we know which actions to check when we're processing our trackers - for (int t = 0; t < trackers_new.size(); t++) { - link_action_to_tracker(trackers_new[t], action); + // Only add our action if we have atleast one valid toplevel path + if (trackers_for_action.size() > 0) { + Action *action = create_action(action_set, xr_action->get_name(), xr_action->get_localized_name(), xr_action->get_action_type(), trackers_for_action); + if (action) { + // add this to our map for creating our interaction profiles + xr_actions[xr_action] = action; } - - // add this to our map for creating our interaction profiles - xr_actions[xr_action] = action; } } } @@ -282,6 +290,13 @@ OpenXRInterface::Action *OpenXRInterface::create_action(ActionSet *p_action_set, action->action_rid = openxr_api->action_create(p_action_set->action_set_rid, p_action_name, p_localized_name, p_action_type, tracker_rids); p_action_set->actions.push_back(action); + // we link our actions back to our trackers so we know which actions to check when we're processing our trackers + for (int i = 0; i < p_trackers.size(); i++) { + if (p_trackers[i]->actions.find(action) == -1) { + p_trackers[i]->actions.push_back(action); + } + } + return action; } @@ -330,6 +345,8 @@ OpenXRInterface::Tracker *OpenXRInterface::find_tracker(const String &p_tracker_ return nullptr; } + ERR_FAIL_COND_V(!openxr_api->is_path_supported(p_tracker_name), nullptr); + // Create our RID RID tracker_rid = openxr_api->tracker_create(p_tracker_name); ERR_FAIL_COND_V(tracker_rid.is_null(), nullptr); @@ -389,12 +406,6 @@ void OpenXRInterface::tracker_profile_changed(RID p_tracker, RID p_interaction_p } } -void OpenXRInterface::link_action_to_tracker(Tracker *p_tracker, Action *p_action) { - if (p_tracker->actions.find(p_action) == -1) { - p_tracker->actions.push_back(p_action); - } -} - void OpenXRInterface::handle_tracker(Tracker *p_tracker) { ERR_FAIL_NULL(openxr_api); ERR_FAIL_COND(p_tracker->positional_tracker.is_null()); @@ -447,9 +458,18 @@ void OpenXRInterface::handle_tracker(Tracker *p_tracker) { void OpenXRInterface::trigger_haptic_pulse(const String &p_action_name, const StringName &p_tracker_name, double p_frequency, double p_amplitude, double p_duration_sec, double p_delay_sec) { ERR_FAIL_NULL(openxr_api); + Action *action = find_action(p_action_name); ERR_FAIL_NULL(action); - Tracker *tracker = find_tracker(p_tracker_name); + + // We need to map our tracker name to our OpenXR name for our inbuild names. + String tracker_name = p_tracker_name; + if (tracker_name == "left_hand") { + tracker_name = "/user/hand/left"; + } else if (tracker_name == "right_hand") { + tracker_name = "/user/hand/right"; + } + Tracker *tracker = find_tracker(tracker_name); ERR_FAIL_NULL(tracker); // TODO OpenXR does not support delay, so we may need to add support for that somehow... @@ -571,6 +591,36 @@ bool OpenXRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) { return false; } +float OpenXRInterface::get_display_refresh_rate() const { + if (openxr_api == nullptr) { + return 0.0; + } else if (!openxr_api->is_initialized()) { + return 0.0; + } else { + return openxr_api->get_display_refresh_rate(); + } +} + +void OpenXRInterface::set_display_refresh_rate(float p_refresh_rate) { + if (openxr_api == nullptr) { + return; + } else if (!openxr_api->is_initialized()) { + return; + } else { + openxr_api->set_display_refresh_rate(p_refresh_rate); + } +} + +Array OpenXRInterface::get_available_display_refresh_rates() const { + if (openxr_api == nullptr) { + return Array(); + } else if (!openxr_api->is_initialized()) { + return Array(); + } else { + return openxr_api->get_available_display_refresh_rates(); + } +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 72935b039c..454612346f 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -91,7 +91,6 @@ private: void free_actions(ActionSet *p_action_set); Tracker *find_tracker(const String &p_tracker_name, bool p_create = false); - void link_action_to_tracker(Tracker *p_tracker, Action *p_action); void handle_tracker(Tracker *p_tracker); void free_trackers(); @@ -120,6 +119,10 @@ public: virtual XRInterface::PlayAreaMode get_play_area_mode() const override; virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override; + float get_display_refresh_rate() const; + void set_display_refresh_rate(float p_refresh_rate); + Array get_available_display_refresh_rates() const; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 56404f796c..2abfc93722 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -4,7 +4,7 @@ Class for searching text for patterns using regular expressions. </brief_description> <description> - A regular expression (or regex) is a compact language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For instance, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. + A regular expression (or regex) is a compact language that can be used to recognise strings that follow a specific pattern, such as URLs, email addresses, complete sentences, etc. For example, a regex of [code]ab[0-9][/code] would find any string that is [code]ab[/code] followed by any number from [code]0[/code] to [code]9[/code]. For a more in-depth look, you can easily find various tutorials and detailed explanations on the Internet. To begin, the RegEx object needs to be compiled with the search pattern using [method compile] before it can be used. [codeblock] var regex = RegEx.new() diff --git a/modules/squish/image_decompress_squish.cpp b/modules/squish/image_decompress_squish.cpp index 3a810e5259..2abd9a0c69 100644 --- a/modules/squish/image_decompress_squish.cpp +++ b/modules/squish/image_decompress_squish.cpp @@ -70,7 +70,7 @@ void image_decompress_squish(Image *p_image) { h >>= 1; } - p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); + p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data); if (p_image->get_format() == Image::FORMAT_DXT5_RA_AS_RG) { p_image->convert_ra_rgba8_to_rg(); diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index f43f2784c7..b8c412a201 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -135,7 +135,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin res = sw_canvas->clear(true); memfree(buffer); - p_image->create(width, height, false, Image::FORMAT_RGBA8, image); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, image); } void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const { diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index c9b0fa7dd5..0929d3a2b0 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -810,12 +810,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_ ret.y = 0x7fffffff; ret.x = 0; + const int *ct_offsets_ptr = ct.offsets.ptr(); for (int j = 0; j < ct.texture_w - mw; j++) { int max_y = 0; - for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; + int y = ct_offsets_ptr[k]; if (y > max_y) { max_y = y; } @@ -1393,7 +1393,10 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f int error = 0; if (!ft_library) { error = FT_Init_FreeType(&ft_library); - ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + if (error != 0) { + memdelete(fd); + ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + } } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -1422,6 +1425,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f if (error) { FT_Done_Face(fd->face); fd->face = nullptr; + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } @@ -1835,6 +1839,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_f FT_Done_MM_Var(ft_library, amaster); } #else + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } else { @@ -2494,9 +2499,7 @@ void TextServerAdvanced::_font_set_texture_image(const RID &p_font_rid, const Ve tex.texture_h = p_image->get_height(); tex.format = p_image->get_format(); - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2515,11 +2518,7 @@ Ref<Image> TextServerAdvanced::_font_get_texture_image(const RID &p_font_rid, co ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); - - return img; + return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); } void TextServerAdvanced::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { @@ -2853,9 +2852,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2901,9 +2898,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3248,9 +3243,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -3340,9 +3333,7 @@ void TextServerAdvanced::_font_draw_glyph_outline(const RID &p_font_rid, const R if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 518c877baa..4a46e17868 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -233,12 +233,13 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_ ret.y = 0x7fffffff; ret.x = 0; + const int *ct_offsets_ptr = ct.offsets.ptr(); for (int j = 0; j < ct.texture_w - mw; j++) { int max_y = 0; for (int k = j; k < j + mw; k++) { - int y = ct.offsets[k]; + int y = ct_offsets_ptr[k]; if (y > max_y) { max_y = y; } @@ -818,7 +819,10 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f int error = 0; if (!ft_library) { error = FT_Init_FreeType(&ft_library); - ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + if (error != 0) { + memdelete(fd); + ERR_FAIL_V_MSG(false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'."); + } } memset(&fd->stream, 0, sizeof(FT_StreamRec)); @@ -847,6 +851,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f if (error) { FT_Done_Face(fd->face); fd->face = nullptr; + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'."); } @@ -945,6 +950,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f FT_Done_MM_Var(ft_library, amaster); } #else + memdelete(fd); ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!"); #endif } @@ -1588,9 +1594,7 @@ void TextServerFallback::_font_set_texture_image(const RID &p_font_rid, const Ve tex.texture_h = p_image->get_height(); tex.format = p_image->get_format(); - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -1609,11 +1613,7 @@ Ref<Image> TextServerFallback::_font_get_texture_image(const RID &p_font_rid, co ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>()); const FontTexture &tex = fd->cache[size]->textures[p_texture_index]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); - - return img; + return Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); } void TextServerFallback::_font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) { @@ -1933,9 +1933,7 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -1981,9 +1979,7 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co if (gl[p_glyph | mod].texture_idx != -1) { if (fd->cache[size]->textures[gl[p_glyph | mod].texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl[p_glyph | mod].texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2310,9 +2306,7 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } @@ -2402,9 +2396,7 @@ void TextServerFallback::_font_draw_glyph_outline(const RID &p_font_rid, const R if (RenderingServer::get_singleton() != nullptr) { if (fd->cache[size]->textures[gl.texture_idx].dirty) { FontTexture &tex = fd->cache[size]->textures.write[gl.texture_idx]; - Ref<Image> img; - img.instantiate(); - img->create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); + Ref<Image> img = Image::create_from_data(tex.texture_w, tex.texture_h, false, tex.format, tex.imgdata); if (fd->mipmaps) { img->generate_mipmaps(); } diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp index aed95294e7..a6fc650414 100644 --- a/modules/tga/image_loader_tga.cpp +++ b/modules/tga/image_loader_tga.cpp @@ -246,7 +246,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff } } - p_image->create(width, height, false, Image::FORMAT_RGBA8, image_data); + p_image->initialize_data(width, height, false, Image::FORMAT_RGBA8, image_data); return OK; } diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 1284412cd8..69fb079970 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -336,9 +336,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { size.x = w; size.y = h; - Ref<Image> img; - img.instantiate(); - img->create(w, h, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(w, h, false, Image::FORMAT_RGBA8); texture->set_image(img); } else { diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp index 5c43bfc8b7..c5aa110fe6 100644 --- a/modules/tinyexr/image_loader_tinyexr.cpp +++ b/modules/tinyexr/image_loader_tinyexr.cpp @@ -280,7 +280,7 @@ Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitF } } - p_image->create(exr_image.width, exr_image.height, false, format, imgdata); + p_image->set_data(exr_image.width, exr_image.height, false, format, imgdata); FreeEXRHeader(&exr_header); FreeEXRImage(&exr_image); diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index 4888dca822..d4054948f6 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -41,7 +41,7 @@ func _ready(): thread = Thread.new() - thread.start(self, "_upnp_setup", SERVER_PORT) + thread.start(_upnp_setup.bind(SERVER_PORT)) func _exit_tree(): # Wait for thread finish here to handle game exit while the thread is running. diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp index bd71c2869a..56ff8097b4 100644 --- a/modules/webp/resource_saver_webp.cpp +++ b/modules/webp/resource_saver_webp.cpp @@ -38,8 +38,8 @@ Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { Ref<ImageTexture> texture = p_resource; - ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP."); - ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP."); + ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WebP."); + ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WebP."); Ref<Image> img = texture->get_image(); @@ -52,7 +52,7 @@ Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_im Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality); Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path)); + ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WebP at path: '%s'.", p_path)); const uint8_t *reader = buffer.ptr(); diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp index 8657a98853..c6440f1796 100644 --- a/modules/webp/webp_common.cpp +++ b/modules/webp/webp_common.cpp @@ -139,7 +139,7 @@ Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) { ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>()); WebPBitstreamFeatures features; if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) { - ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image."); + ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WebP image."); } Vector<uint8_t> dst_image; @@ -183,7 +183,7 @@ Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image."); - p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image); + p_image->set_data(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image); return OK; } diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index 927888fe21..0b42c6ed35 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -6,7 +6,7 @@ <description> This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer]. You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections. - [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer]. + [signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless the peer is created using [method create_client]. Beside that data transfer works like in a [MultiplayerPeer]. [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android. </description> <tutorials> @@ -28,6 +28,31 @@ Close all the add peer connections and channels, freeing all resources. </description> </method> + <method name="create_client"> + <return type="int" enum="Error" /> + <param index="0" name="peer_id" type="int" /> + <param index="1" name="channels_config" type="Array" default="[]" /> + <description> + Initialize the multiplayer peer as a client with the given [code]peer_id[/code] (must be between 2 and 2147483647). In this mode, you should only call [method add_peer] once and with [code]peer_id[/code] of [code]1[/code]. This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying. + You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel). + </description> + </method> + <method name="create_mesh"> + <return type="int" enum="Error" /> + <param index="0" name="peer_id" type="int" /> + <param index="1" name="channels_config" type="Array" default="[]" /> + <description> + Initialize the multiplayer peer as a mesh (i.e. all peers connect to each other) with the given [code]peer_id[/code] (must be between 1 and 2147483647). + </description> + </method> + <method name="create_server"> + <return type="int" enum="Error" /> + <param index="0" name="channels_config" type="Array" default="[]" /> + <description> + Initialize the multiplayer peer as a server (with unique ID of [code]1[/code]). This mode enables [method MultiplayerPeer.is_server_relay_supported], allowing the upper [MultiplayerAPI] layer to perform peer exchange and packet relaying. + You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel). + </description> + </method> <method name="get_peer"> <return type="Dictionary" /> <param index="0" name="peer_id" type="int" /> @@ -48,18 +73,6 @@ Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though). </description> </method> - <method name="initialize"> - <return type="int" enum="Error" /> - <param index="0" name="peer_id" type="int" /> - <param index="1" name="server_compatibility" type="bool" default="false" /> - <param index="2" name="channels_config" type="Array" default="[]" /> - <description> - Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647). - If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted. - If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED]. - You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel). - </description> - </method> <method name="remove_peer"> <return type="void" /> <param index="0" name="peer_id" type="int" /> diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp index e03b6b2473..5ea81d5a1b 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.cpp +++ b/modules/webrtc/webrtc_multiplayer_peer.cpp @@ -34,7 +34,9 @@ #include "core/os/os.h" void WebRTCMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("create_server", "channels_config"), &WebRTCMultiplayerPeer::create_server, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("create_client", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_client, DEFVAL(Array())); + ClassDB::bind_method(D_METHOD("create_mesh", "peer_id", "channels_config"), &WebRTCMultiplayerPeer::create_mesh, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1)); ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer); ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer); @@ -52,6 +54,15 @@ int WebRTCMultiplayerPeer::get_packet_peer() const { return next_packet_peer; } +int WebRTCMultiplayerPeer::get_packet_channel() const { + return next_packet_channel < CH_RESERVED_MAX ? 0 : next_packet_channel - CH_RESERVED_MAX + 1; +} + +MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_packet_mode() const { + ERR_FAIL_INDEX_V(next_packet_channel, channels_modes.size(), TRANSFER_MODE_RELIABLE); + return channels_modes[next_packet_channel]; +} + bool WebRTCMultiplayerPeer::is_server() const { return unique_id == TARGET_PEER_SERVER; } @@ -113,24 +124,14 @@ void WebRTCMultiplayerPeer::poll() { // Signal newly connected peers for (int &E : add) { // Already connected to server: simply notify new peer. - // NOTE: Mesh is always connected. - if (connection_status == CONNECTION_CONNECTED) { - emit_signal(SNAME("peer_connected"), E); - } - - // Server emulation mode suppresses peer_conencted until server connects. - if (server_compat && E == TARGET_PEER_SERVER) { + if (network_mode == MODE_CLIENT) { + ERR_CONTINUE(E != TARGET_PEER_SERVER); // Bug. // Server connected. connection_status = CONNECTION_CONNECTED; emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER); emit_signal(SNAME("connection_succeeded")); - // Notify of all previously connected peers - for (const KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) { - if (F.key != 1 && F.value->connected) { - emit_signal(SNAME("peer_connected"), F.key); - } - } - break; // Because we already notified of all newly added peers. + } else { + emit_signal(SNAME("peer_connected"), E); } } // Fetch next packet @@ -150,11 +151,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() { ++E; continue; } + int idx = 0; for (const Ref<WebRTCDataChannel> &F : E->value->channels) { if (F->get_available_packet_count()) { + next_packet_channel = idx; next_packet_peer = E->key; return; } + idx++; } ++E; } @@ -165,11 +169,14 @@ void WebRTCMultiplayerPeer::_find_next_peer() { ++E; continue; } + int idx = 0; for (const Ref<WebRTCDataChannel> &F : E->value->channels) { if (F->get_available_packet_count()) { + next_packet_channel = idx; next_packet_peer = E->key; return; } + idx++; } if (E->key == (int)next_packet_peer) { break; @@ -177,6 +184,7 @@ void WebRTCMultiplayerPeer::_find_next_peer() { ++E; } // No packet found + next_packet_channel = 0; next_packet_peer = 0; } @@ -184,11 +192,28 @@ MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() return connection_status; } -Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) { +Error WebRTCMultiplayerPeer::create_server(Array p_channels_config) { + return _initialize(1, MODE_SERVER, p_channels_config); +} + +Error WebRTCMultiplayerPeer::create_client(int p_self_id, Array p_channels_config) { + ERR_FAIL_COND_V_MSG(p_self_id == 1, ERR_INVALID_PARAMETER, "Clients cannot have ID 1."); + return _initialize(p_self_id, MODE_CLIENT, p_channels_config); +} + +Error WebRTCMultiplayerPeer::create_mesh(int p_self_id, Array p_channels_config) { + return _initialize(p_self_id, MODE_MESH, p_channels_config); +} + +Error WebRTCMultiplayerPeer::_initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config) { ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER); channels_config.clear(); + channels_modes.clear(); + channels_modes.push_back(TRANSFER_MODE_RELIABLE); + channels_modes.push_back(TRANSFER_MODE_UNRELIABLE_ORDERED); + channels_modes.push_back(TRANSFER_MODE_UNRELIABLE); for (int i = 0; i < p_channels_config.size(); i++) { - ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'"); + ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'"); int mode = p_channels_config[i].operator int(); // Initialize data channel configurations. Dictionary cfg; @@ -207,16 +232,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr case TRANSFER_MODE_RELIABLE: break; default: - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode)); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode)); } channels_config.push_back(cfg); + channels_modes.push_back((TransferMode)mode); } unique_id = p_self_id; - server_compat = p_server_compat; + network_mode = p_mode; // Mesh and server are always connected - if (!server_compat || p_self_id == 1) { + if (p_mode != MODE_CLIENT) { connection_status = CONNECTION_CONNECTED; } else { connection_status = CONNECTION_CONNECTING; @@ -224,6 +250,10 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr return OK; } +bool WebRTCMultiplayerPeer::is_server_relay_supported() const { + return network_mode == MODE_SERVER || network_mode == MODE_CLIENT; +} + int WebRTCMultiplayerPeer::get_unique_id() const { ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1); return unique_id; @@ -261,7 +291,10 @@ Dictionary WebRTCMultiplayerPeer::get_peers() { } Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) { - ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(network_mode == MODE_NONE, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(network_mode == MODE_CLIENT && p_peer_id != 1, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(network_mode == MODE_SERVER && p_peer_id == 1, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_peer_id < 1 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED); // Peer must be valid, and in new state (to create data channels) @@ -308,8 +341,12 @@ void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) { if (peer->connected) { peer->connected = false; emit_signal(SNAME("peer_disconnected"), p_peer_id); - if (server_compat && p_peer_id == TARGET_PEER_SERVER) { - emit_signal(SNAME("server_disconnected")); + if (network_mode == MODE_CLIENT && p_peer_id == TARGET_PEER_SERVER) { + if (connection_status == CONNECTION_CONNECTING) { + emit_signal(SNAME("connection_failed")); + } else { + emit_signal(SNAME("server_disconnected")); + } connection_status = CONNECTION_DISCONNECTED; } } @@ -403,7 +440,9 @@ void WebRTCMultiplayerPeer::close() { channels_config.clear(); unique_id = 0; next_packet_peer = 0; + next_packet_channel = 0; target_peer = 0; + network_mode = MODE_NONE; connection_status = CONNECTION_DISCONNECTED; } diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h index ea7c60036b..3f608200fd 100644 --- a/modules/webrtc/webrtc_multiplayer_peer.h +++ b/modules/webrtc/webrtc_multiplayer_peer.h @@ -48,6 +48,13 @@ private: CH_RESERVED_MAX = 3 }; + enum NetworkMode { + MODE_NONE, + MODE_SERVER, + MODE_CLIENT, + MODE_MESH, + }; + class ConnectedPeer : public RefCounted { public: Ref<WebRTCPeerConnection> connection; @@ -67,19 +74,25 @@ private: int client_count = 0; ConnectionStatus connection_status = CONNECTION_DISCONNECTED; int next_packet_peer = 0; - bool server_compat = false; + int next_packet_channel = 0; + NetworkMode network_mode = MODE_NONE; HashMap<int, Ref<ConnectedPeer>> peer_map; + List<TransferMode> channels_modes; List<Dictionary> channels_config; void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict); void _find_next_peer(); + Ref<ConnectedPeer> _get_next_peer(); + Error _initialize(int p_self_id, NetworkMode p_mode, Array p_channels_config = Array()); public: WebRTCMultiplayerPeer() {} ~WebRTCMultiplayerPeer(); - Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array()); + Error create_server(Array p_channels_config = Array()); + Error create_client(int p_self_id, Array p_channels_config = Array()); + Error create_mesh(int p_self_id, Array p_channels_config = Array()); Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1); void remove_peer(int p_peer_id); bool has_peer(int p_peer_id); @@ -98,8 +111,11 @@ public: int get_unique_id() const override; int get_packet_peer() const override; + int get_packet_channel() const override; + TransferMode get_packet_mode() const override; bool is_server() const override; + bool is_server_relay_supported() const override; void poll() override; diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index c314ebd049..0202c5bac7 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -142,12 +142,21 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(get_connection_status() != CONNECTION_CONNECTED, ERR_UNCONFIGURED); - Vector<uint8_t> buffer = _make_pkt(SYS_NONE, get_unique_id(), target_peer, p_buffer, p_buffer_size); - if (is_server()) { - return _server_relay(1, target_peer, &(buffer.ptr()[0]), buffer.size()); + if (target_peer > 0) { + ERR_FAIL_COND_V_MSG(!peers_map.has(target_peer), ERR_INVALID_PARAMETER, "Peer not found: " + itos(target_peer)); + get_peer(target_peer)->put_packet(p_buffer, p_buffer_size); + } else { + for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) { + if (target_peer && -target_peer == E.key) { + continue; // Excluded. + } + E.value->put_packet(p_buffer, p_buffer_size); + } + } + return OK; } else { - return get_peer(1)->put_packet(&(buffer.ptr()[0]), buffer.size()); + return get_peer(1)->put_packet(p_buffer, p_buffer_size); } } @@ -219,8 +228,41 @@ void WebSocketMultiplayerPeer::_poll_client() { peer->poll(); // Update state and fetch packets. WebSocketPeer::State ready_state = peer->get_ready_state(); if (ready_state == WebSocketPeer::STATE_OPEN) { - while (peer->get_available_packet_count()) { - _process_multiplayer(peer, 1); + if (connection_status == CONNECTION_CONNECTING) { + if (peer->get_available_packet_count() > 0) { + const uint8_t *in_buffer; + int size = 0; + Error err = peer->get_packet(&in_buffer, size); + if (err != OK || size != 4) { + peer->close(); // Will cause connection error on next poll. + ERR_FAIL_MSG("Invalid ID received from server"); + } + unique_id = *((int32_t *)in_buffer); + if (unique_id < 2) { + peer->close(); // Will cause connection error on next poll. + ERR_FAIL_MSG("Invalid ID received from server"); + } + connection_status = CONNECTION_CONNECTED; + emit_signal("peer_connected", 1); + emit_signal("connection_succeeded"); + } else { + return; // Still waiting for an ID. + } + } + int pkts = peer->get_available_packet_count(); + while (pkts > 0 && peer->get_ready_state() == WebSocketPeer::STATE_OPEN) { + const uint8_t *in_buffer; + int size = 0; + Error err = peer->get_packet(&in_buffer, size); + ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(size <= 0); + Packet packet; + packet.data = (uint8_t *)memalloc(size); + memcpy(packet.data, in_buffer, size); + packet.size = size; + packet.source = 1; + incoming_packets.push_back(packet); + pkts--; } } else if (peer->get_ready_state() == WebSocketPeer::STATE_CLOSED) { if (connection_status == CONNECTION_CONNECTED) { @@ -278,9 +320,14 @@ void WebSocketMultiplayerPeer::_poll_server() { // The user does not want new connections, dropping it. continue; } - peers_map[id] = peer.ws; - _send_ack(peer.ws, id); - emit_signal("peer_connected", id); + int32_t peer_id = id; + Error err = peer.ws->put_packet((const uint8_t *)&peer_id, sizeof(peer_id)); + if (err == OK) { + peers_map[id] = peer.ws; + emit_signal("peer_connected", id); + } else { + ERR_PRINT("Failed to send ID to newly connected peer."); + } continue; } else if (state == WebSocketPeer::STATE_CONNECTING) { continue; // Still connecting. @@ -338,8 +385,19 @@ void WebSocketMultiplayerPeer::_poll_server() { } // Fetch packets int pkts = ws->get_available_packet_count(); - while (pkts && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) { - _process_multiplayer(ws, id); + while (pkts > 0 && ws->get_ready_state() == WebSocketPeer::STATE_OPEN) { + const uint8_t *in_buffer; + int size = 0; + Error err = ws->get_packet(&in_buffer, size); + if (err != OK || size <= 0) { + break; + } + Packet packet; + packet.data = (uint8_t *)memalloc(size); + memcpy(packet.data, in_buffer, size); + packet.size = size; + packet.source = E.key; + incoming_packets.push_back(packet); pkts--; } } @@ -371,180 +429,6 @@ Ref<WebSocketPeer> WebSocketMultiplayerPeer::get_peer(int p_id) const { return peers_map[p_id]; } -void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) { - ERR_FAIL_COND(!p_peer.is_valid()); - ERR_FAIL_COND(p_peer->get_ready_state() != WebSocketPeer::STATE_OPEN); - - Vector<uint8_t> message = _make_pkt(p_type, 1, 0, (uint8_t *)&p_peer_id, 4); - p_peer->put_packet(&(message.ptr()[0]), message.size()); -} - -Vector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size) { - Vector<uint8_t> out; - out.resize(PROTO_SIZE + p_data_size); - - uint8_t *w = out.ptrw(); - memcpy(&w[0], &p_type, 1); - memcpy(&w[1], &p_from, 4); - memcpy(&w[5], &p_to, 4); - memcpy(&w[PROTO_SIZE], p_data, p_data_size); - - return out; -} - -void WebSocketMultiplayerPeer::_send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id) { - ERR_FAIL_COND(p_peer.is_null()); - // First of all, confirm the ID! - _send_sys(p_peer, SYS_ID, p_peer_id); - - // Then send the server peer (which will trigger connection_succeded in client) - _send_sys(p_peer, SYS_ADD, 1); - - for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) { - ERR_CONTINUE(E.value.is_null()); - - int32_t id = E.key; - if (p_peer_id == id) { - continue; // Skip the newly added peer (already confirmed) - } - - // Send new peer to others - _send_sys(E.value, SYS_ADD, p_peer_id); - // Send others to new peer - _send_sys(E.value, SYS_ADD, id); - } -} - -void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) { - for (const KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) { - int32_t id = E.key; - if (p_peer_id != id) { - _send_sys(E.value, SYS_DEL, p_peer_id); - } - } -} - -void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size) { - Packet packet; - packet.data = (uint8_t *)memalloc(p_data_size); - packet.size = p_data_size; - packet.source = p_source; - packet.destination = p_dest; - memcpy(packet.data, &p_data[PROTO_SIZE], p_data_size); - incoming_packets.push_back(packet); -} - -Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) { - if (p_to == 1) { - return OK; // Will not send to self - - } else if (p_to == 0) { - for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) { - if (E.key != p_from) { - E.value->put_packet(p_buffer, p_buffer_size); - } - } - return OK; // Sent to all but sender - - } else if (p_to < 0) { - for (KeyValue<int, Ref<WebSocketPeer>> &E : peers_map) { - if (E.key != p_from && E.key != -p_to) { - E.value->put_packet(p_buffer, p_buffer_size); - } - } - return OK; // Sent to all but sender and excluded - - } else { - ERR_FAIL_COND_V(p_to == p_from, FAILED); - - Ref<WebSocketPeer> peer_to = get_peer(p_to); - ERR_FAIL_COND_V(peer_to.is_null(), FAILED); - - return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer - } -} - -void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id) { - ERR_FAIL_COND(!p_peer.is_valid()); - - const uint8_t *in_buffer; - int size = 0; - int data_size = 0; - - Error err = p_peer->get_packet(&in_buffer, size); - - ERR_FAIL_COND(err != OK); - ERR_FAIL_COND(size < PROTO_SIZE); - - data_size = size - PROTO_SIZE; - - uint8_t type = 0; - uint32_t from = 0; - int32_t to = 0; - memcpy(&type, in_buffer, 1); - memcpy(&from, &in_buffer[1], 4); - memcpy(&to, &in_buffer[5], 4); - - if (is_server()) { // Server can resend - - ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages - ERR_FAIL_COND(from != p_peer_id); // Someone is cheating - - if (to == 1) { - // This is for the server - _store_pkt(from, to, in_buffer, data_size); - - } else if (to == 0) { - // Broadcast, for us too - _store_pkt(from, to, in_buffer, data_size); - - } else if (to < -1) { - // All but one, for us if not excluded - _store_pkt(from, to, in_buffer, data_size); - } - // Relay if needed (i.e. "to" includes a peer that is not the server) - _server_relay(from, to, in_buffer, size); - - } else { - if (type == SYS_NONE) { - // Payload message - _store_pkt(from, to, in_buffer, data_size); - return; - } - - // System message - ERR_FAIL_COND(data_size < 4); - int id = 0; - memcpy(&id, &in_buffer[PROTO_SIZE], 4); - - switch (type) { - case SYS_ADD: // Add peer - if (id != 1) { - peers_map[id] = Ref<WebSocketPeer>(); - } else { - pending_peers.clear(); - connection_status = CONNECTION_CONNECTED; - } - emit_signal(SNAME("peer_connected"), id); - if (id == 1) { // We just connected to the server - emit_signal(SNAME("connection_succeeded")); - } - break; - - case SYS_DEL: // Remove peer - emit_signal(SNAME("peer_disconnected"), id); - peers_map.erase(id); - break; - case SYS_ID: // Hello, server assigned ID - unique_id = id; - break; - default: - ERR_FAIL_MSG("Invalid multiplayer message."); - break; - } - } -} - void WebSocketMultiplayerPeer::set_supported_protocols(const Vector<String> &p_protocols) { peer_config->set_supported_protocols(p_protocols); } diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h index 8e7b118faa..ebe013a7bf 100644 --- a/modules/websocket/websocket_multiplayer_peer.h +++ b/modules/websocket/websocket_multiplayer_peer.h @@ -42,9 +42,6 @@ class WebSocketMultiplayerPeer : public MultiplayerPeer { GDCLASS(WebSocketMultiplayerPeer, MultiplayerPeer); private: - Vector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size); - void _store_pkt(int32_t p_source, int32_t p_dest, const uint8_t *p_data, uint32_t p_data_size); - Error _server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size); Ref<WebSocketPeer> _create_peer(); protected: @@ -59,7 +56,6 @@ protected: struct Packet { int source = 0; - int destination = 0; uint8_t *data = nullptr; uint32_t size = 0; }; @@ -90,20 +86,18 @@ protected: static void _bind_methods(); - void _send_ack(Ref<WebSocketPeer> p_peer, int32_t p_peer_id); - void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id); - void _send_del(int32_t p_peer_id); - void _process_multiplayer(Ref<WebSocketPeer> p_peer, uint32_t p_peer_id); - void _poll_client(); void _poll_server(); void _clear(); public: /* MultiplayerPeer */ - void set_target_peer(int p_target_peer) override; - int get_packet_peer() const override; - int get_unique_id() const override; + virtual void set_target_peer(int p_target_peer) override; + virtual int get_packet_peer() const override; + virtual int get_packet_channel() const override { return 0; } + virtual TransferMode get_packet_mode() const override { return TRANSFER_MODE_RELIABLE; } + virtual int get_unique_id() const override; + virtual bool is_server_relay_supported() const override { return true; } virtual int get_max_packet_size() const override; virtual bool is_server() const override; diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 4930b178ec..84e022182e 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -320,7 +320,9 @@ void WSLPeer::_do_client_handshake() { } tcp->poll(); - if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + if (tcp->get_status() == StreamPeerTCP::STATUS_CONNECTING) { + return; // Keep connecting. + } else if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { close(-1); // Failed to connect. return; } @@ -511,7 +513,7 @@ Error WSLPeer::connect_to_url(const String &p_url, bool p_verify_tls, Ref<X509Ce resolver.start(host, port); resolver.try_next_candidate(tcp); - if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTING && !resolver.has_more_candidates()) { + if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTING && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED && !resolver.has_more_candidates()) { _clear(); return FAILED; } diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h index 34d068be3e..d8d5bd99cc 100644 --- a/modules/webxr/godot_webxr.h +++ b/modules/webxr/godot_webxr.h @@ -65,7 +65,7 @@ extern int godot_webxr_get_view_count(); extern int *godot_webxr_get_render_target_size(); extern float *godot_webxr_get_transform_for_eye(int p_eye); extern float *godot_webxr_get_projection_for_eye(int p_eye); -extern void godot_webxr_commit_for_eye(int p_eye, unsigned int p_destination_fbo); +extern void godot_webxr_commit(unsigned int p_texture); extern void godot_webxr_sample_controller_data(); extern int godot_webxr_get_controller_count(); diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js index 9b75796ee5..c476a54c59 100644 --- a/modules/webxr/native/library_godot_webxr.js +++ b/modules/webxr/native/library_godot_webxr.js @@ -337,20 +337,18 @@ const GodotWebXR = { return buf; }, - godot_webxr_commit_for_eye__proxy: 'sync', - godot_webxr_commit_for_eye__sig: 'vii', - godot_webxr_commit_for_eye: function (p_eye, p_destination_fbo) { + godot_webxr_commit__proxy: 'sync', + godot_webxr_commit__sig: 'vi', + godot_webxr_commit: function (p_texture) { if (!GodotWebXR.session || !GodotWebXR.pose) { return; } - const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0; const glLayer = GodotWebXR.session.renderState.baseLayer; - const view = GodotWebXR.pose.views[view_index]; - const viewport = glLayer.getViewport(view); + const views = GodotWebXR.pose.views; const gl = GodotWebXR.gl; - const framebuffer = GL.framebuffers[p_destination_fbo]; + const texture = GL.textures[p_texture]; const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); const orig_read_framebuffer = gl.getParameter(gl.READ_FRAMEBUFFER_BINDING); @@ -359,14 +357,27 @@ const GodotWebXR = { // Copy from Godot render target into framebuffer from WebXR. gl.bindFramebuffer(gl.FRAMEBUFFER, null); - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, framebuffer); - gl.readBuffer(gl.COLOR_ATTACHMENT0); - gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer); - - // Flip Y upside down on destination. - gl.blitFramebuffer(0, 0, viewport.width, viewport.height, - viewport.x, viewport.height, viewport.width, viewport.y, - gl.COLOR_BUFFER_BIT, gl.NEAREST); + for (let i = 0; i < views.length; i++) { + const viewport = glLayer.getViewport(views[i]); + + const read_fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, read_fbo); + if (views.length > 1) { + gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, 0, i); + } else { + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + } + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, glLayer.framebuffer); + + // Flip Y upside down on destination. + gl.blitFramebuffer(0, 0, viewport.width, viewport.height, + viewport.x, viewport.y + viewport.height, viewport.x + viewport.width, viewport.y, + gl.COLOR_BUFFER_BIT, gl.NEAREST); + + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.deleteFramebuffer(read_fbo); + } // Restore state. gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer); diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 6b671c1660..f6ed9f027e 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -415,8 +415,7 @@ Vector<BlitToScreen> WebXRInterfaceJS::post_draw_viewport(RID p_render_target, c GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); - // @todo Support multiple eyes! - godot_webxr_commit_for_eye(1, rt->fbo); + godot_webxr_commit(rt->color); return blit_to_screen; }; diff --git a/modules/zip/SCsub b/modules/zip/SCsub new file mode 100644 index 0000000000..b7710123fd --- /dev/null +++ b/modules/zip/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_zip = env_modules.Clone() + +# Module files +env_zip.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/zip/config.py b/modules/zip/config.py new file mode 100644 index 0000000000..96cd2fc5bd --- /dev/null +++ b/modules/zip/config.py @@ -0,0 +1,17 @@ +def can_build(env, platform): + return env["minizip"] + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "ZIPReader", + "ZIPPacker", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/zip/doc_classes/ZIPPacker.xml b/modules/zip/doc_classes/ZIPPacker.xml new file mode 100644 index 0000000000..95d7ef50f9 --- /dev/null +++ b/modules/zip/doc_classes/ZIPPacker.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ZIPPacker" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Allows the creation of zip files. + </brief_description> + <description> + This class implements a writer that allows storing the multiple blobs in a zip archive. + [codeblock] + func write_zip_file(): + var writer := ZIPPacker.new() + var err := writer.open("user://archive.zip") + if err != OK: + return err + writer.start_file("hello.txt") + writer.write_file("Hello World".to_utf8_buffer()) + writer.close_file() + + writer.close() + return OK + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + <method name="close"> + <return type="int" enum="Error" /> + <description> + Closes the underlying resources used by this instance. + </description> + </method> + <method name="close_file"> + <return type="int" enum="Error" /> + <description> + Stops writing to a file within the archive. + It will fail if there is no open file. + </description> + </method> + <method name="open"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <param index="1" name="append" type="int" enum="ZIPPacker.ZipAppend" default="0" /> + <description> + Opens a zip file for writing at the given path using the specified write mode. + This must be called before everything else. + </description> + </method> + <method name="start_file"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <description> + Starts writing to a file within the archive. Only one file can be written at the same time. + Must be called after [method open]. + </description> + </method> + <method name="write_file"> + <return type="int" enum="Error" /> + <param index="0" name="data" type="PackedByteArray" /> + <description> + Write the given [param data] to the file. + Needs to be called after [method start_file]. + </description> + </method> + </methods> + <constants> + <constant name="APPEND_CREATE" value="0" enum="ZipAppend"> + </constant> + <constant name="APPEND_CREATEAFTER" value="1" enum="ZipAppend"> + </constant> + <constant name="APPEND_ADDINZIP" value="2" enum="ZipAppend"> + </constant> + </constants> +</class> diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml new file mode 100644 index 0000000000..717116a531 --- /dev/null +++ b/modules/zip/doc_classes/ZIPReader.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ZIPReader" inherits="RefCounted" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + Allows reading the content of a zip file. + </brief_description> + <description> + This class implements a reader that can extract the content of individual files inside a zip archive. + [codeblock] + func read_zip_file(): + var reader := ZIPReader.new() + var err := reader.open("user://archive.zip") + if err == OK: + return PackedByteArray() + var res := reader.read_file("hello.txt") + reader.close() + return res + [/codeblock] + </description> + <tutorials> + </tutorials> + <methods> + <method name="close"> + <return type="int" enum="Error" /> + <description> + Closes the underlying resources used by this instance. + </description> + </method> + <method name="get_files"> + <return type="PackedStringArray" /> + <description> + Returns the list of names of all files in the loaded archive. + Must be called after [method open]. + </description> + </method> + <method name="open"> + <return type="int" enum="Error" /> + <param index="0" name="path" type="String" /> + <description> + Opens the zip archive at the given [param path] and reads its file index. + </description> + </method> + <method name="read_file"> + <return type="PackedByteArray" /> + <param index="0" name="path" type="String" /> + <param index="1" name="case_sensitive" type="bool" default="true" /> + <description> + Loads the whole content of a file in the loaded zip archive into memory and returns it. + Must be called after [method open]. + </description> + </method> + </methods> +</class> diff --git a/modules/zip/register_types.cpp b/modules/zip/register_types.cpp new file mode 100644 index 0000000000..20fb484cfe --- /dev/null +++ b/modules/zip/register_types.cpp @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "register_types.h" + +#include "core/object/class_db.h" +#include "zip_packer.h" +#include "zip_reader.h" + +void initialize_zip_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + + GDREGISTER_CLASS(ZIPPacker); + GDREGISTER_CLASS(ZIPReader); +} + +void uninitialize_zip_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} diff --git a/modules/zip/register_types.h b/modules/zip/register_types.h new file mode 100644 index 0000000000..2640be12b8 --- /dev/null +++ b/modules/zip/register_types.h @@ -0,0 +1,39 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ZIP_REGISTER_TYPES_H +#define ZIP_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_zip_module(ModuleInitializationLevel p_level); +void uninitialize_zip_module(ModuleInitializationLevel p_level); + +#endif // ZIP_REGISTER_TYPES_H diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp new file mode 100644 index 0000000000..c37fc0945e --- /dev/null +++ b/modules/zip/zip_packer.cpp @@ -0,0 +1,108 @@ +/*************************************************************************/ +/* zip_packer.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "zip_packer.h" + +#include "core/io/zip_io.h" +#include "core/os/os.h" + +Error ZIPPacker::open(String p_path, ZipAppend p_append) { + if (fa.is_valid()) { + close(); + } + + zlib_filefunc_def io = zipio_create_io(&fa); + zf = zipOpen2(p_path.utf8().get_data(), p_append, NULL, &io); + return zf != NULL ? OK : FAILED; +} + +Error ZIPPacker::close() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker cannot be closed because it is not open."); + + return zipClose(zf, NULL) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::start_file(String p_path) { + ERR_FAIL_COND_V_MSG(zf != NULL, FAILED, "ZIPPacker is already in use."); + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + zip_fileinfo zipfi; + + OS::DateTime time = OS::get_singleton()->get_datetime(); + + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = time.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = time.month - 1; + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = time.year; + zipfi.dosDate = 0; + zipfi.external_fa = 0; + zipfi.internal_fa = 0; + + int ret = zipOpenNewFileInZip(zf, p_path.utf8().get_data(), &zipfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION); + return ret == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::write_file(Vector<uint8_t> p_data) { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + return zipWriteInFileInZip(zf, p_data.ptr(), p_data.size()) == ZIP_OK ? OK : FAILED; +} + +Error ZIPPacker::close_file() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPPacker must be opened before use."); + + Error err = zipCloseFileInZip(zf) == ZIP_OK ? OK : FAILED; + if (err == OK) { + zf = NULL; + } + return err; +} + +void ZIPPacker::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path", "append"), &ZIPPacker::open, DEFVAL(Variant(APPEND_CREATE))); + ClassDB::bind_method(D_METHOD("start_file", "path"), &ZIPPacker::start_file); + ClassDB::bind_method(D_METHOD("write_file", "data"), &ZIPPacker::write_file); + ClassDB::bind_method(D_METHOD("close_file"), &ZIPPacker::close_file); + ClassDB::bind_method(D_METHOD("close"), &ZIPPacker::close); + + BIND_ENUM_CONSTANT(APPEND_CREATE); + BIND_ENUM_CONSTANT(APPEND_CREATEAFTER); + BIND_ENUM_CONSTANT(APPEND_ADDINZIP); +} + +ZIPPacker::ZIPPacker() {} + +ZIPPacker::~ZIPPacker() { + if (fa.is_valid()) { + close(); + } +} diff --git a/modules/zip/zip_packer.h b/modules/zip/zip_packer.h new file mode 100644 index 0000000000..23e96b5ad2 --- /dev/null +++ b/modules/zip/zip_packer.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* zip_packer.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ZIP_PACKER_H +#define ZIP_PACKER_H + +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" + +#include "thirdparty/minizip/zip.h" + +class ZIPPacker : public RefCounted { + GDCLASS(ZIPPacker, RefCounted); + + Ref<FileAccess> fa; + zipFile zf; + +protected: + static void _bind_methods(); + +public: + enum ZipAppend { + APPEND_CREATE = 0, + APPEND_CREATEAFTER = 1, + APPEND_ADDINZIP = 2, + }; + + Error open(String p_path, ZipAppend p_append); + Error close(); + + Error start_file(String p_path); + Error write_file(Vector<uint8_t> p_data); + Error close_file(); + + ZIPPacker(); + ~ZIPPacker(); +}; + +VARIANT_ENUM_CAST(ZIPPacker::ZipAppend) + +#endif // ZIP_PACKER_H diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp new file mode 100644 index 0000000000..f35b947cef --- /dev/null +++ b/modules/zip/zip_reader.cpp @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* zip_reader.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "zip_reader.h" + +#include "core/error/error_macros.h" +#include "core/io/zip_io.h" + +Error ZIPReader::open(String p_path) { + if (fa.is_valid()) { + close(); + } + + zlib_filefunc_def io = zipio_create_io(&fa); + uzf = unzOpen2(p_path.utf8().get_data(), &io); + return uzf != NULL ? OK : FAILED; +} + +Error ZIPReader::close() { + ERR_FAIL_COND_V_MSG(fa.is_null(), FAILED, "ZIPReader cannot be closed because it is not open."); + + return unzClose(uzf) == UNZ_OK ? OK : FAILED; +} + +PackedStringArray ZIPReader::get_files() { + ERR_FAIL_COND_V_MSG(fa.is_null(), PackedStringArray(), "ZIPReader must be opened before use."); + + List<String> s; + + if (unzGoToFirstFile(uzf) != UNZ_OK) { + return PackedStringArray(); + } + + do { + unz_file_info64 file_info; + char filename[256]; // Note filename is a path ! + int err = unzGetCurrentFileInfo64(uzf, &file_info, filename, sizeof(filename), NULL, 0, NULL, 0); + if (err == UNZ_OK) { + s.push_back(filename); + } else { + // Assume filename buffer was too small + char *long_filename_buff = (char *)memalloc(file_info.size_filename); + int err2 = unzGetCurrentFileInfo64(uzf, NULL, long_filename_buff, sizeof(long_filename_buff), NULL, 0, NULL, 0); + if (err2 == UNZ_OK) { + s.push_back(long_filename_buff); + memfree(long_filename_buff); + } + } + } while (unzGoToNextFile(uzf) == UNZ_OK); + + PackedStringArray arr; + arr.resize(s.size()); + int idx = 0; + for (const List<String>::Element *E = s.front(); E; E = E->next()) { + arr.set(idx++, E->get()); + } + return arr; +} + +PackedByteArray ZIPReader::read_file(String p_path, bool p_case_sensitive) { + ERR_FAIL_COND_V_MSG(fa.is_null(), PackedByteArray(), "ZIPReader must be opened before use."); + + int cs = p_case_sensitive ? 1 : 2; + if (unzLocateFile(uzf, p_path.utf8().get_data(), cs) != UNZ_OK) { + ERR_FAIL_V_MSG(PackedByteArray(), "File does not exist in zip archive: " + p_path); + } + if (unzOpenCurrentFile(uzf) != UNZ_OK) { + ERR_FAIL_V_MSG(PackedByteArray(), "Could not open file within zip archive."); + } + + unz_file_info info; + unzGetCurrentFileInfo(uzf, &info, NULL, 0, NULL, 0, NULL, 0); + PackedByteArray data; + data.resize(info.uncompressed_size); + + uint8_t *w = data.ptrw(); + unzReadCurrentFile(uzf, &w[0], info.uncompressed_size); + + unzCloseCurrentFile(uzf); + return data; +} + +ZIPReader::ZIPReader() {} + +ZIPReader::~ZIPReader() { + if (fa.is_valid()) { + close(); + } +} + +void ZIPReader::_bind_methods() { + ClassDB::bind_method(D_METHOD("open", "path"), &ZIPReader::open); + ClassDB::bind_method(D_METHOD("close"), &ZIPReader::close); + ClassDB::bind_method(D_METHOD("get_files"), &ZIPReader::get_files); + ClassDB::bind_method(D_METHOD("read_file", "path", "case_sensitive"), &ZIPReader::read_file, DEFVAL(Variant(true))); +} diff --git a/modules/zip/zip_reader.h b/modules/zip/zip_reader.h new file mode 100644 index 0000000000..fbc2fc0409 --- /dev/null +++ b/modules/zip/zip_reader.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* zip_reader.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ZIP_READER_H +#define ZIP_READER_H + +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" + +#include "thirdparty/minizip/unzip.h" + +class ZIPReader : public RefCounted { + GDCLASS(ZIPReader, RefCounted) + + Ref<FileAccess> fa; + unzFile uzf; + +protected: + static void _bind_methods(); + +public: + Error open(String p_path); + Error close(); + + PackedStringArray get_files(); + PackedByteArray read_file(String p_path, bool p_case_sensitive); + + ZIPReader(); + ~ZIPReader(); +}; + +#endif // ZIP_READER_H diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp index 454bcd2eda..c0b098cd7f 100644 --- a/platform/android/android_input_handler.cpp +++ b/platform/android/android_input_handler.cpp @@ -118,7 +118,7 @@ void AndroidInputHandler::process_key_event(int p_keycode, int p_physical_keycod Input::get_singleton()->parse_input_event(ev); } -void AndroidInputHandler::_parse_all_touch(bool p_pressed) { +void AndroidInputHandler::_parse_all_touch(bool p_pressed, bool p_double_tap) { if (touch.size()) { //end all if exist for (int i = 0; i < touch.size(); i++) { @@ -127,17 +127,18 @@ void AndroidInputHandler::_parse_all_touch(bool p_pressed) { ev->set_index(touch[i].id); ev->set_pressed(p_pressed); ev->set_position(touch[i].pos); + ev->set_double_tap(p_double_tap); Input::get_singleton()->parse_input_event(ev); } } } void AndroidInputHandler::_release_all_touch() { - _parse_all_touch(false); + _parse_all_touch(false, false); touch.clear(); } -void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points) { +void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap) { switch (p_event) { case AMOTION_EVENT_ACTION_DOWN: { //gesture begin // Release any remaining touches or mouse event @@ -151,7 +152,7 @@ void AndroidInputHandler::process_touch_event(int p_event, int p_pointer, const } //send touch - _parse_all_touch(true); + _parse_all_touch(true, p_double_tap); } break; case AMOTION_EVENT_ACTION_MOVE: { //motion diff --git a/platform/android/android_input_handler.h b/platform/android/android_input_handler.h index 88490f0407..4da8a910c0 100644 --- a/platform/android/android_input_handler.h +++ b/platform/android/android_input_handler.h @@ -87,13 +87,13 @@ private: void _release_mouse_event_info(bool p_source_mouse_relative = false); - void _parse_all_touch(bool p_pressed); + void _parse_all_touch(bool p_pressed, bool p_double_tap); void _release_all_touch(); public: void process_mouse_event(int p_event_action, int p_event_android_buttons_mask, Point2 p_event_pos, Vector2 p_delta, bool p_double_click, bool p_source_mouse_relative); - void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points); + void process_touch_event(int p_event, int p_pointer, const Vector<TouchPos> &p_points, bool p_double_tap); void process_magnify(Point2 p_pos, float p_factor); void process_pan(Point2 p_pos, Vector2 p_delta); void process_joy_event(JoypadEvent p_event); diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 6f8efbf069..08369e735d 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -440,8 +440,8 @@ Vector<String> DisplayServerAndroid::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerAndroid::create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerAndroid(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan versions.", "Unable to initialize Video driver"); } @@ -485,7 +485,7 @@ void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) { rect_changed_callback.callp(reinterpret_cast<const Variant **>(&sizep), 1, ret, ce); } -DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { rendering_driver = p_rendering_driver; // TODO: rendering_driver is broken, change when different drivers are supported again diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 6e14ba3e23..a6bc88e048 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -194,7 +194,7 @@ public: virtual void mouse_set_mode(MouseMode p_mode) override; virtual MouseMode mouse_get_mode() const override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_android_driver(); @@ -204,7 +204,7 @@ public: virtual Point2i mouse_get_position() const override; virtual MouseButton mouse_get_button_state() const override; - DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerAndroid(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerAndroid(); }; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index ef3b79b630..6426f95b42 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1551,7 +1551,7 @@ String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, R print_verbose("Creating splash background color image."); splash_bg_color_image.instantiate(); - splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->initialize_data(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); splash_bg_color_image->fill(bg_color); String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); @@ -2901,20 +2901,22 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP _load_image_data(splash_bg_color_image, data); } - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - if (file == launcher_icons[i].export_path) { - _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + if (file.ends_with(".png") && file.contains("mipmap")) { + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } } - } - if (foreground.is_valid() && !foreground->is_empty()) { - if (file == launcher_adaptive_icon_foregrounds[i].export_path) { - _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + if (foreground.is_valid() && !foreground->is_empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } } - } - if (background.is_valid() && !background->is_empty()) { - if (file == launcher_adaptive_icon_backgrounds[i].export_path) { - _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + if (background.is_valid() && !background->is_empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } } } } diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index 05608883d7..5a91e5ce32 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -276,6 +276,11 @@ task generateDevTemplate { finalizedBy 'zipCustomBuild' } +task clean(type: Delete) { + dependsOn 'cleanGodotEditor' + dependsOn 'cleanGodotTemplates' +} + /** * Clean the generated editor artifacts. */ @@ -292,8 +297,6 @@ task cleanGodotEditor(type: Delete) { // Delete the Godot editor apks in the Godot bin directory delete("$binDir/android_editor.apk") delete("$binDir/android_editor_dev.apk") - - finalizedBy getTasksByName("clean", true) } /** @@ -321,5 +324,8 @@ task cleanGodotTemplates(type: Delete) { delete("$binDir/godot-lib.template_debug.dev.aar") delete("$binDir/godot-lib.template_release.aar") - finalizedBy getTasksByName("clean", true) + // Cover deletion for the libs using the previous naming scheme + delete("$binDir/godot-lib.debug.aar") + delete("$binDir/godot-lib.dev.aar") + delete("$binDir/godot-lib.release.aar") } 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 26aad867b1..33896ecb95 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -110,7 +110,7 @@ public class GodotLib { /** * Forward touch events. */ - public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions); + public static native void dispatchTouchEvent(int event, int pointer, int pointerCount, float[] positions, boolean doubleTap); /** * Dispatch mouse events diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt index 9715c31fc1..a7a57621de 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotGestureHandler.kt @@ -55,18 +55,15 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi */ var panningAndScalingEnabled = false - private var doubleTapInProgress = false + private var nextDownIsDoubleTap = false private var dragInProgress = false private var scaleInProgress = false private var contextClickInProgress = false private var pointerCaptureInProgress = false override fun onDown(event: MotionEvent): Boolean { - // Don't send / register a down event while we're in the middle of a double-tap - if (!doubleTapInProgress) { - // Send the down event - GodotInputHandler.handleMotionEvent(event) - } + GodotInputHandler.handleMotionEvent(event.source, MotionEvent.ACTION_DOWN, event.buttonState, event.x, event.y, nextDownIsDoubleTap) + nextDownIsDoubleTap = false return true } @@ -209,24 +206,14 @@ internal class GodotGestureHandler : SimpleOnGestureListener(), OnScaleGestureLi override fun onDoubleTapEvent(event: MotionEvent): Boolean { if (event.actionMasked == MotionEvent.ACTION_UP) { - doubleTapInProgress = false + nextDownIsDoubleTap = false + GodotInputHandler.handleMotionEvent(event) } return true } override fun onDoubleTap(event: MotionEvent): Boolean { - doubleTapInProgress = true - val x = event.x - val y = event.y - val buttonMask = - if (GodotInputHandler.isMouseEvent(event)) { - event.buttonState - } else { - MotionEvent.BUTTON_PRIMARY - } - GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_DOWN, buttonMask, x, y, true) - GodotInputHandler.handleMouseEvent(MotionEvent.ACTION_UP, 0, x, y, false) - + nextDownIsDoubleTap = true return true } 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 03cb8034fa..d2f3c5aed2 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 @@ -438,15 +438,19 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { } static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y) { - return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0); + return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, false); } - static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY) { + static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, boolean doubleTap) { + return handleMotionEvent(eventSource, eventAction, buttonsMask, x, y, 0, 0, doubleTap); + } + + static boolean handleMotionEvent(int eventSource, int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleTap) { if (isMouseEvent(eventSource)) { - return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, false, false); + return handleMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleTap, false); } - return handleTouchEvent(eventAction, x, y); + return handleTouchEvent(eventAction, x, y, doubleTap); } static boolean handleMouseEvent(final MotionEvent event) { @@ -468,10 +472,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, false, false); } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, boolean doubleClick) { - return handleMouseEvent(eventAction, buttonsMask, x, y, 0, 0, doubleClick, false); - } - static boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative) { switch (eventAction) { case MotionEvent.ACTION_CANCEL: @@ -508,14 +508,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { final int action = event.getActionMasked(); final int actionPointerId = event.getPointerId(event.getActionIndex()); - return handleTouchEvent(action, actionPointerId, pointerCount, positions); + return handleTouchEvent(action, actionPointerId, pointerCount, positions, false); } - static boolean handleTouchEvent(int eventAction, float x, float y) { - return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }); + static boolean handleTouchEvent(int eventAction, float x, float y, boolean doubleTap) { + return handleTouchEvent(eventAction, 0, 1, new float[] { 0, x, y }, doubleTap); } - static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions) { + static boolean handleTouchEvent(int eventAction, int actionPointerId, int pointerCount, float[] positions, boolean doubleTap) { switch (eventAction) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_CANCEL: @@ -523,7 +523,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener { case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_DOWN: { - GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions); + GodotLib.dispatchTouchEvent(eventAction, actionPointerId, pointerCount, positions, doubleTap); return true; } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 04b69d5b86..5bf550a2b8 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -265,7 +265,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JN } // Called on the UI thread -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position) { +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray position, jboolean p_double_tap) { if (step.get() <= 0) { return; } @@ -280,7 +280,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JN points.push_back(tp); } - input_handler->process_touch_event(ev, pointer, points); + input_handler->process_touch_event(ev, pointer, points, p_double_tap); } // Called on the UI thread diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 09fed15690..f3f2646bfb 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -46,7 +46,7 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ttsCallback(JNIEnv *env, jclass clazz, jint event, jint id, jint pos); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchMouseEvent(JNIEnv *env, jclass clazz, jint p_event_type, jint p_button_mask, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y, jboolean p_double_click, jboolean p_source_mouse_relative); -JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_dispatchTouchEvent(JNIEnv *env, jclass clazz, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jboolean p_double_tap); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_magnify(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_factor); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_pan(JNIEnv *env, jclass clazz, jfloat p_x, jfloat p_y, jfloat p_delta_x, jfloat p_delta_y); 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); diff --git a/platform/ios/display_server_ios.h b/platform/ios/display_server_ios.h index f3624f24ab..da06ff7431 100644 --- a/platform/ios/display_server_ios.h +++ b/platform/ios/display_server_ios.h @@ -73,7 +73,7 @@ class DisplayServerIOS : public DisplayServer { void perform_event(const Ref<InputEvent> &p_event); - DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerIOS(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerIOS(); public: @@ -82,7 +82,7 @@ public: static DisplayServerIOS *get_singleton(); static void register_ios_driver(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); // MARK: - Events diff --git a/platform/ios/display_server_ios.mm b/platform/ios/display_server_ios.mm index d3a0f38463..b13561c511 100644 --- a/platform/ios/display_server_ios.mm +++ b/platform/ios/display_server_ios.mm @@ -50,7 +50,7 @@ DisplayServerIOS *DisplayServerIOS::get_singleton() { return (DisplayServerIOS *)DisplayServer::get_singleton(); } -DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerIOS::DisplayServerIOS(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { rendering_driver = p_rendering_driver; // Init TTS @@ -152,8 +152,8 @@ DisplayServerIOS::~DisplayServerIOS() { #endif } -DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerIOS::create_func(const String &p_rendering_driver, WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + return memnew(DisplayServerIOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); } Vector<String> DisplayServerIOS::get_rendering_drivers_func() { @@ -235,6 +235,7 @@ void DisplayServerIOS::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, ev->set_index(p_idx); ev->set_pressed(p_pressed); ev->set_position(Vector2(p_x, p_y)); + ev->set_double_tap(p_double_click); perform_event(ev); } } diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 74a57dc614..83b2012d3e 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -740,8 +740,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp img->resize(info.width, info.width / aspect_ratio); } } - Ref<Image> new_img = memnew(Image); - new_img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> new_img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); new_img->fill(boot_bg_color); _blend_and_rotate(new_img, img, false); err = new_img->save_png(p_dest_dir + info.export_name); @@ -755,8 +754,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExp } } else { // Generate loading screen from the splash screen - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(info.width, info.height, false, Image::FORMAT_RGBA8); img->fill(boot_bg_color); Ref<Image> img_bs; diff --git a/platform/ios/view_controller.mm b/platform/ios/view_controller.mm index 43669d3f94..53c5d3d0b4 100644 --- a/platform/ios/view_controller.mm +++ b/platform/ios/view_controller.mm @@ -164,7 +164,11 @@ // MARK: Orientation - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { - return UIRectEdgeAll; + if (GLOBAL_GET("display/window/ios/suppress_ui_gesture")) { + return UIRectEdgeAll; + } else { + return UIRectEdgeNone; + } } - (BOOL)shouldAutorotate { @@ -206,7 +210,11 @@ } - (BOOL)prefersStatusBarHidden { - return YES; + if (GLOBAL_GET("display/window/ios/hide_status_bar")) { + return YES; + } else { + return NO; + } } - (BOOL)prefersHomeIndicatorAutoHidden { diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index e78467beff..88c6500e10 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -126,7 +126,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const { case FEATURE_WINDOW_TRANSPARENCY: //case FEATURE_HIDPI: case FEATURE_ICON: - case FEATURE_NATIVE_ICON: + //case FEATURE_NATIVE_ICON: case FEATURE_SWAP_BUFFERS: #ifdef DBUS_ENABLED case FEATURE_KEEP_SCREEN_ON: @@ -4432,13 +4432,24 @@ Vector<String> DisplayServerX11::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerX11(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU, upgrade it.\n" - "If you have updated your graphics drivers recently, try rebooting.", - "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_name = OS::get_singleton()->get_executable_path().get_file(); + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n'./" + + executable_name + " --rendering-driver opengl3'.\n " + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } @@ -4682,7 +4693,7 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, V return id; } -DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); r_error = OK; @@ -4909,6 +4920,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode Point2i window_position( (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, p_flags, Rect2i(window_position, p_resolution)); if (main_window == INVALID_WINDOW_ID) { r_error = ERR_CANT_CREATE; diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 5723abc751..9ef8f71c05 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -444,12 +444,12 @@ public: virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_x11_driver(); - DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerX11(); }; diff --git a/platform/linuxbsd/godot_linuxbsd.cpp b/platform/linuxbsd/godot_linuxbsd.cpp index 91a260182e..fa5e20891c 100644 --- a/platform/linuxbsd/godot_linuxbsd.cpp +++ b/platform/linuxbsd/godot_linuxbsd.cpp @@ -69,6 +69,7 @@ int main(int argc, char *argv[]) { } if (Main::start()) { + os.set_exit_code(EXIT_SUCCESS); os.run(); // it is actually the OS that decides how to run } Main::cleanup(); diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp index 995a904398..11b667fcef 100644 --- a/platform/linuxbsd/os_linuxbsd.cpp +++ b/platform/linuxbsd/os_linuxbsd.cpp @@ -246,6 +246,10 @@ String OS_LinuxBSD::get_version() const { } Vector<String> OS_LinuxBSD::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + const String rendering_device_name = RenderingServer::get_singleton()->get_rendering_device()->get_device_name(); // e.g. `NVIDIA GeForce GTX 970` const String rendering_device_vendor = RenderingServer::get_singleton()->get_rendering_device()->get_device_vendor_name(); // e.g. `NVIDIA` const String card_name = rendering_device_name.trim_prefix(rendering_device_vendor).strip_edges(); // -> `GeForce GTX 970` diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index 484b8ffebc..618da6b388 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -430,12 +430,12 @@ public: virtual void set_native_icon(const String &p_filename) override; virtual void set_icon(const Ref<Image> &p_icon) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_macos_driver(); - DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerMacOS(); }; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index f4692abc92..49633e6efa 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -3405,10 +3405,29 @@ void DisplayServerMacOS::set_icon(const Ref<Image> &p_icon) { [NSApp setApplicationIconImage:nsimg]; } -DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerMacOS::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerMacOS(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.", "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_command; + if (OS::get_singleton()->get_bundle_resource_dir() == OS::get_singleton()->get_executable_path().get_base_dir()) { + executable_command = vformat("%s --rendering-driver opengl3", OS::get_singleton()->get_executable_path()); + } else { + executable_command = vformat("open %s --args --rendering-driver opengl3", OS::get_singleton()->get_bundle_resource_dir().path_join("../..").simplify_path()); + } + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command: '" + + executable_command + "'.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } @@ -3556,7 +3575,7 @@ bool DisplayServerMacOS::mouse_process_popups(bool p_close) { return closed; } -DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); r_error = OK; @@ -3665,6 +3684,11 @@ DisplayServerMacOS::DisplayServerMacOS(const String &p_rendering_driver, WindowM Point2i window_position( screen_get_position(0).x + (screen_get_size(0).width - p_resolution.width) / 2, screen_get_position(0).y + (screen_get_size(0).height - p_resolution.height) / 2); + + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, Rect2i(window_position, p_resolution)); ERR_FAIL_COND(main_window == INVALID_WINDOW_ID); for (int i = 0; i < WINDOW_FLAG_MAX; i++) { diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm index ae8534f6ab..8ffb0abfdb 100644 --- a/platform/macos/os_macos.mm +++ b/platform/macos/os_macos.mm @@ -148,9 +148,11 @@ void OS_MacOS::alert(const String &p_alert, const String &p_title) { NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()]; NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()]; + NSTextField *text_field = [NSTextField labelWithString:ns_alert]; + [text_field setAlignment:NSTextAlignmentCenter]; [window addButtonWithTitle:@"OK"]; [window setMessageText:ns_title]; - [window setInformativeText:ns_alert]; + [window setAccessoryView:text_field]; [window setAlertStyle:NSAlertStyleWarning]; id key_window = [[NSApplication sharedApplication] keyWindow]; diff --git a/platform/web/SCsub b/platform/web/SCsub index cb00fa9f5b..077024507a 100644 --- a/platform/web/SCsub +++ b/platform/web/SCsub @@ -35,6 +35,7 @@ sys_env.AddJSLibraries( "js/libs/library_godot_os.js", "js/libs/library_godot_runtime.js", "js/libs/library_godot_input.js", + "js/libs/library_godot_webgl2.js", ] ) diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp index f6a61b18e4..10ed8b8343 100644 --- a/platform/web/display_server_web.cpp +++ b/platform/web/display_server_web.cpp @@ -738,11 +738,11 @@ void DisplayServerWeb::_dispatch_input_event(const Ref<InputEvent> &p_event) { } } -DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) { - return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWeb::create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) { + return memnew(DisplayServerWeb(p_rendering_driver, p_window_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); } -DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error) { +DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error) { r_error = OK; // Always succeeds for now. // Ensure the canvas ID. @@ -773,6 +773,9 @@ DisplayServerWeb::DisplayServerWeb(const String &p_rendering_driver, WindowMode if (emscripten_webgl_make_context_current(webgl_ctx) != EMSCRIPTEN_RESULT_SUCCESS) { webgl2_init_failed = true; } else { + if (!emscripten_webgl_enable_extension(webgl_ctx, "OVR_multiview2")) { + // @todo Should we log this? + } RasterizerGLES3::make_current(); } } diff --git a/platform/web/display_server_web.h b/platform/web/display_server_web.h index 85076b906f..3222e2483e 100644 --- a/platform/web/display_server_web.h +++ b/platform/web/display_server_web.h @@ -96,7 +96,7 @@ private: static void _js_utterance_callback(int p_event, int p_id, int p_pos); static Vector<String> get_rendering_drivers_func(); - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static void _dispatch_input_event(const Ref<InputEvent> &p_event); @@ -221,7 +221,7 @@ public: virtual void swap_buffers() override; static void register_web_driver(); - DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Size2i &p_resolution, Error &r_error); + DisplayServerWeb(const String &p_rendering_driver, WindowMode p_window_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Point2i *p_position, const Size2i &p_resolution, Error &r_error); ~DisplayServerWeb(); }; diff --git a/platform/web/godot_webgl2.h b/platform/web/godot_webgl2.h index 968b70f84b..ae6b23ae18 100644 --- a/platform/web/godot_webgl2.h +++ b/platform/web/godot_webgl2.h @@ -34,4 +34,21 @@ #include "GLES3/gl3.h" #include "webgl/webgl2.h" +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 + +#ifdef __cplusplus +extern "C" { +#endif + +void godot_webgl2_glFramebufferTextureMultiviewOVR(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); + +#define glFramebufferTextureMultiviewOVR godot_webgl2_glFramebufferTextureMultiviewOVR + +#ifdef __cplusplus +} +#endif + #endif // GODOT_WEBGL2_H diff --git a/platform/web/js/libs/library_godot_webgl2.js b/platform/web/js/libs/library_godot_webgl2.js new file mode 100644 index 0000000000..365f712be7 --- /dev/null +++ b/platform/web/js/libs/library_godot_webgl2.js @@ -0,0 +1,52 @@ +/*************************************************************************/ +/* library_godot_webgl2.js */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +const GodotWebGL2 = { + $GodotWebGL2__deps: ['$GL', '$GodotRuntime'], + $GodotWebGL2: {}, + + godot_webgl2_glFramebufferTextureMultiviewOVR__deps: ['emscripten_webgl_get_current_context'], + godot_webgl2_glFramebufferTextureMultiviewOVR__proxy: 'sync', + godot_webgl2_glFramebufferTextureMultiviewOVR__sig: 'viiiiii', + godot_webgl2_glFramebufferTextureMultiviewOVR: function (target, attachment, texture, level, base_view_index, num_views) { + const context = GL.currentContext; + if (typeof context.multiviewExt === 'undefined') { + const ext = context.GLctx.getExtension('OVR_multiview2'); + if (!ext) { + console.error('Trying to call glFramebufferTextureMultiviewOVR() without the OVR_multiview2 extension'); + return; + } + context.multiviewExt = ext; + } + context.multiviewExt.framebufferTextureMultiviewOVR(target, attachment, GL.textures[texture], level, base_view_index, num_views); + }, +}; + +autoAddDeps(GodotWebGL2, '$GodotWebGL2'); +mergeInto(LibraryManager.library, GodotWebGL2); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 3b773685c3..d99670243e 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2444,10 +2444,12 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA return 0; // Jump back. } case WM_MOUSELEAVE: { - old_invalid = true; - windows[window_id].mouse_outside = true; + if (window_mouseover_id == window_id) { + old_invalid = true; + window_mouseover_id = INVALID_WINDOW_ID; - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT); + } } break; case WM_INPUT: { @@ -2678,17 +2680,21 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + if (window_mouseover_id != window_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = window_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2779,17 +2785,29 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA } } - if (windows[window_id].mouse_outside) { + DisplayServer::WindowID over_id = get_window_at_screen_position(mouse_get_position()); + if (!Rect2(window_get_position(over_id), Point2(windows[over_id].width, windows[over_id].height)).has_point(mouse_get_position())) { + // Don't consider the windowborder as part of the window. + over_id = INVALID_WINDOW_ID; + } + if (window_mouseover_id != over_id) { // Mouse enter. if (mouse_mode != MOUSE_MODE_CAPTURED) { - _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER); + if (window_mouseover_id != INVALID_WINDOW_ID) { + // Leave previous window. + _send_window_event(windows[window_mouseover_id], WINDOW_EVENT_MOUSE_EXIT); + } + + if (over_id != INVALID_WINDOW_ID) { + _send_window_event(windows[over_id], WINDOW_EVENT_MOUSE_ENTER); + } } CursorShape c = cursor_shape; cursor_shape = CURSOR_MAX; cursor_set_shape(c); - windows[window_id].mouse_outside = false; + window_mouseover_id = over_id; // Once-off notification, must call again. track_mouse_leave_event(hWnd); @@ -2800,9 +2818,13 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA break; } + DisplayServer::WindowID receiving_window_id = _get_focused_window_or_popup(); + if (receiving_window_id == INVALID_WINDOW_ID) { + receiving_window_id = window_id; + } Ref<InputEventMouseMotion> mm; mm.instantiate(); - mm->set_window_id(window_id); + mm->set_window_id(receiving_window_id); mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0); mm->set_shift_pressed((wParam & MK_SHIFT) != 0); mm->set_alt_pressed(alt_mem); @@ -2859,9 +2881,8 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y))); old_x = mm->get_position().x; old_y = mm->get_position().y; - if (windows[window_id].window_has_focus || window_get_active_popup() == window_id) { - Input::get_singleton()->parse_input_event(mm); - } + + Input::get_singleton()->parse_input_event(mm); } break; case WM_LBUTTONDOWN: @@ -3087,7 +3108,7 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA rect_changed = true; } #if defined(VULKAN_ENABLED) - if (context_vulkan && window_created) { + if (context_vulkan && window.context_created) { // Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed. context_vulkan->window_resize(window_id, window.width, window.height); } @@ -3547,6 +3568,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, windows.erase(id); ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window."); } + wd.context_created = true; } #endif @@ -3703,7 +3725,7 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { } } -DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { drop_events = false; key_event_pos = 0; @@ -3855,6 +3877,10 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); + if (p_position != nullptr) { + window_position = *p_position; + } + WindowID main_window = _create_window(p_mode, p_vsync_mode, 0, Rect2i(window_position, p_resolution)); ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); @@ -3918,12 +3944,24 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() { return drivers; } -DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { - DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error)); +DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error)); if (r_error != OK) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported Vulkan or OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", - "Unable to initialize Video driver"); + if (p_rendering_driver == "vulkan") { + String executable_name = OS::get_singleton()->get_executable_path().get_file(); + OS::get_singleton()->alert("Your video card driver does not support the selected Vulkan version.\n" + "Please try updating your GPU driver or try using the OpenGL 3 driver.\n" + "You can enable the OpenGL 3 driver by starting the engine from the\n" + "command line with the command:\n'./" + + executable_name + " --rendering-driver opengl3'.\n " + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } else { + OS::get_singleton()->alert("Your video card driver does not support the selected OpenGL version.\n" + "Please try updating your GPU driver.\n" + "If you have updated your graphics drivers recently, try rebooting.", + "Unable to initialize Video driver"); + } } return ds; } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 6403b57d8d..8ac0086d69 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -323,6 +323,8 @@ class DisplayServerWindows : public DisplayServer { LPARAM lParam; }; + WindowID window_mouseover_id = INVALID_WINDOW_ID; + KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE]; int key_event_pos; @@ -369,6 +371,7 @@ class DisplayServerWindows : public DisplayServer { bool no_focus = false; bool window_has_focus = false; bool exclusive = false; + bool context_created = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -397,7 +400,6 @@ class DisplayServerWindows : public DisplayServer { Size2 window_rect; Point2 last_pos; - bool mouse_outside = true; ObjectID instance_id; @@ -622,11 +624,11 @@ public: virtual void set_context(Context p_context) override; - static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); static Vector<String> get_rendering_drivers_func(); static void register_windows_driver(); - DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); ~DisplayServerWindows(); }; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 5ca064e523..a8911788fe 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -311,6 +311,10 @@ String OS_Windows::get_version() const { } Vector<String> OS_Windows::get_video_adapter_driver_info() const { + if (RenderingServer::get_singleton()->get_rendering_device() == nullptr) { + return Vector<String>(); + } + REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID REFIID uuid = IID_IWbemLocator; // Interface UUID IWbemLocator *wbemLocator = NULL; // to get the services diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 80169bc80c..78b5199358 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -172,6 +172,7 @@ void Light2D::set_shadow_filter(ShadowFilter p_filter) { ERR_FAIL_INDEX(p_filter, SHADOW_FILTER_MAX); shadow_filter = p_filter; RS::get_singleton()->canvas_light_set_shadow_filter(canvas_light, RS::CanvasLightShadowFilter(p_filter)); + notify_property_list_changed(); } Light2D::ShadowFilter Light2D::get_shadow_filter() const { @@ -231,6 +232,10 @@ void Light2D::_validate_property(PropertyInfo &p_property) const { if (!shadow && (p_property.name == "shadow_color" || p_property.name == "shadow_filter" || p_property.name == "shadow_filter_smooth" || p_property.name == "shadow_item_cull_mask")) { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } + + if (shadow && p_property.name == "shadow_filter_smooth" && shadow_filter == SHADOW_FILTER_NONE) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } void Light2D::_bind_methods() { diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index f077f7f5e6..748fbbe2d1 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -104,7 +104,7 @@ void NavigationAgent2D::_bind_methods() { ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); ADD_SIGNAL(MethodInfo("navigation_finished")); - ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR3, "safe_velocity"))); + ADD_SIGNAL(MethodInfo("velocity_computed", PropertyInfo(Variant::VECTOR2, "safe_velocity"))); } void NavigationAgent2D::_notification(int p_what) { @@ -478,8 +478,8 @@ void NavigationAgent2D::_request_repath() { void NavigationAgent2D::_check_distance_to_target() { if (!target_reached) { if (distance_to_target() < target_desired_distance) { - emit_signal(SNAME("target_reached")); target_reached = true; + emit_signal(SNAME("target_reached")); } } } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 430af160e8..58172b6153 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1097,8 +1097,8 @@ void TileMap::_rendering_update_layer(int p_layer) { rs->canvas_item_set_sort_children_by_y(ci, layers[p_layer].y_sort_enabled); rs->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid()); rs->canvas_item_set_z_index(ci, layers[p_layer].z_index); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat())); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree())); rs->canvas_item_set_light_mask(ci, get_light_mask()); } @@ -1208,8 +1208,8 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List rs->canvas_item_set_z_as_relative_to_parent(ci, true); rs->canvas_item_set_z_index(ci, tile_z_index); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat())); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(get_texture_repeat_in_tree())); q.canvas_items.push_back(ci); @@ -2206,11 +2206,10 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat } } -TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints) { +TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints, TileSet::TerrainsPattern p_current_pattern) { if (!tile_set.is_valid()) { return TileSet::TerrainsPattern(); } - // Returns all tiles compatible with the given constraints. RBMap<TileSet::TerrainsPattern, int> terrain_pattern_score; RBSet<TileSet::TerrainsPattern> pattern_set = tile_set->get_terrains_pattern_set(p_terrain_set); @@ -2221,28 +2220,41 @@ TileSet::TerrainsPattern TileMap::_get_best_terrain_pattern_for_constraints(int // Check the center bit constraint TerrainConstraint terrain_constraint = TerrainConstraint(this, p_position, terrain_pattern.get_terrain()); RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint); - if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain() != terrain_pattern.get_terrain()) { + continue; // Ignore a pattern that cannot keep bits without constraints unmodified. } // Check the surrounding bits + bool invalid_pattern = false; for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { // Check if the bit is compatible with the constraints. TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit)); in_set_constraint_element = p_constraints.find(terrain_bit_constraint); - if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain_peering_bit(bit) != terrain_pattern.get_terrain_peering_bit(bit)) { + invalid_pattern = true; // Ignore a pattern that cannot keep bits without constraints unmodified. + break; } } } + if (invalid_pattern) { + continue; + } terrain_pattern_score[terrain_pattern] = score; } // Compute the minimum score - TileSet::TerrainsPattern min_score_pattern; + TileSet::TerrainsPattern min_score_pattern = p_current_pattern; int min_score = INT32_MAX; for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) { if (E.value < min_score) { @@ -2274,7 +2286,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_added_p return output; } -RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_list(int p_layer, const RBSet<Vector2i> &p_cell_list, int p_terrain_set, bool p_ignore_empty_terrains) const { +RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_painted_cells_list(int p_layer, const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { if (!tile_set.is_valid()) { return RBSet<TerrainConstraint>(); } @@ -2284,8 +2296,8 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l // Build a set of dummy constraints to get the constrained points. RBSet<TerrainConstraint> dummy_constraints; - for (const Vector2i &E : p_cell_list) { - for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides. + for (const Vector2i &E : p_painted) { + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits. TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { dummy_constraints.insert(TerrainConstraint(this, E, bit, -1)); @@ -2342,7 +2354,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l } // Add the centers as constraints - for (Vector2i E_coords : p_cell_list) { + for (Vector2i E_coords : p_painted) { TileData *tile_data = nullptr; TileMapCell cell = get_cell(p_layer, E_coords); if (cell.source_id != TileSet::INVALID_SOURCE) { @@ -2362,7 +2374,7 @@ RBSet<TileMap::TerrainConstraint> TileMap::_get_terrain_constraints_from_cells_l return constraints; } -HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) { +HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints) { if (!tile_set.is_valid()) { return HashMap<Vector2i, TileSet::TerrainsPattern>(); } @@ -2378,7 +2390,20 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_constraints(co const Vector2i &coords = p_to_replace[i]; // Select the best pattern for the given constraints - TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints); + TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(p_layer, coords); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + current_pattern = tile_data->get_terrains_pattern(); + } + } + } + TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern); // Update the constraint set with the new ones RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern); @@ -2492,12 +2517,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_connect(int p_ } // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2527,10 +2552,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay // Build list and set of tiles that can be modified (painted and their surroundings) Vector<Vector2i> can_modify_list; RBSet<Vector2i> can_modify_set; + RBSet<Vector2i> painted_set; for (int i = p_path.size() - 1; i >= 0; i--) { const Vector2i &coords = p_path[i]; can_modify_list.push_back(coords); can_modify_set.insert(coords); + painted_set.insert(coords); } for (Vector2i coords : p_path) { // Find the adequate neighbor @@ -2563,12 +2590,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_path(int p_lay } // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2580,10 +2607,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ // Build list and set of tiles that can be modified (painted and their surroundings). Vector<Vector2i> can_modify_list; RBSet<Vector2i> can_modify_set; + RBSet<Vector2i> painted_set; for (int i = p_coords_array.size() - 1; i >= 0; i--) { const Vector2i &coords = p_coords_array[i]; can_modify_list.push_back(coords); can_modify_set.insert(coords); + painted_set.insert(coords); } for (Vector2i coords : p_coords_array) { // Find the adequate neighbor @@ -2613,12 +2642,12 @@ HashMap<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_fill_pattern(int p_ } // Fills in the constraint list from modified tiles border. - for (TerrainConstraint c : _get_terrain_constraints_from_cells_list(p_layer, can_modify_set, p_terrain_set, p_ignore_empty_terrains)) { + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(p_layer, painted_set, p_terrain_set, p_ignore_empty_terrains)) { constraints.insert(c); } // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + output = terrain_fill_constraints(p_layer, can_modify_list, p_terrain_set, constraints); return output; } @@ -2627,14 +2656,38 @@ void TileMap::set_cells_terrain_connect(int p_layer, TypedArray<Vector2i> p_cell ERR_FAIL_INDEX(p_layer, (int)layers.size()); ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); - Vector<Vector2i> vector_cells; + Vector<Vector2i> cells_vector; + HashSet<Vector2i> painted_set; for (int i = 0; i < p_cells.size(); i++) { - vector_cells.push_back(p_cells[i]); - } - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, vector_cells, p_terrain_set, p_terrain, p_ignore_empty_terrains); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); - set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + cells_vector.push_back(p_cells[i]); + painted_set.insert(p_cells[i]); + } + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(p_layer, cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains); + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { + // Paint a random tile with the correct terrain for the painted path. + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(p_layer, kv.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + if (in_map_terrain_pattern != kv.value) { + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } + } } } @@ -2644,13 +2697,38 @@ void TileMap::set_cells_terrain_path(int p_layer, TypedArray<Vector2i> p_path, i ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); Vector<Vector2i> vector_path; + HashSet<Vector2i> painted_set; for (int i = 0; i < p_path.size(); i++) { vector_path.push_back(p_path[i]); + painted_set.insert(p_path[i]); } + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(p_layer, vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : terrain_fill_output) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value); - set_cell(p_layer, E.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { + // Paint a random tile with the correct terrain for the painted path. + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(p_layer, kv.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + if (in_map_terrain_pattern != kv.value) { + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(p_layer, kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } + } } } @@ -3717,13 +3795,14 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) { } void TileMap::set_texture_filter(TextureFilter p_texture_filter) { - // Set a default texture filter for the whole tilemap + // Set a default texture filter for the whole tilemap. CanvasItem::set_texture_filter(p_texture_filter); + TextureFilter target_filter = get_texture_filter_in_tree(); for (unsigned int layer = 0; layer < layers.size(); layer++) { for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { TileMapQuadrant &q = F->value; for (const RID &ci : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(p_texture_filter)); + RenderingServer::get_singleton()->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(target_filter)); _make_quadrant_dirty(F); } } @@ -3732,13 +3811,14 @@ void TileMap::set_texture_filter(TextureFilter p_texture_filter) { } void TileMap::set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) { - // Set a default texture repeat for the whole tilemap + // Set a default texture repeat for the whole tilemap. CanvasItem::set_texture_repeat(p_texture_repeat); + TextureRepeat target_repeat = get_texture_repeat_in_tree(); for (unsigned int layer = 0; layer < layers.size(); layer++) { for (HashMap<Vector2i, TileMapQuadrant>::Iterator F = layers[layer].quadrant_map.begin(); F; ++F) { TileMapQuadrant &q = F->value; for (const RID &ci : q.canvas_items) { - RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(p_texture_repeat)); + RenderingServer::get_singleton()->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(target_repeat)); _make_quadrant_dirty(F); } } diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index d468675e91..7f3e241871 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -266,9 +266,9 @@ private: void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant); // Terrains. - TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints); + TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, RBSet<TerrainConstraint> p_constraints, TileSet::TerrainsPattern p_current_pattern); RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; - RBSet<TerrainConstraint> _get_terrain_constraints_from_cells_list(int p_layer, const RBSet<Vector2i> &p_on_map, int p_terrain_set, bool p_ignore_empty_terrains) const; + RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(int p_layer, const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const; // Set and get tiles from data arrays. void _set_tile_data(int p_layer, const Vector<int> &p_data); @@ -352,7 +352,7 @@ public: void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern); // Terrains. - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(int p_layer, const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> p_constraints); // Not exposed. HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(int p_layer, const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed. diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index d57e6bd21c..2c5df48b75 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -490,9 +490,7 @@ Ref<Image> GPUParticlesCollisionSDF3D::bake() { params.thickness = th; _compute_sdf(¶ms); - Ref<Image> ret; - ret.instantiate(); - ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data); + Ref<Image> ret = Image::create_from_data(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, cells_data); ret->convert(Image::FORMAT_RH); //convert to half, save space ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 3ea1de57cd..1b5427c80d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -145,10 +145,7 @@ Array LightmapGIData::_get_light_textures_data() const { for (int i = 0; i < texture_count; i++) { int texture_slice_count = (i == texture_count - 1 && last_count != 0) ? last_count : slices_per_texture; - Ref<Image> texture_image; - texture_image.instantiate(); - - texture_image->create(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); + Ref<Image> texture_image = Image::create_empty(slice_width, slice_height * texture_slice_count, false, images[0]->get_format()); for (int j = 0; j < texture_slice_count; j++) { texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); @@ -818,7 +815,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } md.albedo_on_uv2.instantiate(); - md.albedo_on_uv2->create(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); + md.albedo_on_uv2->set_data(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); } md.emission_on_uv2 = images[RS::BAKE_CHANNEL_EMISSION]; @@ -1054,7 +1051,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } break; case ENVIRONMENT_MODE_CUSTOM_COLOR: { environment_image.instantiate(); - environment_image->create(128, 64, false, Image::FORMAT_RGBAF); + environment_image->initialize_data(128, 64, false, Image::FORMAT_RGBAF); Color c = environment_custom_color; c.r *= environment_custom_energy; c.g *= environment_custom_energy; diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 39068fe83c..f64cabb75c 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -495,8 +495,8 @@ void NavigationAgent3D::_request_repath() { void NavigationAgent3D::_check_distance_to_target() { if (!target_reached) { if (distance_to_target() < target_desired_distance) { - emit_signal(SNAME("target_reached")); target_reached = true; + emit_signal(SNAME("target_reached")); } } } diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 5f515acead..1743fa1838 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -253,9 +253,7 @@ Vector3 Node3D::get_global_rotation() const { void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { Transform3D transform = get_global_transform(); - Basis new_basis = transform.get_basis(); - new_basis.set_euler(p_euler_rad); - transform.set_basis(new_basis); + transform.basis = Basis::from_euler(p_euler_rad); set_global_transform(transform); } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 04f73a4cbd..457f634c34 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -71,8 +71,8 @@ public: }; private: - // For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaterinon/Scale and Euler Rotation/Scale. - // Transform and Quaterinon are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand). + // For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaternion/Scale and Euler Rotation/Scale. + // Transform and Quaternion are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand). // Euler needs to be kept separate because converting to Basis and back may result in a different vector (which is troublesome for users // editing in the inspector, not only because of the numerical precision loss but because they expect these rotations to be consistent, or support // "redundant" rotations for animation interpolation, like going from 0 to 720 degrees). diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 2013a93e26..6d4c18a69e 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -189,7 +189,7 @@ void VisibleOnScreenEnabler3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler3D::get_enable_node_path); ADD_GROUP("Enabling", "enable_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,When Paused"), "set_enable_mode", "get_enable_mode"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path"); BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT); diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index e93ad5ecbf..3868d1e42e 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -40,10 +40,6 @@ AABB VisualInstance3D::get_aabb() const { return AABB(); } -AABB VisualInstance3D::get_transformed_aabb() const { - return get_global_transform().xform(get_aabb()); -} - void VisualInstance3D::_update_visibility() { if (!is_inside_tree()) { return; @@ -121,8 +117,6 @@ void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_layer_mask_value", "layer_number", "value"), &VisualInstance3D::set_layer_mask_value); ClassDB::bind_method(D_METHOD("get_layer_mask_value", "layer_number"), &VisualInstance3D::get_layer_mask_value); - ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb); - GDVIRTUAL_BIND(_get_aabb); ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_RENDER), "set_layer_mask", "get_layer_mask"); } diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 4755545516..06a8c05faa 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -60,8 +60,6 @@ public: RID get_instance() const; virtual AABB get_aabb() const; - virtual AABB get_transformed_aabb() const; // helper - void set_base(const RID &p_base); RID get_base() const; diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 3dba0221bb..7caf2f4874 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -77,9 +77,7 @@ Dictionary VoxelGIData::_get_data() const { d["octree_cells"] = get_octree_cells(); d["octree_data"] = get_data_cells(); if (otsize != Vector3i()) { - Ref<Image> img; - img.instantiate(); - img->create(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); + Ref<Image> img = Image::create_from_data(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); Vector<uint8_t> df_png = img->save_png_to_buffer(); ERR_FAIL_COND_V(df_png.size() == 0, Dictionary()); d["octree_df_png"] = df_png; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index fe4ccbc7dc..f5d30c584f 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -363,7 +363,7 @@ void XRNode3D::_unbind_tracker() { } void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { - if (p_tracker_name == p_tracker_name) { + if (tracker_name == p_tracker_name) { // just in case unref our current tracker _unbind_tracker(); @@ -373,7 +373,7 @@ void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_t } void XRNode3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { - if (p_tracker_name == p_tracker_name) { + if (tracker_name == p_tracker_name) { // unref our tracker, it's no longer available _unbind_tracker(); } diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index a2c138c0b2..929bf27be6 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -1367,7 +1367,7 @@ void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) { Ref<Image> img = r->get_texture()->get_image(); if (img.is_valid() && !img->is_empty()) { - Vector2 ofs = mev->get_global_position() - r->get_visible_rect().get_position(); + Vector2 ofs = mev->get_global_position(); Color c = img->get_pixel(ofs.x, ofs.y); set_pick_color(c); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 565e450d60..2ec0a48278 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1436,13 +1436,6 @@ Rect2 Control::get_screen_rect() const { return r; } -Rect2 Control::get_window_rect() const { - ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - Rect2 gr = get_global_rect(); - gr.position += get_viewport()->get_visible_rect().position; - return gr; -} - Rect2 Control::get_anchorable_rect() const { return Rect2(Point2(), get_size()); } @@ -1564,7 +1557,7 @@ Size2 Control::get_minimum_size() const { return Vector2(); } -void Control::set_custom_minimum_size(const Size2i &p_custom) { +void Control::set_custom_minimum_size(const Size2 &p_custom) { if (p_custom == data.custom_minimum_size) { return; } @@ -1572,7 +1565,7 @@ void Control::set_custom_minimum_size(const Size2i &p_custom) { update_minimum_size(); } -Size2i Control::get_custom_minimum_size() const { +Size2 Control::get_custom_minimum_size() const { return data.custom_minimum_size; } @@ -3175,7 +3168,7 @@ void Control::_bind_methods() { ADD_GROUP("Layout", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction"); ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode"); ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION); diff --git a/scene/gui/control.h b/scene/gui/control.h index e526690cbe..72e870930d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -201,7 +201,7 @@ private: int h_size_flags = SIZE_FILL; int v_size_flags = SIZE_FILL; real_t expand = 1.0; - Point2i custom_minimum_size; + Point2 custom_minimum_size; // Input events and rendering. @@ -445,7 +445,6 @@ public: Rect2 get_rect() const; Rect2 get_global_rect() const; Rect2 get_screen_rect() const; - Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server Rect2 get_anchorable_rect() const override; void set_scale(const Vector2 &p_scale); @@ -463,8 +462,8 @@ public: virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; - void set_custom_minimum_size(const Size2i &p_custom); - Size2i get_custom_minimum_size() const; + void set_custom_minimum_size(const Size2 &p_custom); + Size2 get_custom_minimum_size() const; // Container sizing. diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 36e85b5d02..65670b7786 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -2175,6 +2175,12 @@ void LineEdit::_emit_text_change() { } void LineEdit::_shape() { + const Ref<Font> &font = theme_cache.font; + int font_size = theme_cache.font_size; + if (font.is_null()) { + return; + } + Size2 old_size = TS->shaped_text_get_size(text_rid); TS->shaped_text_clear(text_rid); @@ -2197,9 +2203,6 @@ void LineEdit::_shape() { } TS->shaped_text_set_preserve_control(text_rid, draw_control_chars); - const Ref<Font> &font = theme_cache.font; - int font_size = theme_cache.font_size; - ERR_FAIL_COND(font.is_null()); TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language); for (int i = 0; i < TextServer::SPACING_MAX; i++) { TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 193c6f1ce7..6899178885 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -338,7 +338,7 @@ void ScrollBar::_notification(int p_what) { if (scrolling) { if (get_value() != target_scroll) { double target = target_scroll - get_value(); - double dist = sqrt(target * target); + double dist = abs(target); double vel = ((target / dist) * 500) * get_physics_process_delta_time(); if (Math::abs(vel) >= dist) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 4cd244306c..48853cde9c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -464,7 +464,7 @@ void TextEdit::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (scrolling && get_v_scroll() != target_v_scroll) { double target_y = target_v_scroll - get_v_scroll(); - double dist = sqrt(target_y * target_y); + double dist = abs(target_y); // To ensure minimap is responsive override the speed setting. double vel = ((target_y / dist) * ((minimap_clicked) ? 3000 : v_scroll_speed)) * get_physics_process_delta_time(); @@ -2051,7 +2051,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (is_shortcut_keys_enabled()) { - // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE. + // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, CUT, COPY, PASTE. if (k->is_action("ui_text_select_all", true)) { select_all(); accept_event(); @@ -2062,6 +2062,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } + if (k->is_action("ui_text_add_selection_for_next_occurrence", true)) { + add_selection_for_next_occurrence(); + accept_event(); + return; + } if (k->is_action("ui_cut", true)) { cut(); accept_event(); @@ -3863,7 +3868,7 @@ void TextEdit::undo() { } caret_pos_dirty = true; } - queue_redraw(); + adjust_viewport_to_caret(); } void TextEdit::redo() { @@ -3915,7 +3920,7 @@ void TextEdit::redo() { } caret_pos_dirty = true; } - queue_redraw(); + adjust_viewport_to_caret(); } void TextEdit::clear_undo_history() { @@ -4832,6 +4837,45 @@ void TextEdit::select_word_under_caret(int p_caret) { merge_overlapping_carets(); } +void TextEdit::add_selection_for_next_occurrence() { + if (!selecting_enabled || !is_multiple_carets_enabled()) { + return; + } + + if (text.size() == 1 && text[0].length() == 0) { + return; + } + + // Always use the last caret, to correctly search for + // the next occurrence that comes after this caret. + int caret = get_caret_count() - 1; + + if (!has_selection(caret)) { + select_word_under_caret(caret); + return; + } + + const String &highlighted_text = get_selected_text(caret); + int column = get_selection_from_column(caret) + 1; + int line = get_caret_line(caret); + + const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column); + + if (next_occurrence.x == -1 || next_occurrence.y == -1) { + return; + } + + int to_column = get_selection_to_column(caret) + 1; + int end = next_occurrence.x + (to_column - column); + int new_caret = add_caret(next_occurrence.y, end); + + if (new_caret != -1) { + select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret); + adjust_viewport_to_caret(new_caret); + merge_overlapping_carets(); + } +} + void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret) { ERR_FAIL_INDEX(p_caret, carets.size()); if (!selecting_enabled) { @@ -6000,6 +6044,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); ClassDB::bind_method(D_METHOD("select_word_under_caret", "caret_index"), &TextEdit::select_word_under_caret, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_selection_for_next_occurrence"), &TextEdit::add_selection_for_next_occurrence); ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column", "caret_index"), &TextEdit::select, DEFVAL(0)); ClassDB::bind_method(D_METHOD("has_selection", "caret_index"), &TextEdit::has_selection, DEFVAL(-1)); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index e4af621b73..86b838e387 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -851,6 +851,7 @@ public: void select_all(); void select_word_under_caret(int p_caret = -1); + void add_selection_for_next_occurrence(); void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret = 0); bool has_selection(int p_caret = -1) const; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 05d86f77f2..840913d466 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -986,8 +986,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat); ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat); - ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children); - ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children); + ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode); + ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode); GDVIRTUAL_BIND(_draw); @@ -997,7 +997,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only,Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); ADD_GROUP("Texture", "texture_"); @@ -1035,6 +1035,11 @@ void CanvasItem::_bind_methods() { BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); + + BIND_ENUM_CONSTANT(CLIP_CHILDREN_DISABLED); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX); } Transform2D CanvasItem::get_canvas_transform() const { @@ -1092,7 +1097,7 @@ int CanvasItem::get_canvas_layer() const { } } -void CanvasItem::_update_texture_filter_changed(bool p_propagate) { +void CanvasItem::_refresh_texture_filter_cache() { if (!is_inside_tree()) { return; } @@ -1107,6 +1112,14 @@ void CanvasItem::_update_texture_filter_changed(bool p_propagate) { } else { texture_filter_cache = RS::CanvasItemTextureFilter(texture_filter); } +} + +void CanvasItem::_update_texture_filter_changed(bool p_propagate) { + if (!is_inside_tree()) { + return; + } + _refresh_texture_filter_cache(); + RS::get_singleton()->canvas_item_set_default_texture_filter(get_canvas_item(), texture_filter_cache); queue_redraw(); @@ -1133,7 +1146,7 @@ CanvasItem::TextureFilter CanvasItem::get_texture_filter() const { return texture_filter; } -void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { +void CanvasItem::_refresh_texture_repeat_cache() { if (!is_inside_tree()) { return; } @@ -1148,6 +1161,14 @@ void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { } else { texture_repeat_cache = RS::CanvasItemTextureRepeat(texture_repeat); } +} + +void CanvasItem::_update_texture_repeat_changed(bool p_propagate) { + if (!is_inside_tree()) { + return; + } + _refresh_texture_repeat_cache(); + RS::get_singleton()->canvas_item_set_default_texture_repeat(get_canvas_item(), texture_repeat_cache); queue_redraw(); if (p_propagate) { @@ -1169,26 +1190,39 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { notify_property_list_changed(); } -void CanvasItem::set_clip_children(bool p_enabled) { - if (clip_children == p_enabled) { +void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) { + ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX); + + if (clip_children_mode == p_clip_mode) { return; } - clip_children = p_enabled; + clip_children_mode = p_clip_mode; if (Object::cast_to<CanvasGroup>(this) != nullptr) { //avoid accidental bugs, make this not work on CanvasGroup return; } - RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED); + + RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CanvasGroupMode(clip_children_mode)); } -bool CanvasItem::is_clipping_children() const { - return clip_children; +CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const { + return clip_children_mode; } CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { return texture_repeat; } +CanvasItem::TextureFilter CanvasItem::get_texture_filter_in_tree() { + _refresh_texture_filter_cache(); + return (TextureFilter)texture_filter_cache; +} + +CanvasItem::TextureRepeat CanvasItem::get_texture_repeat_in_tree() { + _refresh_texture_repeat_cache(); + return (TextureRepeat)texture_repeat_cache; +} + CanvasItem::CanvasItem() : xform_change(this) { canvas_item = RenderingServer::get_singleton()->canvas_item_create(); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index b80289fdb4..565ea930ce 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -66,8 +66,16 @@ public: TEXTURE_REPEAT_MAX, }; + enum ClipChildrenMode { + CLIP_CHILDREN_DISABLED, + CLIP_CHILDREN_ONLY, + CLIP_CHILDREN_AND_DRAW, + CLIP_CHILDREN_MAX, + }; + private: - mutable SelfList<Node> xform_change; + mutable SelfList<Node> + xform_change; RID canvas_item; StringName canvas_group; @@ -85,7 +93,6 @@ private: Window *window = nullptr; bool visible = true; bool parent_visible_in_tree = false; - bool clip_children = false; bool pending_update = false; bool top_level = false; bool drawing = false; @@ -95,6 +102,8 @@ private: bool notify_local_transform = false; bool notify_transform = false; + ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED; + RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; @@ -121,7 +130,9 @@ private: static CanvasItem *current_item_drawn; friend class Viewport; + void _refresh_texture_repeat_cache(); void _update_texture_repeat_changed(bool p_propagate); + void _refresh_texture_filter_cache(); void _update_texture_filter_changed(bool p_propagate); protected: @@ -200,8 +211,8 @@ public: void queue_redraw(); void move_to_front(); - void set_clip_children(bool p_enabled); - bool is_clipping_children() const; + void set_clip_children_mode(ClipChildrenMode p_clip_mode); + ClipChildrenMode get_clip_children_mode() const; virtual void set_light_mask(int p_light_mask); int get_light_mask() const; @@ -310,6 +321,9 @@ public: virtual void set_texture_repeat(TextureRepeat p_texture_repeat); TextureRepeat get_texture_repeat() const; + TextureFilter get_texture_filter_in_tree(); + TextureRepeat get_texture_repeat_in_tree(); + // Used by control nodes to retrieve the parent's anchorable area virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }; @@ -321,6 +335,7 @@ public: VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) +VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode) class CanvasTexture : public Texture2D { GDCLASS(CanvasTexture, Texture2D); diff --git a/scene/main/multiplayer_peer.cpp b/scene/main/multiplayer_peer.cpp index 9b63118f7c..462dc1babb 100644 --- a/scene/main/multiplayer_peer.cpp +++ b/scene/main/multiplayer_peer.cpp @@ -78,6 +78,10 @@ bool MultiplayerPeer::is_refusing_new_connections() const { return refuse_connections; } +bool MultiplayerPeer::is_server_relay_supported() const { + return false; +} + void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); @@ -86,6 +90,8 @@ void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer); ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer); + ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel); + ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode); ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll); @@ -96,6 +102,8 @@ void MultiplayerPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections); + ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); @@ -177,6 +185,14 @@ bool MultiplayerPeerExtension::is_refusing_new_connections() const { return MultiplayerPeer::is_refusing_new_connections(); } +bool MultiplayerPeerExtension::is_server_relay_supported() const { + bool can_relay; + if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) { + return can_relay; + } + return MultiplayerPeer::is_server_relay_supported(); +} + void MultiplayerPeerExtension::_bind_methods() { GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size"); GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size"); diff --git a/scene/main/multiplayer_peer.h b/scene/main/multiplayer_peer.h index ab7483ece5..63ce66871e 100644 --- a/scene/main/multiplayer_peer.h +++ b/scene/main/multiplayer_peer.h @@ -74,10 +74,13 @@ public: virtual TransferMode get_transfer_mode() const; virtual void set_refuse_new_connections(bool p_enable); virtual bool is_refusing_new_connections() const; + virtual bool is_server_relay_supported() const; virtual void set_target_peer(int p_peer_id) = 0; virtual int get_packet_peer() const = 0; + virtual TransferMode get_packet_mode() const = 0; + virtual int get_packet_channel() const = 0; virtual bool is_server() const = 0; @@ -123,12 +126,17 @@ public: virtual bool is_refusing_new_connections() const override; GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional. + virtual bool is_server_relay_supported() const override; + GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional. + EXBIND1(set_transfer_channel, int); EXBIND0RC(int, get_transfer_channel); EXBIND1(set_transfer_mode, TransferMode); EXBIND0RC(TransferMode, get_transfer_mode); EXBIND1(set_target_peer, int); EXBIND0RC(int, get_packet_peer); + EXBIND0RC(TransferMode, get_packet_mode); + EXBIND0RC(int, get_packet_channel); EXBIND0RC(bool, is_server); EXBIND0(poll); EXBIND0RC(int, get_unique_id); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 6ab27853f1..2ea45df309 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -322,62 +322,62 @@ void Node::_propagate_exit_tree() { data.depth = -1; } -void Node::move_child(Node *p_child, int p_pos) { +void Node::move_child(Node *p_child, int p_index) { ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); // We need to check whether node is internal and move it only in the relevant node range. if (p_child->_is_internal_front()) { - if (p_pos < 0) { - p_pos += data.internal_children_front; + if (p_index < 0) { + p_index += data.internal_children_front; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_front, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_front, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, p_index); } else if (p_child->_is_internal_back()) { - if (p_pos < 0) { - p_pos += data.internal_children_back; + if (p_index < 0) { + p_index += data.internal_children_back; } - ERR_FAIL_INDEX_MSG(p_pos, data.internal_children_back, vformat("Invalid new child position: %d. Child is internal.", p_pos)); - _move_child(p_child, data.children.size() - data.internal_children_back + p_pos); + ERR_FAIL_INDEX_MSG(p_index, data.internal_children_back, vformat("Invalid new child index: %d. Child is internal.", p_index)); + _move_child(p_child, data.children.size() - data.internal_children_back + p_index); } else { - if (p_pos < 0) { - p_pos += get_child_count(false); + if (p_index < 0) { + p_index += get_child_count(false); } - ERR_FAIL_INDEX_MSG(p_pos, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child position: %d.", p_pos)); - _move_child(p_child, p_pos + data.internal_children_front); + ERR_FAIL_INDEX_MSG(p_index, data.children.size() + 1 - data.internal_children_front - data.internal_children_back, vformat("Invalid new child index: %d.", p_index)); + _move_child(p_child, p_index + data.internal_children_front); } } -void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { +void Node::_move_child(Node *p_child, int p_index, bool p_ignore_end) { ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, move_child() failed. Consider using call_deferred(\"move_child\") instead (or \"popup\" if this is from a popup)."); // Specifying one place beyond the end - // means the same as moving to the last position + // means the same as moving to the last index if (!p_ignore_end) { // p_ignore_end is a little hack to make back internal children work properly. if (p_child->_is_internal_front()) { - if (p_pos == data.internal_children_front) { - p_pos--; + if (p_index == data.internal_children_front) { + p_index--; } } else if (p_child->_is_internal_back()) { - if (p_pos == data.children.size()) { - p_pos--; + if (p_index == data.children.size()) { + p_index--; } } else { - if (p_pos == data.children.size() - data.internal_children_back) { - p_pos--; + if (p_index == data.children.size() - data.internal_children_back) { + p_index--; } } } - if (p_child->data.pos == p_pos) { + if (p_child->data.index == p_index) { return; //do nothing } - int motion_from = MIN(p_pos, p_child->data.pos); - int motion_to = MAX(p_pos, p_child->data.pos); + int motion_from = MIN(p_index, p_child->data.index); + int motion_to = MAX(p_index, p_child->data.index); - data.children.remove_at(p_child->data.pos); - data.children.insert(p_pos, p_child); + data.children.remove_at(p_child->data.index); + data.children.insert(p_index, p_child); if (data.tree) { data.tree->tree_changed(); @@ -386,7 +386,7 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) { data.blocked++; //new pos first for (int i = motion_from; i <= motion_to; i++) { - data.children[i]->data.pos = i; + data.children[i]->data.index = i; } // notification second move_child_notify(p_child); @@ -1104,7 +1104,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { //add a child node quickly, without name validation p_child->data.name = p_name; - p_child->data.pos = data.children.size(); + p_child->data.index = data.children.size(); data.children.push_back(p_child); p_child->data.parent = this; @@ -1171,9 +1171,9 @@ void Node::remove_child(Node *p_child) { Node **children = data.children.ptrw(); int idx = -1; - if (p_child->data.pos >= 0 && p_child->data.pos < child_count) { - if (children[p_child->data.pos] == p_child) { - idx = p_child->data.pos; + if (p_child->data.index >= 0 && p_child->data.index < child_count) { + if (children[p_child->data.index] == p_child) { + idx = p_child->data.index; } } @@ -1209,12 +1209,12 @@ void Node::remove_child(Node *p_child) { children = data.children.ptrw(); for (int i = idx; i < child_count; i++) { - children[i]->data.pos = i; + children[i]->data.index = i; children[i]->notification(NOTIFICATION_MOVED_IN_PARENT); } p_child->data.parent = nullptr; - p_child->data.pos = -1; + p_child->data.index = -1; if (data.inside_tree) { p_child->_propagate_after_exit_tree(); @@ -1473,7 +1473,7 @@ bool Node::is_greater_than(const Node *p_node) const { int idx = data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, data.depth, false); - this_stack[idx--] = n->data.pos; + this_stack[idx--] = n->data.index; n = n->data.parent; } ERR_FAIL_COND_V(idx != -1, false); @@ -1481,7 +1481,7 @@ bool Node::is_greater_than(const Node *p_node) const { idx = p_node->data.depth - 1; while (n) { ERR_FAIL_INDEX_V(idx, p_node->data.depth, false); - that_stack[idx--] = n->data.pos; + that_stack[idx--] = n->data.index; n = n->data.parent; } @@ -1892,9 +1892,9 @@ int Node::get_index(bool p_include_internal) const { ERR_FAIL_COND_V_MSG(!p_include_internal && (_is_internal_front() || _is_internal_back()), -1, "Node is internal. Can't get index with 'include_internal' being false."); if (data.parent && !p_include_internal) { - return data.pos - data.parent->data.internal_children_front; + return data.index - data.parent->data.internal_children_front; } - return data.pos; + return data.index; } Ref<Tween> Node::create_tween() { @@ -2389,12 +2389,12 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) { } Node *parent = data.parent; - int pos_in_parent = data.pos; + int index_in_parent = data.index; if (data.parent) { parent->remove_child(this); parent->add_child(p_node); - parent->move_child(p_node, pos_in_parent); + parent->move_child(p_node, index_in_parent); } while (get_child_count()) { @@ -2757,7 +2757,7 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("add_to_group", "group", "persistent"), &Node::add_to_group, DEFVAL(false)); ClassDB::bind_method(D_METHOD("remove_from_group", "group"), &Node::remove_from_group); ClassDB::bind_method(D_METHOD("is_in_group", "group"), &Node::is_in_group); - ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_position"), &Node::move_child); + ClassDB::bind_method(D_METHOD("move_child", "child_node", "to_index"), &Node::move_child); ClassDB::bind_method(D_METHOD("get_groups"), &Node::_get_groups); ClassDB::bind_method(D_METHOD("set_owner", "owner"), &Node::set_owner); ClassDB::bind_method(D_METHOD("get_owner"), &Node::get_owner); diff --git a/scene/main/node.h b/scene/main/node.h index 8c82c41e46..c8c8c395ce 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -105,7 +105,7 @@ private: int internal_children_front = 0; int internal_children_back = 0; - int pos = -1; + int index = -1; int depth = -1; int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed. StringName name; @@ -187,8 +187,8 @@ private: Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.pos < data.parent->data.internal_children_front; } - _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.pos >= data.parent->data.children.size() - data.parent->data.internal_children_back; } + _FORCE_INLINE_ bool _is_internal_front() const { return data.parent && data.index < data.parent->data.internal_children_front; } + _FORCE_INLINE_ bool _is_internal_back() const { return data.parent && data.index >= data.parent->data.children.size() - data.parent->data.internal_children_back; } friend class SceneTree; @@ -347,8 +347,8 @@ public: void get_groups(List<GroupInfo> *p_groups) const; int get_persistent_group_count() const; - void move_child(Node *p_child, int p_pos); - void _move_child(Node *p_child, int p_pos, bool p_ignore_end = false); + void move_child(Node *p_child, int p_index); + void _move_child(Node *p_child, int p_index, bool p_ignore_end = false); void set_owner(Node *p_owner); Node *get_owner() const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 270e5b7025..5ff9a29792 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -896,7 +896,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal call_lock++; - Vector<Node *> no_context_nodes; + Vector<ObjectID> no_context_node_ids; // Nodes may be deleted due to this shortcut input. for (int i = gr_node_count - 1; i >= 0; i--) { if (p_viewport->is_input_handled()) { @@ -922,7 +922,7 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal // If calling shortcut input on a control, ensure it respects the shortcut context. // Shortcut context (based on focus) only makes sense for controls (UI), so don't need to worry about it for nodes if (c->get_shortcut_context() == nullptr) { - no_context_nodes.append(n); + no_context_node_ids.append(n->get_instance_id()); continue; } if (!c->is_focus_owner_in_shortcut_context()) { @@ -941,8 +941,11 @@ void SceneTree::_call_input_pause(const StringName &p_group, CallInputType p_cal } } - for (Node *n : no_context_nodes) { - n->_call_shortcut_input(p_input); + for (const ObjectID &id : no_context_node_ids) { + Node *n = Object::cast_to<Node>(ObjectDB::get_instance(id)); + if (n) { + n->_call_shortcut_input(p_input); + } } call_lock--; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f7a515dc1d..ad40346c36 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1263,6 +1263,7 @@ void Viewport::_gui_show_tooltip() { Point2 tooltip_offset = ProjectSettings::get_singleton()->get("display/mouse_cursor/tooltip_position_offset"); Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size()); + r.size = r.size.min(panel->get_max_size()); Window *window = gui.tooltip_popup->get_parent_visible_window(); Rect2i vr = window->get_usable_parent_rect(); @@ -1403,10 +1404,6 @@ Control *Viewport::gui_find_control(const Point2 &p_global) { } Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform, Transform2D &r_inv_xform) { - if (Object::cast_to<Viewport>(p_node)) { - return nullptr; - } - if (!p_node->is_visible()) { return nullptr; // Canvas item hidden, discard. } @@ -1490,7 +1487,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mb->get_position(); - gui.last_mouse_pos = mpos; if (mb->is_pressed()) { Size2 pos = mpos; if (gui.mouse_focus_mask != MouseButton::NONE) { @@ -1644,8 +1640,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.key_event_accepted = false; Point2 mpos = mm->get_position(); - gui.last_mouse_pos = mpos; - // Drag & drop. if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { gui.drag_accum += mm->get_relative(); @@ -2759,6 +2753,11 @@ void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) { ev = p_event; } + Ref<InputEventMouse> me = ev; + if (me.is_valid()) { + gui.last_mouse_pos = me->get_position(); + } + if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) { set_input_as_handled(); return; @@ -3727,6 +3726,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform); + ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform); ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect); ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 8fb497113d..ebb11ecee8 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -587,6 +587,18 @@ bool Window::is_visible() const { } void Window::_update_window_size() { + // Force window to respect size limitations of rendering server + RenderingServer *rendering_server = RenderingServer::get_singleton(); + if (rendering_server) { + Size2i max_window_size = rendering_server->get_maximum_viewport_size(); + + if (max_window_size != Size2i()) { + size = size.min(max_window_size); + min_size = min_size.min(max_window_size); + max_size = max_size.min(max_window_size); + } + } + Size2i size_limit; if (wrap_controls) { size_limit = get_contents_minimum_size(); @@ -1828,6 +1840,11 @@ void Window::_bind_methods() { } Window::Window() { + RenderingServer *rendering_server = RenderingServer::get_singleton(); + if (rendering_server) { + max_size = rendering_server->get_maximum_viewport_size(); + } + theme_owner = memnew(ThemeOwner); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); } diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index 0505f6b559..1b06e09bb8 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -639,9 +639,7 @@ void BitMap::resize(const Size2i &p_new_size) { } Ref<Image> BitMap::convert_to_image() const { - Ref<Image> image; - image.instantiate(); - image->create(width, height, false, Image::FORMAT_L8); + Ref<Image> image = Image::create_empty(width, height, false, Image::FORMAT_L8); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index ebdaaaa95f..23bd8a4be4 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -78,7 +78,7 @@ float Environment::get_sky_custom_fov() const { void Environment::set_sky_rotation(const Vector3 &p_rotation) { bg_sky_rotation = p_rotation; - RS::get_singleton()->environment_set_sky_orientation(environment, Basis(p_rotation)); + RS::get_singleton()->environment_set_sky_orientation(environment, Basis::from_euler(p_rotation)); } Vector3 Environment::get_sky_rotation() const { diff --git a/scene/resources/shape_2d.cpp b/scene/resources/shape_2d.cpp index fe43f345d4..61daf801e8 100644 --- a/scene/resources/shape_2d.cpp +++ b/scene/resources/shape_2d.cpp @@ -105,6 +105,7 @@ void Shape2D::_bind_methods() { ClassDB::bind_method(D_METHOD("collide_and_get_contacts", "local_xform", "with_shape", "shape_xform"), &Shape2D::collide_and_get_contacts); ClassDB::bind_method(D_METHOD("collide_with_motion_and_get_contacts", "local_xform", "local_motion", "with_shape", "shape_xform", "shape_motion"), &Shape2D::collide_with_motion_and_get_contacts); ClassDB::bind_method(D_METHOD("draw", "canvas_item", "color"), &Shape2D::draw); + ClassDB::bind_method(D_METHOD("get_rect"), &Shape2D::get_rect); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_solver_bias", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_custom_solver_bias", "get_custom_solver_bias"); } diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index 4085aa8ec2..b994498dbf 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -122,7 +122,7 @@ void Texture2D::_bind_methods() { GDVIRTUAL_BIND(_draw, "to_canvas_item", "pos", "modulate", "transpose") GDVIRTUAL_BIND(_draw_rect, "to_canvas_item", "rect", "tile", "modulate", "transpose") - GDVIRTUAL_BIND(_draw_rect_region, "tp_canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"); + GDVIRTUAL_BIND(_draw_rect_region, "to_canvas_item", "rect", "src_rect", "modulate", "transpose", "clip_uv"); } Texture2D::Texture2D() { @@ -653,7 +653,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si Image::Format format = Image::Format(f->get_32()); if (data_format == DATA_FORMAT_PNG || data_format == DATA_FORMAT_WEBP || data_format == DATA_FORMAT_BASIS_UNIVERSAL) { - //look for a PNG or WEBP file inside + //look for a PNG or WebP file inside int sw = w; int sh = h; @@ -740,7 +740,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si } } - image->create(w, h, true, mipmap_images[0]->get_format(), img_data); + image->set_data(w, h, true, mipmap_images[0]->get_format(), img_data); return image; } @@ -766,10 +766,7 @@ Ref<Image> CompressedTexture2D::load_image_from_file(Ref<FileAccess> f, int p_si f->get_buffer(wr, data.size()); } - Ref<Image> image; - image.instantiate(); - - image->create(tw, th, mipmaps - i ? true : false, format, data); + Ref<Image> image = Image::create_from_data(tw, th, mipmaps - i ? true : false, format, data); return image; } @@ -2336,11 +2333,11 @@ void GradientTexture2D::_update() { image.instantiate(); if (gradient->get_points_count() <= 1) { // No need to interpolate. - image->create(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); + image->initialize_data(width, height, false, (use_hdr) ? Image::FORMAT_RGBAF : Image::FORMAT_RGBA8); image->fill((gradient->get_points_count() == 1) ? gradient->get_color(0) : Color(0, 0, 0, 1)); } else { if (use_hdr) { - image->create(width, height, false, Image::FORMAT_RGBAF); + image->initialize_data(width, height, false, Image::FORMAT_RGBAF); Gradient &g = **gradient; // `create()` isn't available for non-uint8_t data, so fill in the data manually. for (int y = 0; y < height; y++) { @@ -2367,7 +2364,7 @@ void GradientTexture2D::_update() { } } } - image->create(width, height, false, Image::FORMAT_RGBA8, data); + image->set_data(width, height, false, Image::FORMAT_RGBA8, data); } } diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index d7d7b5fe31..3caf6484d9 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -1801,11 +1801,11 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) { // Get the best tile. Ref<Texture2D> texture = counts[terrain_set][terrain].texture; Rect2i region = counts[terrain_set][terrain].region; - image->create(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); + image->initialize_data(region.size.x, region.size.y, false, Image::FORMAT_RGBA8); image->blit_rect(texture->get_image(), region, Point2i()); image->resize(p_size.x, p_size.y, Image::INTERPOLATE_NEAREST); } else { - image->create(1, 1, false, Image::FORMAT_RGBA8); + image->initialize_data(1, 1, false, Image::FORMAT_RGBA8); image->set_pixel(0, 0, get_terrain_color(terrain_set, terrain)); } Ref<ImageTexture> icon = ImageTexture::create_from_image(image); @@ -4602,9 +4602,7 @@ void TileSetAtlasSource::_update_padded_texture() { return; } - Ref<Image> image; - image.instantiate(); - image->create(size.x, size.y, false, src->get_format()); + Ref<Image> image = Image::create_empty(size.x, size.y, false, src->get_format()); for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) { for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index e156679711..8f175e99a6 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -277,6 +277,9 @@ public: bool operator<(const TerrainsPattern &p_terrains_pattern) const; bool operator==(const TerrainsPattern &p_terrains_pattern) const; + bool operator!=(const TerrainsPattern &p_terrains_pattern) const { + return !operator==(p_terrains_pattern); + }; void set_terrain(int p_terrain); int get_terrain() const; diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index de13912b75..04637983b5 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -1642,17 +1642,20 @@ bool VisualShaderNodeLinearSceneDepth::has_output_port_preview(int p_port) const String VisualShaderNodeLinearSceneDepth::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; + code += " {\n"; - code += " float _log_depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).x;\n"; - code += " vec3 _depth_ndc = vec3(SCREEN_UV * 2.0 - 1.0, _log_depth);\n"; - code += " vec4 _depth_view = INV_PROJECTION_MATRIX * vec4(_depth_ndc, 1.0);\n"; - code += " _depth_view.xyz /= _depth_view.w;"; - code += vformat(" %s = -_depth_view.z;", p_output_vars[0]); + code += " float __log_depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).x;\n"; + code += " vec3 __depth_ndc = vec3(SCREEN_UV * 2.0 - 1.0, __log_depth);\n"; + code += " vec4 __depth_view = INV_PROJECTION_MATRIX * vec4(__depth_ndc, 1.0);\n"; + code += " __depth_view.xyz /= __depth_view.w;\n"; + code += vformat(" %s = -__depth_view.z;\n", p_output_vars[0]); + code += " }\n"; return code; } VisualShaderNodeLinearSceneDepth::VisualShaderNodeLinearSceneDepth() { + simple_decl = false; } ////////////// Float Op @@ -3204,6 +3207,7 @@ String VisualShaderNodeUVPolarCoord::get_output_port_name(int p_port) const { String VisualShaderNodeUVPolarCoord::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; + code += " {\n"; String uv; if (p_input_vars[0].is_empty()) { @@ -3220,17 +3224,18 @@ String VisualShaderNodeUVPolarCoord::generate_code(Shader::Mode p_mode, VisualSh String repeat = vformat("%s", p_input_vars[3]); if (p_mode == Shader::MODE_CANVAS_ITEM) { - code += vformat(" vec2 __dir = %s - %s;\n", uv, center); - code += " float __radius = length(__dir) * 2.0;\n"; - code += " float __angle = atan(__dir.y, __dir.x) * 1.0/(PI * 2.0);\n"; - code += vformat(" %s = mod(vec2(__radius * %s, __angle * %s), 1.0);\n", p_output_vars[0], zoom, repeat); + code += vformat(" vec2 __dir = %s - %s;\n", uv, center); + code += " float __radius = length(__dir) * 2.0;\n"; + code += " float __angle = atan(__dir.y, __dir.x) * 1.0 / (PI * 2.0);\n"; + code += vformat(" %s = mod(vec2(__radius * %s, __angle * %s), 1.0);\n", p_output_vars[0], zoom, repeat); } else { - code += vformat(" vec2 __dir = %s - %s;\n", uv, center); - code += " float __radius = length(__dir) * 2.0;\n"; - code += " float __angle = atan(__dir.y, __dir.x) * 1.0/(PI * 2.0);\n"; - code += vformat(" %s = vec2(__radius * %s, __angle * %s);\n", p_output_vars[0], zoom, repeat); + code += vformat(" vec2 __dir = %s - %s;\n", uv, center); + code += " float __radius = length(__dir) * 2.0;\n"; + code += " float __angle = atan(__dir.y, __dir.x) * 1.0 / (PI * 2.0);\n"; + code += vformat(" %s = vec2(__radius * %s, __angle * %s);\n", p_output_vars[0], zoom, repeat); } + code += " }\n"; return code; } @@ -3238,6 +3243,8 @@ VisualShaderNodeUVPolarCoord::VisualShaderNodeUVPolarCoord() { set_input_port_default_value(1, Vector2(0.5, 0.5)); // center set_input_port_default_value(2, 1.0); // zoom set_input_port_default_value(3, 1.0); // repeat + + simple_decl = false; } ////////////// Dot Product @@ -7256,22 +7263,26 @@ bool VisualShaderNodeProximityFade::has_output_port_preview(int p_port) const { String VisualShaderNodeProximityFade::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; + code += " {\n"; String proximity_fade_distance = vformat("%s", p_input_vars[0]); - code += " float __depth_tex = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;\n"; + code += " float __depth_tex = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;\n"; if (!RenderingServer::get_singleton()->is_low_end()) { - code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, __depth_tex, 1.0);\n"; } else { - code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(vec3(SCREEN_UV, __depth_tex) * 2.0 - 1.0, 1.0);\n"; + code += " vec4 __depth_world_pos = INV_PROJECTION_MATRIX * vec4(vec3(SCREEN_UV, __depth_tex) * 2.0 - 1.0, 1.0);\n"; } - code += " __depth_world_pos.xyz /= __depth_world_pos.z;\n"; - code += vformat(" %s = clamp(1.0 - smoothstep(__depth_world_pos.z + %s, __depth_world_pos.z, VERTEX.z), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0]); + code += " __depth_world_pos.xyz /= __depth_world_pos.w;\n"; + code += vformat(" %s = clamp(1.0 - smoothstep(__depth_world_pos.z + %s, __depth_world_pos.z, VERTEX.z), 0.0, 1.0);\n", p_output_vars[0], p_input_vars[0]); + code += " }\n"; return code; } VisualShaderNodeProximityFade::VisualShaderNodeProximityFade() { set_input_port_default_value(0, 1.0); + + simple_decl = false; } ////////////// Random Range @@ -7416,11 +7427,11 @@ String VisualShaderNodeRemap::get_output_port_name(int p_port) const { String VisualShaderNodeRemap::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { String code; - - code += vformat(" float _input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); - code += vformat(" float _output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); - code += vformat(" %s = %s + _output_range * ((%s - %s) / _input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); - + code += " {\n"; + code += vformat(" float __input_range = %s - %s;\n", p_input_vars[2], p_input_vars[1]); + code += vformat(" float __output_range = %s - %s;\n", p_input_vars[4], p_input_vars[3]); + code += vformat(" %s = %s + __output_range * ((%s - %s) / __input_range);\n", p_output_vars[0], p_input_vars[3], p_input_vars[0], p_input_vars[1]); + code += " }\n"; return code; } @@ -7429,4 +7440,6 @@ VisualShaderNodeRemap::VisualShaderNodeRemap() { set_input_port_default_value(2, 1.0); set_input_port_default_value(3, 0.0); set_input_port_default_value(4, 1.0); + + simple_decl = false; } diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp index ab7354f6e7..f125b05a26 100644 --- a/scene/resources/visual_shader_particle_nodes.cpp +++ b/scene/resources/visual_shader_particle_nodes.cpp @@ -460,9 +460,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector2> image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBF); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); } for (int i = 0; i < p_array.size(); i++) { @@ -481,9 +481,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Vector3> image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBF); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBF); } for (int i = 0; i < p_array.size(); i++) { @@ -502,9 +502,9 @@ void VisualShaderNodeParticleMeshEmitter::_update_texture(const Vector<Color> &p image.instantiate(); if (p_array.size() == 0) { - image->create(1, 1, false, Image::Format::FORMAT_RGBA8); + image->initialize_data(1, 1, false, Image::Format::FORMAT_RGBA8); } else { - image->create(p_array.size(), 1, false, Image::Format::FORMAT_RGBA8); + image->initialize_data(p_array.size(), 1, false, Image::Format::FORMAT_RGBA8); } for (int i = 0; i < p_array.size(); i++) { diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 4b97bede56..f079085543 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -865,9 +865,9 @@ Vector<String> DisplayServer::get_create_function_rendering_drivers(int p_index) return server_create_functions[p_index].get_rendering_drivers_function(); } -DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { +DisplayServer *DisplayServer::create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); - return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_resolution, r_error); + return server_create_functions[p_index].create_function(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, r_error); } void DisplayServer::_input_set_mouse_mode(Input::MouseMode p_mode) { diff --git a/servers/display_server.h b/servers/display_server.h index 31ebf12531..55e9fb55dc 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -72,7 +72,7 @@ public: WINDOW_VIEW, }; - typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Size2i &, Error &r_error); + typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, VSyncMode, uint32_t, const Point2i *, const Size2i &, Error &r_error); typedef Vector<String> (*GetRenderingDriversFunction)(); private: @@ -483,7 +483,7 @@ public: static int get_create_function_count(); static const char *get_create_function_name(int p_index); static Vector<String> get_create_function_rendering_drivers(int p_index); - static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + static DisplayServer *create(int p_index, const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error); DisplayServer(); ~DisplayServer(); diff --git a/servers/display_server_headless.h b/servers/display_server_headless.h index 3853ff4fdc..e1a27b6137 100644 --- a/servers/display_server_headless.h +++ b/servers/display_server_headless.h @@ -45,7 +45,7 @@ private: return drivers; } - static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, Error &r_error) { r_error = OK; RasterizerDummy::make_current(); return memnew(DisplayServerHeadless()); diff --git a/servers/physics_3d/godot_body_3d.cpp b/servers/physics_3d/godot_body_3d.cpp index cec233d95b..53f4ab86f9 100644 --- a/servers/physics_3d/godot_body_3d.cpp +++ b/servers/physics_3d/godot_body_3d.cpp @@ -118,7 +118,7 @@ void GodotBody3D::update_mass_properties() { shape_inertia_tensor = shape_basis * shape_inertia_tensor * shape_basis.transposed(); Vector3 shape_origin = shape_transform.origin - center_of_mass_local; - inertia_tensor += shape_inertia_tensor + (Basis() * shape_origin.dot(shape_origin) - shape_origin.outer(shape_origin)) * mass; + inertia_tensor += shape_inertia_tensor + (Basis() * shape_origin.dot(shape_origin) - shape_origin.outer(shape_origin)) * mass_new; } // Set the inertia to a valid value when there are no valid shapes. diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp index cd61ceab62..1443cd166b 100644 --- a/servers/physics_3d/godot_shape_3d.cpp +++ b/servers/physics_3d/godot_shape_3d.cpp @@ -817,48 +817,78 @@ GodotCylinderShape3D::GodotCylinderShape3D() {} /********** CONVEX POLYGON *************/ void GodotConvexPolygonShape3D::project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const { - int vertex_count = mesh.vertices.size(); + uint32_t vertex_count = mesh.vertices.size(); if (vertex_count == 0) { return; } const Vector3 *vrts = &mesh.vertices[0]; - for (int i = 0; i < vertex_count; i++) { - real_t d = p_normal.dot(p_transform.xform(vrts[i])); + if (vertex_count > 3 * extreme_vertices.size()) { + // For a large mesh, two calls to get_support() is faster than a full + // scan over all vertices. - if (i == 0 || d > r_max) { - r_max = d; - } - if (i == 0 || d < r_min) { - r_min = d; + Vector3 n = p_transform.basis.xform_inv(p_normal).normalized(); + r_min = p_normal.dot(p_transform.xform(get_support(-n))); + r_max = p_normal.dot(p_transform.xform(get_support(n))); + } else { + for (uint32_t i = 0; i < vertex_count; i++) { + real_t d = p_normal.dot(p_transform.xform(vrts[i])); + + if (i == 0 || d > r_max) { + r_max = d; + } + if (i == 0 || d < r_min) { + r_min = d; + } } } } Vector3 GodotConvexPolygonShape3D::get_support(const Vector3 &p_normal) const { - Vector3 n = p_normal; - - int vert_support_idx = -1; - real_t support_max = 0; - - int vertex_count = mesh.vertices.size(); - if (vertex_count == 0) { + if (mesh.vertices.size() == 0) { return Vector3(); } - const Vector3 *vrts = &mesh.vertices[0]; - - for (int i = 0; i < vertex_count; i++) { - real_t d = n.dot(vrts[i]); + // Find an initial guess for the support vertex by checking the ones we + // found in _setup(). - if (i == 0 || d > support_max) { - support_max = d; - vert_support_idx = i; + int best_vertex = -1; + real_t max_support = 0.0; + for (uint32_t i = 0; i < extreme_vertices.size(); i++) { + real_t s = p_normal.dot(mesh.vertices[extreme_vertices[i]]); + if (best_vertex == -1 || s > max_support) { + best_vertex = extreme_vertices[i]; + max_support = s; } } + if (extreme_vertices.size() == mesh.vertices.size()) { + // We've already checked every vertex, so we can return now. + return mesh.vertices[best_vertex]; + } + + // Move along the surface until we reach the true support vertex. - return vrts[vert_support_idx]; + int last_vertex = -1; + while (true) { + int next_vertex = -1; + for (uint32_t i = 0; i < vertex_neighbors[best_vertex].size(); i++) { + int vert = vertex_neighbors[best_vertex][i]; + if (vert != last_vertex) { + real_t s = p_normal.dot(mesh.vertices[vert]); + if (s > max_support) { + next_vertex = vert; + max_support = s; + break; + } + } + } + if (next_vertex == -1) { + return mesh.vertices[best_vertex]; + } + last_vertex = best_vertex; + best_vertex = next_vertex; + } } void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const { @@ -1067,6 +1097,43 @@ void GodotConvexPolygonShape3D::_setup(const Vector<Vector3> &p_vertices) { } configure(_aabb); + + // Pre-compute the extreme vertices in 26 directions. This will be used + // to speed up get_support() by letting us quickly get a good guess for + // the support vertex. + + for (int x = -1; x < 2; x++) { + for (int y = -1; y < 2; y++) { + for (int z = -1; z < 2; z++) { + if (x != 0 || y != 0 || z != 0) { + Vector3 dir(x, y, z); + dir.normalize(); + real_t max_support = 0.0; + int best_vertex = -1; + for (uint32_t i = 0; i < mesh.vertices.size(); i++) { + real_t s = dir.dot(mesh.vertices[i]); + if (best_vertex == -1 || s > max_support) { + best_vertex = i; + max_support = s; + } + } + if (extreme_vertices.find(best_vertex) == -1) + extreme_vertices.push_back(best_vertex); + } + } + } + } + + // Record all the neighbors of each vertex. This is used in get_support(). + + if (extreme_vertices.size() < mesh.vertices.size()) { + vertex_neighbors.resize(mesh.vertices.size()); + for (uint32_t i = 0; i < mesh.edges.size(); i++) { + Geometry3D::MeshData::Edge &edge = mesh.edges[i]; + vertex_neighbors[edge.vertex_a].push_back(edge.vertex_b); + vertex_neighbors[edge.vertex_b].push_back(edge.vertex_a); + } + } } void GodotConvexPolygonShape3D::set_data(const Variant &p_data) { diff --git a/servers/physics_3d/godot_shape_3d.h b/servers/physics_3d/godot_shape_3d.h index 1fc8f7c711..dc8e34e2bc 100644 --- a/servers/physics_3d/godot_shape_3d.h +++ b/servers/physics_3d/godot_shape_3d.h @@ -277,6 +277,8 @@ public: struct GodotConvexPolygonShape3D : public GodotShape3D { Geometry3D::MeshData mesh; + LocalVector<int> extreme_vertices; + LocalVector<LocalVector<int>> vertex_neighbors; void _setup(const Vector<Vector3> &p_vertices); diff --git a/servers/rendering/dummy/storage/material_storage.h b/servers/rendering/dummy/storage/material_storage.h index ed8fefc558..b4e0a21ee1 100644 --- a/servers/rendering/dummy/storage/material_storage.h +++ b/servers/rendering/dummy/storage/material_storage.h @@ -54,7 +54,7 @@ public: virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override { return 0; } virtual void global_shader_parameters_instance_free(RID p_instance) override {} - virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override {} + virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override {} /* SHADER API */ diff --git a/servers/rendering/dummy/storage/utilities.h b/servers/rendering/dummy/storage/utilities.h index cb7b2a2b63..a42ab0d814 100644 --- a/servers/rendering/dummy/storage/utilities.h +++ b/servers/rendering/dummy/storage/utilities.h @@ -109,6 +109,8 @@ public: virtual String get_video_adapter_vendor() const override { return String(); } virtual RenderingDevice::DeviceType get_video_adapter_type() const override { return RenderingDevice::DeviceType::DEVICE_TYPE_OTHER; } virtual String get_video_adapter_api_version() const override { return String(); } + + virtual Size2i get_maximum_viewport_size() const override { return Size2i(); }; }; } // namespace RendererDummy diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 53237c1dfb..a14228eb3d 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -357,8 +357,8 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE; } - copy.push_constant.section[0] = 0; - copy.push_constant.section[1] = 0; + copy.push_constant.section[0] = p_rect.position.x; + copy.push_constant.section[1] = p_rect.position.y; copy.push_constant.section[2] = p_rect.size.width; copy.push_constant.section[3] = p_rect.size.height; copy.push_constant.target[0] = p_rect.position.x; diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index 9b6be0b422..550fe27e4c 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -2335,7 +2335,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ img.instantiate(); for (uint32_t i = 0; i < cascade_size; i++) { Vector<uint8_t> subarr = data.slice(128 * 128 * i, 128 * 128 * (i + 1)); - img->create(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr); + img->set_data(cascade_size, cascade_size, false, Image::FORMAT_L8, subarr); img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png"); } @@ -2866,7 +2866,8 @@ void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID { Transform3D to_cell = gi->voxel_gi_get_to_cell_xform(probe); - Transform3D to_probe_xform = (transform * to_cell.affine_inverse()).affine_inverse(); + Transform3D to_probe_xform = to_cell * transform.affine_inverse(); + //update lights for (uint32_t i = 0; i < light_count; i++) { diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 4ff2a66975..6940276040 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -772,9 +772,7 @@ Ref<Image> SkyRD::Sky::bake_panorama(float p_energy, int p_roughness_layers, con Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rad_tex, 0); RD::get_singleton()->free(rad_tex); - Ref<Image> img; - img.instantiate(); - img->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data); + Ref<Image> img = Image::create_from_data(p_size.width, p_size.height, false, Image::FORMAT_RGBAF, data); for (int i = 0; i < p_size.width; i++) { for (int j = 0; j < p_size.height; j++) { Color c = img->get_pixel(i, j); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 59a8cdf30a..ceed287184 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -963,8 +963,8 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con distance = 1.0; } - uint32_t indices; - surf->sort.lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, &indices); + uint32_t indices = 0; + surf->sort.lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, indices); if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); if (p_render_list == RENDER_LIST_OPAQUE) { //opaque diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 2dfdb302dc..f76d016ae7 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1830,8 +1830,8 @@ void RenderForwardMobile::_fill_render_list(RenderListType p_render_list, const distance = 1.0; } - uint32_t indices; - surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, &indices); + uint32_t indices = 0; + surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, indices); if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); if (p_render_list == RENDER_LIST_OPAQUE) { //opaque diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 5728444312..dc0214e307 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1111,8 +1111,20 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - if (material.is_null() && ci->canvas_group != nullptr) { - material = default_canvas_group_material; + if (ci->canvas_group != nullptr) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + if (!p_to_backbuffer) { + material = default_clip_children_material; + } + } else { + if (material.is_null()) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } + } } if (material != prev_material) { @@ -1437,10 +1449,12 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); item_count = 0; - Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; - - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { + Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -1620,7 +1634,7 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); } - Vector3 cam_target = Basis(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); ShadowRenderPushConstant push_constant; @@ -2738,6 +2752,26 @@ void fragment() { material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); } + { + default_clip_children_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_clip_children_shader); + + material_storage->shader_set_code(default_clip_children_shader, R"( +// Default clip children shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + COLOR.rgb = c.rgb; +} +)"); + default_clip_children_material = material_storage->material_allocate(); + material_storage->material_initialize(default_clip_children_material); + + material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); + } + static_assert(sizeof(PushConstant) == 128); } @@ -2789,6 +2823,9 @@ RendererCanvasRenderRD::~RendererCanvasRenderRD() { material_storage->material_free(default_canvas_group_material); material_storage->shader_free(default_canvas_group_shader); + material_storage->material_free(default_clip_children_material); + material_storage->shader_free(default_clip_children_shader); + { if (state.canvas_state_buffer.is_valid()) { RD::get_singleton()->free(state.canvas_state_buffer); diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 67db56d913..4f1f77af5e 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -421,6 +421,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender { RID default_canvas_group_shader; RID default_canvas_group_material; + RID default_clip_children_material; + RID default_clip_children_shader; RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index e5e94ea1fb..75fe84f46b 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -250,9 +250,7 @@ Ref<Image> RendererSceneRenderRD::environment_bake_panorama(RID p_env, bool p_ba panorama_color = ambient_color.lerp(panorama_color, ambient_color_sky_mix); } - Ref<Image> panorama; - panorama.instantiate(); - panorama->create(p_size.width, p_size.height, false, Image::FORMAT_RGBAF); + Ref<Image> panorama = Image::create_empty(p_size.width, p_size.height, false, Image::FORMAT_RGBAF); panorama->fill(panorama_color); return panorama; } @@ -1305,36 +1303,28 @@ TypedArray<Image> RendererSceneRenderRD::bake_render_uv2(RID p_base, const Typed { PackedByteArray data = RD::get_singleton()->texture_get_data(albedo_alpha_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(albedo_alpha_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(normal_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(normal_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(orm_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBA8, data); RD::get_singleton()->free(orm_tex); ret.push_back(img); } { PackedByteArray data = RD::get_singleton()->texture_get_data(emission_tex, 0); - Ref<Image> img; - img.instantiate(); - img->create(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBAH, data); + Ref<Image> img = Image::create_from_data(p_image_size.width, p_image_size.height, false, Image::FORMAT_RGBAH, data); RD::get_singleton()->free(emission_tex); ret.push_back(img); } diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl index 4a18c7c052..8593e6b265 100644 --- a/servers/rendering/renderer_rd/shaders/canvas.glsl +++ b/servers/rendering/renderer_rd/shaders/canvas.glsl @@ -655,20 +655,7 @@ void main() { if (i >= light_count) { break; } - uint light_base; - if (i < 8) { - if (i < 4) { - light_base = draw_data.lights[0]; - } else { - light_base = draw_data.lights[1]; - } - } else { - if (i < 12) { - light_base = draw_data.lights[2]; - } else { - light_base = draw_data.lights[3]; - } - } + uint light_base = draw_data.lights[i >> 2]; light_base >>= (i & 3) * 8; light_base &= 0xFF; diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 1a8a1f3aa3..293532bf2d 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -1447,18 +1447,24 @@ void fragment_shader(in SceneData scene_data) { } //finalize ambient light here - ambient_light *= albedo.rgb; - ambient_light *= ao; + { +#if defined(AMBIENT_LIGHT_DISABLED) + ambient_light = vec3(0.0, 0.0, 0.0); +#else + ambient_light *= albedo.rgb; + ambient_light *= ao; + + if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) { + vec4 ssil = textureLod(sampler2D(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv, 0.0); + ambient_light *= 1.0 - ssil.a; + ambient_light += ssil.rgb * albedo.rgb; + } +#endif // AMBIENT_LIGHT_DISABLED + } // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); - if (bool(implementation_data.ss_effects_flags & SCREEN_SPACE_EFFECTS_FLAGS_USE_SSIL)) { - vec4 ssil = textureLod(sampler2D(ssil_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), screen_uv, 0.0); - ambient_light *= 1.0 - ssil.a; - ambient_light += ssil.rgb * albedo.rgb; - } - //this saves some VGPRs vec3 f0 = F0(metallic, specular, albedo); diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 33fd4c35b1..9aeaa6d978 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -1172,8 +1172,14 @@ void main() { } //Reflection probes // finalize ambient light here - ambient_light *= albedo.rgb; - ambient_light *= ao; + { +#if defined(AMBIENT_LIGHT_DISABLED) + ambient_light = vec3(0.0, 0.0, 0.0); +#else + ambient_light *= albedo.rgb; + ambient_light *= ao; +#endif // AMBIENT_LIGHT_DISABLED + } // convert ao to direct light ao ao = mix(1.0, ao, ao_light_affect); diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index f0b8d006cb..d8ef4baa41 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -85,7 +85,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = v & 1 ? 1 : 0; gui[1] = v & 2 ? 1 : 0; } @@ -112,7 +112,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy gui[j + 3] = 0; // ignored } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -141,7 +141,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy } } } else { - int v = value; + uint32_t v = value; gui[0] = (v & 1) ? 1 : 0; gui[1] = (v & 2) ? 1 : 0; gui[2] = (v & 4) ? 1 : 0; @@ -734,7 +734,7 @@ _FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, switch (type) { case ShaderLanguage::TYPE_BOOL: { uint32_t *gui = (uint32_t *)data; - *gui = value[0].boolean ? 1 : 0; + gui[0] = value[0].boolean ? 1 : 0; } break; case ShaderLanguage::TYPE_BVEC2: { uint32_t *gui = (uint32_t *)data; @@ -2213,7 +2213,7 @@ void MaterialStorage::global_shader_parameters_instance_free(RID p_instance) { global_shader_uniforms.instance_buffer_pos.erase(p_instance); } -void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) { +void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count) { if (!global_shader_uniforms.instance_buffer_pos.has(p_instance)) { return; //just not allocated, ignore } @@ -2223,7 +2223,9 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i return; //again, not allocated, ignore } ERR_FAIL_INDEX(p_index, ShaderLanguage::MAX_INSTANCE_UNIFORM_INDICES); - ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + + Variant::Type value_type = p_value.get_type(); + ERR_FAIL_COND_MSG(p_value.get_type() > Variant::COLOR, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported const ShaderLanguage::DataType datatype_from_value[Variant::COLOR + 1] = { ShaderLanguage::TYPE_MAX, //nil @@ -2249,9 +2251,23 @@ void MaterialStorage::global_shader_parameters_instance_update(RID p_instance, i ShaderLanguage::TYPE_VEC4 //color }; - ShaderLanguage::DataType datatype = datatype_from_value[p_value.get_type()]; - - ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(p_value.get_type())); //anything greater not supported + ShaderLanguage::DataType datatype = ShaderLanguage::TYPE_MAX; + if (value_type == Variant::INT && p_flags_count > 0) { + switch (p_flags_count) { + case 1: + datatype = ShaderLanguage::TYPE_BVEC2; + break; + case 2: + datatype = ShaderLanguage::TYPE_BVEC3; + break; + case 3: + datatype = ShaderLanguage::TYPE_BVEC4; + break; + } + } else { + datatype = datatype_from_value[value_type]; + } + ERR_FAIL_COND_MSG(datatype == ShaderLanguage::TYPE_MAX, "Unsupported variant type for instance parameter: " + Variant::get_type_name(value_type)); //anything greater not supported pos += p_index; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index 19c2f39553..fb25e56ed3 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -361,7 +361,7 @@ public: virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) override; virtual void global_shader_parameters_instance_free(RID p_instance) override; - virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) override; + virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) override; RID global_shader_uniforms_get_storage_buffer() const; diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index c8a33bd4d7..6a7400631d 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -398,13 +398,11 @@ public: return s->index_count ? s->index_count : s->vertex_count; } - _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const { + _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); int32_t current_lod = -1; - if (r_index_count) { - *r_index_count = s->index_count; - } + r_index_count = s->index_count; for (uint32_t i = 0; i < s->lod_count; i++) { float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold; if (screen_size > p_mesh_lod_threshold) { @@ -415,9 +413,7 @@ public: if (current_lod == -1) { return 0; } else { - if (r_index_count) { - *r_index_count = s->lods[current_lod].index_count; - } + r_index_count = s->lods[current_lod].index_count; return current_lod + 1; } } diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index d5285c07f4..bc70c57b69 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1124,9 +1124,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); texture_2d_initialize(p_texture, image); @@ -1135,9 +1133,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) { void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS::TextureLayeredType p_layered_type) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -1156,9 +1152,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RS void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) { //this could be better optimized to reuse an existing image , done this way //for now to get it working - Ref<Image> image; - image.instantiate(); - image->create(4, 4, false, Image::FORMAT_RGBA8); + Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); image->fill(Color(1, 0, 1, 1)); Vector<Ref<Image>> images; @@ -1181,9 +1175,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const { #endif Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, 0); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (tex->format != tex->validated_format) { image->convert(tex->format); @@ -1204,9 +1196,7 @@ Ref<Image> TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons Vector<uint8_t> data = RD::get_singleton()->texture_get_data(tex->rd_texture, p_layer); ERR_FAIL_COND_V(data.size() == 0, Ref<Image>()); - Ref<Image> image; - image.instantiate(); - image->create(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); + Ref<Image> image = Image::create_from_data(tex->width, tex->height, tex->mipmaps > 1, tex->validated_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref<Image>()); if (tex->format != tex->validated_format) { image->convert(tex->format); @@ -1232,9 +1222,7 @@ Vector<Ref<Image>> TextureStorage::texture_3d_get(RID p_texture) const { ERR_FAIL_COND_V(bs.offset + bs.buffer_size > (uint32_t)all_data.size(), Vector<Ref<Image>>()); Vector<uint8_t> sub_region = all_data.slice(bs.offset, bs.offset + bs.buffer_size); - Ref<Image> img; - img.instantiate(); - img->create(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); + Ref<Image> img = Image::create_from_data(bs.size.width, bs.size.height, false, tex->validated_format, sub_region); ERR_FAIL_COND_V(img->is_empty(), Vector<Ref<Image>>()); if (tex->format != tex->validated_format) { img->convert(tex->format); diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.cpp b/servers/rendering/renderer_rd/storage_rd/utilities.cpp index e517186955..4048c46d28 100644 --- a/servers/rendering/renderer_rd/storage_rd/utilities.cpp +++ b/servers/rendering/renderer_rd/storage_rd/utilities.cpp @@ -321,3 +321,11 @@ RenderingDevice::DeviceType Utilities::get_video_adapter_type() const { String Utilities::get_video_adapter_api_version() const { return RenderingDevice::get_singleton()->get_device_api_version(); } + +Size2i Utilities::get_maximum_viewport_size() const { + RenderingDevice *device = RenderingDevice::get_singleton(); + + int max_x = device->limit_get(RenderingDevice::LIMIT_MAX_VIEWPORT_DIMENSIONS_X); + int max_y = device->limit_get(RenderingDevice::LIMIT_MAX_VIEWPORT_DIMENSIONS_Y); + return Size2i(max_x, max_y); +} diff --git a/servers/rendering/renderer_rd/storage_rd/utilities.h b/servers/rendering/renderer_rd/storage_rd/utilities.h index a80eb8510e..dda656c380 100644 --- a/servers/rendering/renderer_rd/storage_rd/utilities.h +++ b/servers/rendering/renderer_rd/storage_rd/utilities.h @@ -115,6 +115,8 @@ public: virtual String get_video_adapter_vendor() const override; virtual RenderingDevice::DeviceType get_video_adapter_type() const override; virtual String get_video_adapter_api_version() const override; + + virtual Size2i get_maximum_viewport_size() const override; }; } // namespace RendererRD diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index d0cb46dee9..348f6e4695 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -1449,8 +1449,23 @@ void RendererSceneCull::instance_geometry_set_shader_parameter(RID p_instance, c } else { E->value.value = p_value; if (E->value.index >= 0 && instance->instance_allocated_shader_uniforms) { + int flags_count = 0; + if (E->value.info.hint == PROPERTY_HINT_FLAGS) { + // A small hack to detect boolean flags count and prevent overhead. + switch (E->value.info.hint_string.length()) { + case 3: // "x,y" + flags_count = 1; + break; + case 5: // "x,y,z" + flags_count = 2; + break; + case 7: // "x,y,z,w" + flags_count = 3; + break; + } + } //update directly - RSG::material_storage->global_shader_parameters_instance_update(p_instance, E->value.index, p_value); + RSG::material_storage->global_shader_parameters_instance_update(p_instance, E->value.index, p_value, flags_count); } } } @@ -3884,7 +3899,22 @@ void RendererSceneCull::_update_dirty_instance(Instance *p_instance) { for (const KeyValue<StringName, Instance::InstanceShaderParameter> &E : p_instance->instance_shader_uniforms) { if (E.value.value.get_type() != Variant::NIL) { - RSG::material_storage->global_shader_parameters_instance_update(p_instance->self, E.value.index, E.value.value); + int flags_count = 0; + if (E.value.info.hint == PROPERTY_HINT_FLAGS) { + // A small hack to detect boolean flags count and prevent overhead. + switch (E.value.info.hint_string.length()) { + case 3: // "x,y" + flags_count = 1; + break; + case 5: // "x,y,z" + flags_count = 2; + break; + case 7: // "x,y,z,w" + flags_count = 3; + break; + } + } + RSG::material_storage->global_shader_parameters_instance_update(p_instance->self, E.value.index, E.value.value, flags_count); } } } else { diff --git a/servers/rendering/renderer_scene_occlusion_cull.cpp b/servers/rendering/renderer_scene_occlusion_cull.cpp index bda0950097..e1ca5a7103 100644 --- a/servers/rendering/renderer_scene_occlusion_cull.cpp +++ b/servers/rendering/renderer_scene_occlusion_cull.cpp @@ -180,7 +180,7 @@ RID RendererSceneOcclusionCull::HZBuffer::get_debug_texture() { ptrw[i] = MIN(mips[0][i] / debug_tex_range, 1.0) * 255; } - debug_image->create(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); + debug_image->set_data(sizes[0].x, sizes[0].y, false, Image::FORMAT_L8, debug_data); if (debug_texture.is_null()) { debug_texture = RS::get_singleton()->texture_2d_create(debug_image); diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index dd190437a3..93118f39e7 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -974,6 +974,8 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X); BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y); BIND_ENUM_CONSTANT(LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z); + BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_X); + BIND_ENUM_CONSTANT(LIMIT_MAX_VIEWPORT_DIMENSIONS_Y); BIND_ENUM_CONSTANT(MEMORY_TEXTURES); BIND_ENUM_CONSTANT(MEMORY_BUFFERS); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index ca30a30786..d0b85d8220 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -1252,6 +1252,8 @@ public: LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_X, LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Y, LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z, + LIMIT_MAX_VIEWPORT_DIMENSIONS_X, + LIMIT_MAX_VIEWPORT_DIMENSIONS_Y, LIMIT_SUBGROUP_SIZE, LIMIT_SUBGROUP_IN_SHADERS, // Set flags using SHADER_STAGE_VERTEX_BIT, SHADER_STAGE_FRAGMENT_BIT, etc. LIMIT_SUBGROUP_OPERATIONS, diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 9103b0cf56..c0cd564308 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -331,6 +331,14 @@ bool RenderingServerDefault::is_low_end() const { return RendererCompositor::is_low_end(); } +Size2i RenderingServerDefault::get_maximum_viewport_size() const { + if (RSG::utilities) { + return RSG::utilities->get_maximum_viewport_size(); + } else { + return Size2i(); + } +} + void RenderingServerDefault::_thread_exit() { exit.set(); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index fe212151e3..a68c7ddf2c 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -994,6 +994,8 @@ public: virtual void set_print_gpu_profile(bool p_enable) override; + virtual Size2i get_maximum_viewport_size() const override; + RenderingServerDefault(bool p_create_thread = false); ~RenderingServerDefault(); }; diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 54cc7a011a..1606f537e8 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -373,16 +373,16 @@ void ShaderCompiler::_dump_function_deps(const SL::ShaderNode *p_node, const Str static String _get_global_shader_uniform_from_type_and_index(const String &p_buffer, const String &p_index, ShaderLanguage::DataType p_type) { switch (p_type) { case ShaderLanguage::TYPE_BOOL: { - return "(" + p_buffer + "[" + p_index + "].x != 0.0)"; + return "(floatBitsToUint(" + p_buffer + "[" + p_index + "].x) != 0)"; } case ShaderLanguage::TYPE_BVEC2: { - return "(notEqual(" + p_buffer + "[" + p_index + "].xy, vec2(0.0)))"; + return "bvec2(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y))"; } case ShaderLanguage::TYPE_BVEC3: { - return "(notEqual(" + p_buffer + "[" + p_index + "].xyz, vec3(0.0)))"; + return "bvec3(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y), floatBitsToUint(" + p_buffer + "[" + p_index + "].z))"; } case ShaderLanguage::TYPE_BVEC4: { - return "(notEqual(" + p_buffer + "[" + p_index + "].xyzw, vec4(0.0)))"; + return "bvec4(floatBitsToUint(" + p_buffer + "[" + p_index + "].x), floatBitsToUint(" + p_buffer + "[" + p_index + "].y), floatBitsToUint(" + p_buffer + "[" + p_index + "].z), floatBitsToUint(" + p_buffer + "[" + p_index + "].w))"; } case ShaderLanguage::TYPE_INT: { return "floatBitsToInt(" + p_buffer + "[" + p_index + "].x)"; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index a92292209f..4d27c400f0 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -8635,12 +8635,15 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f } break; case TK_HINT_SCREEN_TEXTURE: { new_hint = ShaderNode::Uniform::HINT_SCREEN_TEXTURE; + --texture_uniforms; } break; case TK_HINT_NORMAL_ROUGHNESS_TEXTURE: { new_hint = ShaderNode::Uniform::HINT_NORMAL_ROUGHNESS_TEXTURE; + --texture_uniforms; } break; case TK_HINT_DEPTH_TEXTURE: { new_hint = ShaderNode::Uniform::HINT_DEPTH_TEXTURE; + --texture_uniforms; } break; case TK_FILTER_NEAREST: { new_filter = FILTER_NEAREST; diff --git a/servers/rendering/storage/material_storage.h b/servers/rendering/storage/material_storage.h index 396668c9ed..a3de6343e5 100644 --- a/servers/rendering/storage/material_storage.h +++ b/servers/rendering/storage/material_storage.h @@ -53,7 +53,7 @@ public: virtual int32_t global_shader_parameters_instance_allocate(RID p_instance) = 0; virtual void global_shader_parameters_instance_free(RID p_instance) = 0; - virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value) = 0; + virtual void global_shader_parameters_instance_update(RID p_instance, int p_index, const Variant &p_value, int p_flags_count = 0) = 0; /* SHADER API */ virtual RID shader_allocate() = 0; diff --git a/servers/rendering/storage/utilities.h b/servers/rendering/storage/utilities.h index d240d58917..23a782c14a 100644 --- a/servers/rendering/storage/utilities.h +++ b/servers/rendering/storage/utilities.h @@ -181,6 +181,8 @@ public: virtual String get_video_adapter_vendor() const = 0; virtual RenderingDevice::DeviceType get_video_adapter_type() const = 0; virtual String get_video_adapter_api_version() const = 0; + + virtual Size2i get_maximum_viewport_size() const = 0; }; #endif // RENDERER_UTILITIES_H diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index cf13118451..e1a3fe46be 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2636,7 +2636,8 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX); BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_DISABLED); - BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_OPAQUE); + BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_ONLY); + BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_AND_DRAW); BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_TRANSPARENT); /* CANVAS LIGHT */ diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 6885d80301..36779690de 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1367,7 +1367,8 @@ public: enum CanvasGroupMode { CANVAS_GROUP_MODE_DISABLED, - CANVAS_GROUP_MODE_OPAQUE, + CANVAS_GROUP_MODE_CLIP_ONLY, + CANVAS_GROUP_MODE_CLIP_AND_DRAW, CANVAS_GROUP_MODE_TRANSPARENT, }; @@ -1574,6 +1575,8 @@ public: virtual void set_print_gpu_profile(bool p_enable) = 0; + virtual Size2i get_maximum_viewport_size() const = 0; + RenderingDevice *get_rendering_device() const; RenderingDevice *create_local_rendering_device() const; diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp index 430e2042d9..a5ee1d5726 100644 --- a/servers/xr/xr_interface.cpp +++ b/servers/xr/xr_interface.cpp @@ -222,12 +222,7 @@ RID XRInterface::get_vrs_texture() { data_ptr[d++] = density; } } - - Ref<Image> image; - image.instantiate(); - image->create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data); - - images.push_back(image); + images.push_back(Image::create_from_data(vrs_sizei.x, vrs_sizei.y, false, Image::FORMAT_R8, data)); } if (images.size() == 1) { diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 38b616cda0..181d9a8a54 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -124,7 +124,7 @@ TEST_CASE("[Image] Saving and loading") { image_jpg->load_jpg_from_buffer(data_jpg) == OK, "The JPG image should load successfully."); - // Load WEBP + // Load WebP Ref<Image> image_webp = memnew(Image()); Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); PackedByteArray data_webp; @@ -132,7 +132,7 @@ TEST_CASE("[Image] Saving and loading") { f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length()); CHECK_MESSAGE( image_webp->load_webp_from_buffer(data_webp) == OK, - "The WEBP image should load successfully."); + "The WebP image should load successfully."); // Load PNG Ref<Image> image_png = memnew(Image()); diff --git a/tests/core/math/test_basis.h b/tests/core/math/test_basis.h index a65020597a..90ca9e439f 100644 --- a/tests/core/math/test_basis.h +++ b/tests/core/math/test_basis.h @@ -170,8 +170,7 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) { // Double check `to_rotation` decomposing with XYZ rotation order. const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ); - Basis rotation_from_xyz_computed_euler; - rotation_from_xyz_computed_euler.set_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ); + Basis rotation_from_xyz_computed_euler = Basis::from_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ); res = to_rotation.inverse() * rotation_from_xyz_computed_euler; diff --git a/tests/core/math/test_quaternion.h b/tests/core/math/test_quaternion.h index d1912cbf42..eb5fea1d2d 100644 --- a/tests/core/math/test_quaternion.h +++ b/tests/core/math/test_quaternion.h @@ -192,7 +192,7 @@ TEST_CASE("[Quaternion] Construct Basis Euler") { double roll = Math::deg_to_rad(10.0); Vector3 euler_yxz(pitch, yaw, roll); Quaternion q_yxz(euler_yxz); - Basis basis_axes(euler_yxz); + Basis basis_axes = Basis::from_euler(euler_yxz); Quaternion q(basis_axes); CHECK(q.is_equal_approx(q_yxz)); } @@ -218,7 +218,7 @@ TEST_CASE("[Quaternion] Construct Basis Axes") { // This is by design, but may be subject to change. // Workaround by constructing Basis from Euler angles. // basis_axes = Basis(i_unit, j_unit, k_unit); - Basis basis_axes(euler_yxz); + Basis basis_axes = Basis::from_euler(euler_yxz); Quaternion q(basis_axes); CHECK(basis_axes.get_column(0).is_equal_approx(i_unit)); @@ -334,7 +334,7 @@ TEST_CASE("[Quaternion] xform unit vectors") { TEST_CASE("[Quaternion] xform vector") { // Arbitrary quaternion rotates an arbitrary vector. Vector3 euler_yzx(Math::deg_to_rad(31.41), Math::deg_to_rad(-49.16), Math::deg_to_rad(12.34)); - Basis basis_axes(euler_yzx); + Basis basis_axes = Basis::from_euler(euler_yzx); Quaternion q(basis_axes); Vector3 v_arb(3.0, 4.0, 5.0); @@ -347,7 +347,7 @@ TEST_CASE("[Quaternion] xform vector") { // Test vector xform for a single combination of Quaternion and Vector. void test_quat_vec_rotate(Vector3 euler_yzx, Vector3 v_in) { - Basis basis_axes(euler_yzx); + Basis basis_axes = Basis::from_euler(euler_yzx); Quaternion q(basis_axes); Vector3 v_rot = q.xform(v_in); diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h index cf34caac28..cd1b421ce8 100644 --- a/tests/core/string/test_string.h +++ b/tests/core/string/test_string.h @@ -411,9 +411,13 @@ TEST_CASE("[String] Number to string") { CHECK(String::num_real(3.141593) == "3.141593"); CHECK(String::num_real(3.141) == "3.141"); // No trailing zeros. #ifdef REAL_T_IS_DOUBLE + CHECK_MESSAGE(String::num_real(123.456789) == "123.456789", "Prints the appropriate amount of digits for real_t = double."); + CHECK_MESSAGE(String::num_real(-123.456789) == "-123.456789", "Prints the appropriate amount of digits for real_t = double."); CHECK_MESSAGE(String::num_real(Math_PI) == "3.14159265358979", "Prints the appropriate amount of digits for real_t = double."); CHECK_MESSAGE(String::num_real(3.1415f) == "3.1414999961853", "Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero."); #else + CHECK_MESSAGE(String::num_real(123.456789) == "123.4568", "Prints the appropriate amount of digits for real_t = float."); + CHECK_MESSAGE(String::num_real(-123.456789) == "-123.4568", "Prints the appropriate amount of digits for real_t = float."); CHECK_MESSAGE(String::num_real(Math_PI) == "3.141593", "Prints the appropriate amount of digits for real_t = float."); CHECK_MESSAGE(String::num_real(3.1415f) == "3.1415", "Prints only reliable digits of 32-bit float when real_t = float."); #endif // REAL_T_IS_DOUBLE diff --git a/tests/scene/test_bit_map.h b/tests/scene/test_bit_map.h index 635449181e..a102f40725 100644 --- a/tests/scene/test_bit_map.h +++ b/tests/scene/test_bit_map.h @@ -76,15 +76,11 @@ TEST_CASE("[BitMap] Create bit map from image alpha") { bit_map.create_from_image_alpha(empty_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because bitmap creation from an empty image should fail."); - Ref<Image> wrong_format_img; - wrong_format_img.instantiate(); - wrong_format_img->create(3, 3, false, Image::Format::FORMAT_DXT1); + Ref<Image> wrong_format_img = Image::create_empty(3, 3, false, Image::Format::FORMAT_DXT1); bit_map.create_from_image_alpha(wrong_format_img); CHECK_MESSAGE(bit_map.get_size() == Size2i(256, 256), "Bitmap should have its old values because converting from a compressed image should fail."); - Ref<Image> img; - img.instantiate(); - img->create(3, 3, false, Image::Format::FORMAT_RGBA8); + Ref<Image> img = Image::create_empty(3, 3, false, Image::Format::FORMAT_RGBA8); img->set_pixel(0, 0, Color(0, 0, 0, 0)); img->set_pixel(0, 1, Color(0, 0, 0, 0.09f)); img->set_pixel(0, 2, Color(0, 0, 0, 0.25f)); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 3a20ac123b..4b92b9daff 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -733,6 +733,46 @@ TEST_CASE("[SceneTree][TextEdit] text entry") { SIGNAL_CHECK_FALSE("caret_changed"); } + SUBCASE("[TextEdit] add selection for next occurrence") { + text_edit->set_text("\ntest other_test\nrandom test\nword test word"); + text_edit->set_caret_column(0); + text_edit->set_caret_line(1); + + text_edit->select_word_under_caret(); + CHECK(text_edit->has_selection(0)); + CHECK(text_edit->get_selected_text(0) == "test"); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 2); + CHECK(text_edit->get_selected_text(1) == "test"); + CHECK(text_edit->get_selection_from_line(1) == 1); + CHECK(text_edit->get_selection_from_column(1) == 13); + CHECK(text_edit->get_selection_to_line(1) == 1); + CHECK(text_edit->get_selection_to_column(1) == 17); + CHECK(text_edit->get_caret_line(1) == 1); + CHECK(text_edit->get_caret_column(1) == 17); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 3); + CHECK(text_edit->get_selected_text(2) == "test"); + CHECK(text_edit->get_selection_from_line(2) == 2); + CHECK(text_edit->get_selection_from_column(2) == 9); + CHECK(text_edit->get_selection_to_line(2) == 2); + CHECK(text_edit->get_selection_to_column(2) == 13); + CHECK(text_edit->get_caret_line(2) == 2); + CHECK(text_edit->get_caret_column(2) == 13); + + text_edit->add_selection_for_next_occurrence(); + CHECK(text_edit->get_caret_count() == 4); + CHECK(text_edit->get_selected_text(3) == "test"); + CHECK(text_edit->get_selection_from_line(3) == 3); + CHECK(text_edit->get_selection_from_column(3) == 5); + CHECK(text_edit->get_selection_to_line(3) == 3); + CHECK(text_edit->get_selection_to_column(3) == 9); + CHECK(text_edit->get_caret_line(3) == 3); + CHECK(text_edit->get_caret_column(3) == 9); + } + SUBCASE("[TextEdit] deselect on focus loss") { text_edit->set_text("test"); @@ -3918,6 +3958,32 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { CHECK(text_edit->get_last_full_visible_line_wrap_index() == 0); CHECK(text_edit->get_caret_wrap_index() == 0); + // Typing and undo / redo should adjust viewport + text_edit->set_caret_line(0); + text_edit->set_caret_column(0); + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + SEND_GUI_KEY_EVENT(text_edit, Key::A); + CHECK(text_edit->get_first_visible_line() == 0); + + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + text_edit->undo(); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 0); + + text_edit->set_line_as_first_visible(5); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 5); + + text_edit->redo(); + MessageQueue::get_singleton()->flush(); + CHECK(text_edit->get_first_visible_line() == 0); + memdelete(text_edit); } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 4c861eacba..d30d4a9e70 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -199,7 +199,7 @@ struct GodotTestCaseListener : public doctest::IReporter { OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { if (String("headless") == DisplayServer::get_create_function_name(i)) { - DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, Vector2i(0, 0), err); + DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), err); break; } } diff --git a/thirdparty/glad/KHR/khrplatform.h b/thirdparty/glad/KHR/khrplatform.h index dd22d92701..01646449ca 100644 --- a/thirdparty/glad/KHR/khrplatform.h +++ b/thirdparty/glad/KHR/khrplatform.h @@ -153,6 +153,20 @@ typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif #elif defined(__VMS ) || defined(__sgi) @@ -235,14 +249,21 @@ typedef unsigned short int khronos_uint16_t; * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ -#ifdef _WIN64 +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; -typedef signed long long int khronos_ssize_t; -typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif diff --git a/thirdparty/glad/glad.c b/thirdparty/glad/glad.c index dc1b8cb697..4b20178ef7 100644 --- a/thirdparty/glad/glad.c +++ b/thirdparty/glad/glad.c @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020. + OpenGL loader generated by glad 0.1.36 on Sun Sep 4 15:50:32 2022. Language/Generator: C/C++ Specification: gl @@ -11,16 +11,18 @@ GL_ARB_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, - GL_EXT_framebuffer_object + GL_EXT_framebuffer_object, + GL_OVR_multiview, + GL_OVR_multiview2 Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object" + --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2 */ #include <stdio.h> @@ -1000,6 +1002,8 @@ int GLAD_GL_ARB_framebuffer_object = 0; int GLAD_GL_EXT_framebuffer_blit = 0; int GLAD_GL_EXT_framebuffer_multisample = 0; int GLAD_GL_EXT_framebuffer_object = 0; +int GLAD_GL_OVR_multiview = 0; +int GLAD_GL_OVR_multiview2 = 0; PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL; PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL; PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL; @@ -1023,6 +1027,7 @@ PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT = NULL; PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT = NULL; PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT = NULL; +PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR = NULL; static void load_GL_VERSION_1_0(GLADloadproc load) { if(!GLAD_GL_VERSION_1_0) return; glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); @@ -1844,6 +1849,10 @@ static void load_GL_EXT_framebuffer_object(GLADloadproc load) { glad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load("glGetFramebufferAttachmentParameterivEXT"); glad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)load("glGenerateMipmapEXT"); } +static void load_GL_OVR_multiview(GLADloadproc load) { + if(!GLAD_GL_OVR_multiview) return; + glad_glFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)load("glFramebufferTextureMultiviewOVR"); +} static int find_extensionsGL(void) { if (!get_exts()) return 0; GLAD_GL_ARB_debug_output = has_ext("GL_ARB_debug_output"); @@ -1851,6 +1860,8 @@ static int find_extensionsGL(void) { GLAD_GL_EXT_framebuffer_blit = has_ext("GL_EXT_framebuffer_blit"); GLAD_GL_EXT_framebuffer_multisample = has_ext("GL_EXT_framebuffer_multisample"); GLAD_GL_EXT_framebuffer_object = has_ext("GL_EXT_framebuffer_object"); + GLAD_GL_OVR_multiview = has_ext("GL_OVR_multiview"); + GLAD_GL_OVR_multiview2 = has_ext("GL_OVR_multiview2"); free_exts(); return 1; } @@ -1934,6 +1945,7 @@ int gladLoadGLLoader(GLADloadproc load) { load_GL_EXT_framebuffer_blit(load); load_GL_EXT_framebuffer_multisample(load); load_GL_EXT_framebuffer_object(load); + load_GL_OVR_multiview(load); return GLVersion.major != 0 || GLVersion.minor != 0; } diff --git a/thirdparty/glad/glad/glad.h b/thirdparty/glad/glad/glad.h index f211e6aa57..37d12e4ee2 100644 --- a/thirdparty/glad/glad/glad.h +++ b/thirdparty/glad/glad/glad.h @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.34 on Tue Nov 17 16:41:02 2020. + OpenGL loader generated by glad 0.1.36 on Sun Sep 4 15:50:32 2022. Language/Generator: C/C++ Specification: gl @@ -11,16 +11,18 @@ GL_ARB_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, - GL_EXT_framebuffer_object + GL_EXT_framebuffer_object, + GL_OVR_multiview, + GL_OVR_multiview2 Loader: True Local files: False Omit khrplatform: False Reproducible: False Commandline: - --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object" + --profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions="GL_ARB_debug_output,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_OVR_multiview,GL_OVR_multiview2" Online: - https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3&extensions=GL_ARB_debug_output&extensions=GL_ARB_framebuffer_object&extensions=GL_EXT_framebuffer_blit&extensions=GL_EXT_framebuffer_multisample&extensions=GL_EXT_framebuffer_object&extensions=GL_OVR_multiview&extensions=GL_OVR_multiview2 */ @@ -3687,6 +3689,10 @@ GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv; #define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 #define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 #define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 #ifndef GL_ARB_debug_output #define GL_ARB_debug_output 1 GLAPI int GLAD_GL_ARB_debug_output; @@ -3776,6 +3782,17 @@ typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum target); GLAPI PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT; #define glGenerateMipmapEXT glad_glGenerateMipmapEXT #endif +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +GLAPI int GLAD_GL_OVR_multiview; +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +GLAPI PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glad_glFramebufferTextureMultiviewOVR; +#define glFramebufferTextureMultiviewOVR glad_glFramebufferTextureMultiviewOVR +#endif +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +GLAPI int GLAD_GL_OVR_multiview2; +#endif #ifdef __cplusplus } |