summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/project_settings.cpp19
-rw-r--r--core/core_bind.cpp4
-rw-r--r--core/core_bind.h8
-rw-r--r--core/extension/gdnative_interface.h12
-rw-r--r--core/extension/native_extension.cpp2
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/input/input_event.cpp15
-rw-r--r--core/input/input_event.h4
-rw-r--r--core/input/input_map.cpp29
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/io/image.cpp57
-rw-r--r--core/io/image.h18
-rw-r--r--core/io/resource_saver.cpp33
-rw-r--r--core/io/resource_saver.h2
-rw-r--r--core/io/zip_io.cpp12
-rw-r--r--core/math/convex_hull.cpp54
-rw-r--r--core/math/convex_hull.h6
-rw-r--r--core/math/geometry_3d.cpp50
-rw-r--r--core/math/geometry_3d.h12
-rw-r--r--core/math/quick_hull.cpp19
-rw-r--r--core/math/random_number_generator.h2
-rw-r--r--core/object/class_db.cpp5
-rw-r--r--core/object/method_bind.cpp4
-rw-r--r--core/object/object.cpp44
-rw-r--r--core/object/object.h4
-rw-r--r--core/os/midi_driver.cpp5
-rw-r--r--core/string/ustring.cpp70
-rw-r--r--core/variant/array.cpp6
-rw-r--r--core/variant/array.h1
-rw-r--r--core/variant/container_type_validate.h15
-rw-r--r--core/variant/variant_call.cpp5
-rw-r--r--core/variant/variant_internal.h2
-rw-r--r--core/variant/variant_op.cpp6
33 files changed, 376 insertions, 153 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/core_bind.cpp b/core/core_bind.cpp
index 5d3afdac90..87b36f7a21 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -605,8 +605,8 @@ void OS::_bind_methods() {
ADD_PROPERTY_DEFAULT("low_processor_usage_mode", false);
ADD_PROPERTY_DEFAULT("low_processor_usage_mode_sleep_usec", 6900);
- BIND_ENUM_CONSTANT(VIDEO_DRIVER_VULKAN);
- BIND_ENUM_CONSTANT(VIDEO_DRIVER_OPENGL_3);
+ BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
+ BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
BIND_ENUM_CONSTANT(DAY_SUNDAY);
BIND_ENUM_CONSTANT(DAY_MONDAY);
diff --git a/core/core_bind.h b/core/core_bind.h
index c44785255b..784f3e63b1 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -124,9 +124,9 @@ protected:
static OS *singleton;
public:
- enum VideoDriver {
- VIDEO_DRIVER_VULKAN,
- VIDEO_DRIVER_OPENGL_3,
+ enum RenderingDriver {
+ RENDERING_DRIVER_VULKAN,
+ RENDERING_DRIVER_OPENGL3,
};
enum Weekday {
@@ -578,7 +578,7 @@ VARIANT_ENUM_CAST(core_bind::ResourceLoader::CacheMode);
VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags);
-VARIANT_ENUM_CAST(core_bind::OS::VideoDriver);
+VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver);
VARIANT_ENUM_CAST(core_bind::OS::Weekday);
VARIANT_ENUM_CAST(core_bind::OS::Month);
VARIANT_ENUM_CAST(core_bind::OS::SystemDir);
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
index 9ac5cc9398..1ce50bc186 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -201,18 +201,18 @@ typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_
typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance);
typedef struct {
- uint32_t type;
+ GDNativeVariantType type;
const char *name;
const char *class_name;
- uint32_t hint;
+ uint32_t hint; // Bitfield of `PropertyHint` (defined in `extension_api.json`)
const char *hint_string;
- uint32_t usage;
+ uint32_t usage; // Bitfield of `PropertyUsageFlags` (defined in `extension_api.json`)
} GDNativePropertyInfo;
typedef struct {
const char *name;
GDNativePropertyInfo return_value;
- uint32_t flags; // From GDNativeExtensionClassMethodFlags
+ uint32_t flags; // Bitfield of `GDNativeExtensionClassMethodFlags`
int32_t id;
GDNativePropertyInfo *arguments;
uint32_t argument_count;
@@ -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;
@@ -292,7 +294,7 @@ typedef struct {
void *method_userdata;
GDNativeExtensionClassMethodCall call_func;
GDNativeExtensionClassMethodPtrCall ptrcall_func;
- uint32_t method_flags; /* GDNativeExtensionClassMethodFlags */
+ uint32_t method_flags; // Bitfield of `GDNativeExtensionClassMethodFlags`
uint32_t argument_count;
GDNativeBool has_return_value;
GDNativeExtensionClassMethodGetArgumentType get_argument_type_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..ea75a6b079 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -331,12 +331,17 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_text_caret_document_start.macos", TTRC("Caret Document Start") },
{ "ui_text_caret_document_end", TTRC("Caret Document End") },
{ "ui_text_caret_document_end.macos", TTRC("Caret Document End") },
+ { "ui_text_caret_add_below", TTRC("Caret Add Below") },
+ { "ui_text_caret_add_below.macos", TTRC("Caret Add Below") },
+ { "ui_text_caret_add_above", TTRC("Caret Add Above") },
+ { "ui_text_caret_add_above.macos", TTRC("Caret Add Above") },
{ "ui_text_scroll_up", TTRC("Scroll Up") },
{ "ui_text_scroll_up.macos", TTRC("Scroll Up") },
{ "ui_text_scroll_down", TTRC("Scroll Down") },
{ "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") },
@@ -616,6 +621,24 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL));
default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs);
+ // Text Caret Addition Below/Above
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_below", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_below.macos", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_above", inputs);
+
+ inputs = List<Ref<InputEvent>>();
+ inputs.push_back(InputEventKey::create_reference(Key::O | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL));
+ default_builtin_cache.insert("ui_text_caret_add_above.macos", inputs);
+
// Text Scrolling
inputs = List<Ref<InputEvent>>();
@@ -641,10 +664,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/dir_access.cpp b/core/io/dir_access.cpp
index 79e7fa16e3..7eb50d2261 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -351,7 +351,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
const size_t copy_buffer_limit = 65536; // 64 KB
fsrc->seek_end(0);
- int size = fsrc->get_position();
+ uint64_t size = fsrc->get_position();
fsrc->seek(0);
err = OK;
size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
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/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 996f4f4d67..561970d2ee 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -62,6 +62,7 @@ subject to the following restrictions:
#include "core/math/aabb.h"
#include "core/math/math_defs.h"
#include "core/os/memory.h"
+#include "core/templates/oa_hash_map.h"
#include "core/templates/paged_allocator.h"
#include <string.h>
@@ -2252,19 +2253,62 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
r_mesh.vertices = ch.vertices;
+ // Tag which face each edge belongs to
+ LocalVector<int32_t> edge_faces;
+ edge_faces.resize(ch.edges.size());
+
+ for (uint32_t i = 0; i < ch.edges.size(); i++) {
+ edge_faces[i] = -1;
+ }
+
+ for (uint32_t i = 0; i < ch.faces.size(); i++) {
+ const Edge *e_start = &ch.edges[ch.faces[i]];
+ const Edge *e = e_start;
+ do {
+ int64_t ofs = e - ch.edges.ptr();
+ edge_faces[ofs] = i;
+
+ e = e->get_next_edge_of_face();
+ } while (e != e_start);
+ }
+
// Copy the edges over. There's two "half-edges" for every edge, so we pick only one of them.
r_mesh.edges.resize(ch.edges.size() / 2);
+ OAHashMap<uint64_t, int32_t> edge_map;
+ edge_map.reserve(ch.edges.size() * 4); // The higher the capacity, the faster the insert
+
uint32_t edges_copied = 0;
for (uint32_t i = 0; i < ch.edges.size(); i++) {
+ ERR_CONTINUE(edge_faces[i] == -1); // Sanity check
+
uint32_t a = (&ch.edges[i])->get_source_vertex();
uint32_t b = (&ch.edges[i])->get_target_vertex();
if (a < b) { // Copy only the "canonical" edge. For the reverse edge, this will be false.
ERR_BREAK(edges_copied >= (uint32_t)r_mesh.edges.size());
- r_mesh.edges.write[edges_copied].a = a;
- r_mesh.edges.write[edges_copied].b = b;
+ r_mesh.edges[edges_copied].vertex_a = a;
+ r_mesh.edges[edges_copied].vertex_b = b;
+ r_mesh.edges[edges_copied].face_a = edge_faces[i];
+ r_mesh.edges[edges_copied].face_b = -1;
+
+ uint64_t key = a;
+ key <<= 32;
+ key |= b;
+ edge_map.insert(key, edges_copied);
+
edges_copied++;
+ } else {
+ uint64_t key = b;
+ key <<= 32;
+ key |= a;
+ int32_t index;
+ if (!edge_map.lookup(key, index)) {
+ ERR_PRINT("Invalid edge");
+ } else {
+ r_mesh.edges[index].face_b = edge_faces[i];
+ }
}
}
+
if (edges_copied != (uint32_t)r_mesh.edges.size()) {
ERR_PRINT("Invalid edge count.");
}
@@ -2273,7 +2317,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
for (uint32_t i = 0; i < ch.faces.size(); i++) {
const Edge *e_start = &ch.edges[ch.faces[i]];
const Edge *e = e_start;
- Geometry3D::MeshData::Face &face = r_mesh.faces.write[i];
+ Geometry3D::MeshData::Face &face = r_mesh.faces[i];
do {
face.indices.push_back(e->get_target_vertex());
@@ -2284,8 +2328,8 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
// reverse indices: Godot wants clockwise, but this is counter-clockwise
if (face.indices.size() > 2) {
// reverse all but the first index.
- int *indices = face.indices.ptrw();
- for (int c = 0; c < (face.indices.size() - 1) / 2; c++) {
+ int *indices = face.indices.ptr();
+ for (uint32_t c = 0; c < (face.indices.size() - 1) / 2; c++) {
SWAP(indices[c + 1], indices[face.indices.size() - 1 - c]);
}
}
diff --git a/core/math/convex_hull.h b/core/math/convex_hull.h
index cc41a794bd..ab6671a7d0 100644
--- a/core/math/convex_hull.h
+++ b/core/math/convex_hull.h
@@ -62,6 +62,10 @@ public:
friend class ConvexHullComputer;
public:
+ int32_t get_next_relative() const {
+ return next;
+ }
+
int32_t get_source_vertex() const {
return (this + reverse)->target_vertex;
}
@@ -86,7 +90,7 @@ public:
};
// Vertices of the output hull
- Vector<Vector3> vertices;
+ LocalVector<Vector3> vertices;
// Edges of the output hull
LocalVector<Edge> edges;
diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp
index c5871358ed..548b9e4620 100644
--- a/core/math/geometry_3d.cpp
+++ b/core/math/geometry_3d.cpp
@@ -141,21 +141,21 @@ real_t Geometry3D::get_closest_distance_between_segments(const Vector3 &p_p0, co
void Geometry3D::MeshData::optimize_vertices() {
HashMap<int, int> vtx_remap;
- for (int i = 0; i < faces.size(); i++) {
- for (int j = 0; j < faces[i].indices.size(); j++) {
+ for (uint32_t i = 0; i < faces.size(); i++) {
+ for (uint32_t j = 0; j < faces[i].indices.size(); j++) {
int idx = faces[i].indices[j];
if (!vtx_remap.has(idx)) {
int ni = vtx_remap.size();
vtx_remap[idx] = ni;
}
- faces.write[i].indices.write[j] = vtx_remap[idx];
+ faces[i].indices[j] = vtx_remap[idx];
}
}
- for (int i = 0; i < edges.size(); i++) {
- int a = edges[i].a;
- int b = edges[i].b;
+ for (uint32_t i = 0; i < edges.size(); i++) {
+ int a = edges[i].vertex_a;
+ int b = edges[i].vertex_b;
if (!vtx_remap.has(a)) {
int ni = vtx_remap.size();
@@ -166,16 +166,16 @@ void Geometry3D::MeshData::optimize_vertices() {
vtx_remap[b] = ni;
}
- edges.write[i].a = vtx_remap[a];
- edges.write[i].b = vtx_remap[b];
+ edges[i].vertex_a = vtx_remap[a];
+ edges[i].vertex_b = vtx_remap[b];
}
- Vector<Vector3> new_vertices;
+ LocalVector<Vector3> new_vertices;
new_vertices.resize(vtx_remap.size());
- for (int i = 0; i < vertices.size(); i++) {
+ for (uint32_t i = 0; i < vertices.size(); i++) {
if (vtx_remap.has(i)) {
- new_vertices.write[vtx_remap[i]] = vertices[i];
+ new_vertices[vtx_remap[i]] = vertices[i];
}
}
vertices = new_vertices;
@@ -751,7 +751,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
Vector3 center = p.center();
// make a quad clockwise
- Vector<Vector3> vertices = {
+ LocalVector<Vector3> vertices = {
center - up * subplane_size + right * subplane_size,
center - up * subplane_size - right * subplane_size,
center + up * subplane_size - right * subplane_size,
@@ -763,7 +763,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
continue;
}
- Vector<Vector3> new_vertices;
+ LocalVector<Vector3> new_vertices;
Plane clip = p_planes[j];
if (clip.normal.dot(p.normal) > 0.95f) {
@@ -774,7 +774,7 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
break;
}
- for (int k = 0; k < vertices.size(); k++) {
+ for (uint32_t k = 0; k < vertices.size(); k++) {
int k_n = (k + 1) % vertices.size();
Vector3 edge0_A = vertices[k];
@@ -816,9 +816,9 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
MeshData::Face face;
// Add face indices.
- for (int j = 0; j < vertices.size(); j++) {
+ for (uint32_t j = 0; j < vertices.size(); j++) {
int idx = -1;
- for (int k = 0; k < mesh.vertices.size(); k++) {
+ for (uint32_t k = 0; k < mesh.vertices.size(); k++) {
if (mesh.vertices[k].distance_to(vertices[j]) < 0.001f) {
idx = k;
break;
@@ -837,28 +837,34 @@ Geometry3D::MeshData Geometry3D::build_convex_mesh(const Vector<Plane> &p_planes
// Add edge.
- for (int j = 0; j < face.indices.size(); j++) {
+ for (uint32_t j = 0; j < face.indices.size(); j++) {
int a = face.indices[j];
int b = face.indices[(j + 1) % face.indices.size()];
bool found = false;
- for (int k = 0; k < mesh.edges.size(); k++) {
- if (mesh.edges[k].a == a && mesh.edges[k].b == b) {
+ int found_idx = -1;
+ for (uint32_t k = 0; k < mesh.edges.size(); k++) {
+ if (mesh.edges[k].vertex_a == a && mesh.edges[k].vertex_b == b) {
found = true;
+ found_idx = k;
break;
}
- if (mesh.edges[k].b == a && mesh.edges[k].a == b) {
+ if (mesh.edges[k].vertex_b == a && mesh.edges[k].vertex_a == b) {
found = true;
+ found_idx = k;
break;
}
}
if (found) {
+ mesh.edges[found_idx].face_b = j;
continue;
}
MeshData::Edge edge;
- edge.a = a;
- edge.b = b;
+ edge.vertex_a = a;
+ edge.vertex_b = b;
+ edge.face_a = j;
+ edge.face_b = -1;
mesh.edges.push_back(edge);
}
}
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index e5ace9db72..4b4c173a1e 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -33,6 +33,7 @@
#include "core/math/face3.h"
#include "core/object/object.h"
+#include "core/templates/local_vector.h"
#include "core/templates/vector.h"
class Geometry3D {
@@ -539,18 +540,19 @@ public:
struct MeshData {
struct Face {
Plane plane;
- Vector<int> indices;
+ LocalVector<int> indices;
};
- Vector<Face> faces;
+ LocalVector<Face> faces;
struct Edge {
- int a, b;
+ int vertex_a, vertex_b;
+ int face_a, face_b;
};
- Vector<Edge> edges;
+ LocalVector<Edge> edges;
- Vector<Vector3> vertices;
+ LocalVector<Vector3> vertices;
void optimize_vertices();
};
diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp
index c7727a44a1..c194e1cc21 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -369,7 +369,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) {
Geometry3D::MeshData::Face &f = E->get();
- for (int i = 0; i < f.indices.size(); i++) {
+ for (uint32_t i = 0; i < f.indices.size(); i++) {
int a = E->get().indices[i];
int b = E->get().indices[(i + 1) % f.indices.size()];
Edge e(a, b);
@@ -436,17 +436,24 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_
r_mesh.faces.clear();
r_mesh.faces.resize(ret_faces.size());
+ HashMap<List<Geometry3D::MeshData::Face>::Element *, int> face_indices;
+
int idx = 0;
- for (const Geometry3D::MeshData::Face &E : ret_faces) {
- r_mesh.faces.write[idx++] = E;
+ for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) {
+ face_indices[E] = idx;
+ r_mesh.faces[idx++] = E->get();
}
r_mesh.edges.resize(ret_edges.size());
idx = 0;
for (const KeyValue<Edge, RetFaceConnect> &E : ret_edges) {
Geometry3D::MeshData::Edge e;
- e.a = E.key.vertices[0];
- e.b = E.key.vertices[1];
- r_mesh.edges.write[idx++] = e;
+ e.vertex_a = E.key.vertices[0];
+ e.vertex_b = E.key.vertices[1];
+ ERR_CONTINUE(!face_indices.has(E.value.left));
+ ERR_CONTINUE(!face_indices.has(E.value.right));
+ e.face_a = face_indices[E.value.left];
+ e.face_b = face_indices[E.value.right];
+ r_mesh.edges[idx++] = e;
}
r_mesh.vertices = p_points;
diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h
index 9352bae0a6..bf67154cd6 100644
--- a/core/math/random_number_generator.h
+++ b/core/math/random_number_generator.h
@@ -57,7 +57,7 @@ public:
_FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); }
_FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); }
- RandomNumberGenerator() {}
+ RandomNumberGenerator() { randbase.randomize(); }
};
#endif // RANDOM_NUMBER_GENERATOR_H
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/method_bind.cpp b/core/object/method_bind.cpp
index 0edc7a90c2..5381c596ce 100644
--- a/core/object/method_bind.cpp
+++ b/core/object/method_bind.cpp
@@ -63,7 +63,9 @@ PropertyInfo MethodBind::get_argument_info(int p_argument) const {
PropertyInfo info = _gen_argument_type_info(p_argument);
#ifdef DEBUG_METHODS_ENABLED
- info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("_unnamed_arg" + itos(p_argument));
+ if (info.name.is_empty()) {
+ info.name = p_argument < arg_names.size() ? String(arg_names[p_argument]) : String("_unnamed_arg" + itos(p_argument));
+ }
#endif
return info;
}
diff --git a/core/object/object.cpp b/core/object/object.cpp
index c275164b14..540b9a8f19 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -1468,8 +1468,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class);
ClassDB::bind_method(D_METHOD("set", "property", "value"), &Object::_set_bind);
ClassDB::bind_method(D_METHOD("get", "property"), &Object::_get_bind);
- ClassDB::bind_method(D_METHOD("set_indexed", "property", "value"), &Object::_set_indexed_bind);
- ClassDB::bind_method(D_METHOD("get_indexed", "property"), &Object::_get_indexed_bind);
+ ClassDB::bind_method(D_METHOD("set_indexed", "property_path", "value"), &Object::_set_indexed_bind);
+ ClassDB::bind_method(D_METHOD("get_indexed", "property_path"), &Object::_get_indexed_bind);
ClassDB::bind_method(D_METHOD("get_property_list"), &Object::_get_property_list_bind);
ClassDB::bind_method(D_METHOD("get_method_list"), &Object::_get_method_list_bind);
ClassDB::bind_method(D_METHOD("notification", "what", "reversed"), &Object::notification, DEFVAL(false));
@@ -1856,6 +1856,46 @@ void ObjectDB::debug_objects(DebugFunc p_func) {
}
void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ if (p_idx == 0) {
+ if (p_function == "connect" || p_function == "is_connected" || p_function == "disconnect" || p_function == "emit_signal" || p_function == "has_signal") {
+ List<MethodInfo> signals;
+ get_signal_list(&signals);
+ for (const MethodInfo &E : signals) {
+ r_options->push_back(E.name.quote());
+ }
+ } else if (p_function == "call" || p_function == "call_deferred" || p_function == "callv" || p_function == "has_method") {
+ List<MethodInfo> methods;
+ get_method_list(&methods);
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("_") && !(E.flags & METHOD_FLAG_VIRTUAL)) {
+ continue;
+ }
+ r_options->push_back(E.name.quote());
+ }
+ } else if (p_function == "set" || p_function == "set_deferred" || p_function == "get") {
+ List<PropertyInfo> properties;
+ get_property_list(&properties);
+ for (const PropertyInfo &E : properties) {
+ if (E.usage & PROPERTY_USAGE_DEFAULT && !(E.usage & PROPERTY_USAGE_INTERNAL)) {
+ r_options->push_back(E.name.quote());
+ }
+ }
+ } else if (p_function == "set_meta" || p_function == "get_meta" || p_function == "has_meta" || p_function == "remove_meta") {
+ for (const KeyValue<StringName, Variant> &K : metadata) {
+ r_options->push_back(String(K.key).quote());
+ }
+ }
+ } else if (p_idx == 2) {
+ if (p_function == "connect") {
+ // Ideally, the constants should be inferred by the parameter.
+ // But a parameter's PropertyInfo does not store the enum they come from, so this will do for now.
+ List<StringName> constants;
+ ClassDB::get_enum_constants("Object", "ConnectFlags", &constants);
+ for (const StringName &E : constants) {
+ r_options->push_back(String(E));
+ }
+ }
+ }
}
SpinLock ObjectDB::spin_lock;
diff --git a/core/object/object.h b/core/object/object.h
index 5ba5453b31..fa3003cc1f 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -133,7 +133,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_ARRAY = 1 << 29, // Used in the inspector to group properties as elements of an array.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
- PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNATIONALIZED,
+ PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNATIONALIZED,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,
};
@@ -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/os/midi_driver.cpp b/core/os/midi_driver.cpp
index 410b62068a..79eef95ef2 100644
--- a/core/os/midi_driver.cpp
+++ b/core/os/midi_driver.cpp
@@ -86,11 +86,6 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_
if (length >= 2 + param_position) {
event->set_pitch(data[param_position]);
event->set_velocity(data[param_position + 1]);
-
- if (event->get_message() == MIDIMessage::NOTE_ON && event->get_velocity() == 0) {
- // https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on
- event->set_message(MIDIMessage::NOTE_OFF);
- }
}
break;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 872c8357ae..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);
}
@@ -3667,32 +3668,43 @@ bool String::is_network_share_path() const {
String String::simplify_path() const {
String s = *this;
String drive;
- if (s.begins_with("local://")) {
- drive = "local://";
- s = s.substr(8);
- } else if (s.begins_with("res://")) {
- drive = "res://";
- s = s.substr(6);
- } else if (s.begins_with("user://")) {
- drive = "user://";
- s = s.substr(7);
- } else if (s.begins_with("uid://")) {
- drive = "uid://";
- s = s.substr(6);
- } else if (is_network_share_path()) {
- drive = s.substr(0, 2);
- s = s.substr(2, s.length() - 2);
- } else if (s.begins_with("/") || s.begins_with("\\")) {
- drive = s.substr(0, 1);
- s = s.substr(1, s.length() - 1);
- } else {
- int p = s.find(":/");
- if (p == -1) {
- p = s.find(":\\");
+
+ // Check if we have a special path (like res://) or a protocol identifier.
+ int p = s.find("://");
+ bool found = false;
+ if (p > 0) {
+ bool only_chars = true;
+ for (int i = 0; i < p; i++) {
+ if (!is_ascii_alphanumeric_char(s[i])) {
+ only_chars = false;
+ break;
+ }
+ }
+ if (only_chars) {
+ found = true;
+ drive = s.substr(0, p + 3);
+ s = s.substr(p + 3);
}
- if (p != -1 && p < s.find("/")) {
- drive = s.substr(0, p + 2);
- s = s.substr(p + 2);
+ }
+ if (!found) {
+ if (is_network_share_path()) {
+ // Network path, beginning with // or \\.
+ drive = s.substr(0, 2);
+ s = s.substr(2);
+ } else if (s.begins_with("/") || s.begins_with("\\")) {
+ // Absolute path.
+ drive = s.substr(0, 1);
+ s = s.substr(1);
+ } else {
+ // Windows-style drive path, like C:/ or C:\.
+ p = s.find(":/");
+ if (p == -1) {
+ p = s.find(":\\");
+ }
+ if (p != -1 && p < s.find("/")) {
+ drive = s.substr(0, p + 2);
+ s = s.substr(p + 2);
+ }
}
}
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);