summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/project_settings.cpp18
-rw-r--r--core/core_bind.cpp39
-rw-r--r--core/core_bind.h3
-rw-r--r--core/core_constants.cpp4
-rw-r--r--core/error/error_macros.h22
-rw-r--r--core/extension/gdnative_interface.cpp4
-rw-r--r--core/extension/gdnative_interface.h2
-rw-r--r--core/extension/native_extension.cpp1
-rw-r--r--core/input/input_map.cpp2
-rw-r--r--core/io/dir_access.cpp14
-rw-r--r--core/io/dir_access.h2
-rw-r--r--core/io/logger.cpp10
-rw-r--r--core/io/resource.cpp20
-rw-r--r--core/io/resource_format_binary.cpp22
-rw-r--r--core/io/resource_saver.h3
-rw-r--r--core/math/bvh.h264
-rw-r--r--core/math/bvh_abb.h64
-rw-r--r--core/math/bvh_cull.inc95
-rw-r--r--core/math/bvh_debug.inc4
-rw-r--r--core/math/bvh_logic.inc16
-rw-r--r--core/math/bvh_misc.inc6
-rw-r--r--core/math/bvh_pair.inc14
-rw-r--r--core/math/bvh_public.inc146
-rw-r--r--core/math/bvh_split.inc18
-rw-r--r--core/math/bvh_structs.inc69
-rw-r--r--core/math/bvh_tree.h50
-rw-r--r--core/math/expression.cpp2
-rw-r--r--core/math/math_funcs.h2
-rw-r--r--core/math/vector2.h3
-rw-r--r--core/math/vector2i.h3
-rw-r--r--core/math/vector3.h2
-rw-r--r--core/math/vector3i.h3
-rw-r--r--core/object/class_db.cpp21
-rw-r--r--core/object/class_db.h17
-rw-r--r--core/object/make_virtuals.py7
-rw-r--r--core/object/message_queue.cpp41
-rw-r--r--core/object/message_queue.h43
-rw-r--r--core/object/object.cpp60
-rw-r--r--core/object/object.h53
-rw-r--r--core/object/script_language.cpp14
-rw-r--r--core/object/script_language.h20
-rw-r--r--core/object/undo_redo.cpp55
-rw-r--r--core/object/undo_redo.h32
-rw-r--r--core/os/os.cpp8
-rw-r--r--core/os/thread.cpp1
-rw-r--r--core/os/thread.h1
-rw-r--r--core/os/time.cpp20
-rw-r--r--core/os/time.h5
-rw-r--r--core/register_core_types.cpp32
-rw-r--r--core/templates/paged_allocator.h2
-rw-r--r--core/templates/pooled_list.h149
-rw-r--r--core/variant/binder_common.h6
-rw-r--r--core/variant/callable.cpp6
-rw-r--r--core/variant/variant.cpp20
-rw-r--r--core/variant/variant.h27
-rw-r--r--core/variant/variant_call.cpp4
-rw-r--r--core/variant/variant_setget.cpp6
-rw-r--r--core/variant/variant_utility.cpp5
58 files changed, 1042 insertions, 540 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 3a7fc828aa..93c1abe7b5 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -145,7 +145,7 @@ String ProjectSettings::localize_path(const String &p_path) const {
return p_path.simplify_path();
}
- DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
String path = p_path.replace("\\", "/").simplify_path();
@@ -153,8 +153,6 @@ String ProjectSettings::localize_path(const String &p_path) const {
String cwd = dir->get_current_dir();
cwd = cwd.replace("\\", "/");
- memdelete(dir);
-
// Ensure that we end with a '/'.
// This is important to ensure that we do not wrongly localize the resource path
// in an absolute path that just happens to contain this string but points to a
@@ -173,8 +171,6 @@ String ProjectSettings::localize_path(const String &p_path) const {
return cwd.replace_first(res_path, "res://");
} else {
- memdelete(dir);
-
int sep = path.rfind("/");
if (sep == -1) {
return "res://" + path;
@@ -541,7 +537,7 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
// Nothing was found, try to find a project file in provided path (`p_path`)
// or, if requested (`p_upwards`) in parent directories.
- DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
ERR_FAIL_COND_V_MSG(!d, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_path + "'.");
d->change_dir(p_path);
@@ -573,8 +569,6 @@ Error ProjectSettings::_setup(const String &p_path, const String &p_main_pack, b
}
}
- memdelete(d);
-
if (!found) {
return err;
}
@@ -614,11 +608,7 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
bool ProjectSettings::has_setting(String p_var) const {
_THREAD_SAFE_METHOD_
- StringName name = p_var;
- if (!disable_feature_overrides && feature_overrides.has(name)) {
- name = feature_overrides[name];
- }
- return props.has(name);
+ return props.has(p_var);
}
Error ProjectSettings::_load_settings_binary(const String &p_path) {
@@ -1203,6 +1193,8 @@ ProjectSettings::ProjectSettings() {
singleton = this;
GLOBAL_DEF_BASIC("application/config/name", "");
+ GLOBAL_DEF_BASIC("application/config/name_localized", Dictionary());
+ custom_prop_info["application/config/name_localized"] = PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING);
GLOBAL_DEF_BASIC("application/config/description", "");
custom_prop_info["application/config/description"] = PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT);
GLOBAL_DEF_BASIC("application/run/main_scene", "");
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index bb4b49d9cd..2d0eaadbdf 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -137,7 +137,7 @@ void ResourceLoader::_bind_methods() {
////// ResourceSaver //////
-Error ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) {
+Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + String(p_path) + "'.");
return ::ResourceSaver::save(p_path, p_resource, p_flags);
}
@@ -156,9 +156,10 @@ Vector<String> ResourceSaver::get_recognized_extensions(const RES &p_resource) {
ResourceSaver *ResourceSaver::singleton = nullptr;
void ResourceSaver::_bind_methods() {
- ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &ResourceSaver::save, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("save", "path", "resource", "flags"), &ResourceSaver::save, DEFVAL((uint32_t)FLAG_NONE));
ClassDB::bind_method(D_METHOD("get_recognized_extensions", "type"), &ResourceSaver::get_recognized_extensions);
+ BIND_ENUM_CONSTANT(FLAG_NONE);
BIND_ENUM_CONSTANT(FLAG_RELATIVE_PATHS);
BIND_ENUM_CONSTANT(FLAG_BUNDLE_RESOURCES);
BIND_ENUM_CONSTANT(FLAG_CHANGE_PATH);
@@ -1568,10 +1569,8 @@ String Directory::get_current_dir() {
Error Directory::make_dir(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
if (!p_dir.is_relative_path()) {
- DirAccess *d = DirAccess::create_for_path(p_dir);
- Error err = d->make_dir(p_dir);
- memdelete(d);
- return err;
+ DirAccessRef da = DirAccess::create_for_path(p_dir);
+ return da->make_dir(p_dir);
}
return d->make_dir(p_dir);
}
@@ -1579,10 +1578,8 @@ Error Directory::make_dir(String p_dir) {
Error Directory::make_dir_recursive(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, ERR_UNCONFIGURED, "Directory is not configured properly.");
if (!p_dir.is_relative_path()) {
- DirAccess *d = DirAccess::create_for_path(p_dir);
- Error err = d->make_dir_recursive(p_dir);
- memdelete(d);
- return err;
+ DirAccessRef da = DirAccess::create_for_path(p_dir);
+ return da->make_dir_recursive(p_dir);
}
return d->make_dir_recursive(p_dir);
}
@@ -1592,19 +1589,14 @@ bool Directory::file_exists(String p_file) {
if (!p_file.is_relative_path()) {
return FileAccess::exists(p_file);
}
-
return d->file_exists(p_file);
}
bool Directory::dir_exists(String p_dir) {
ERR_FAIL_COND_V_MSG(!d, false, "Directory is not configured properly.");
if (!p_dir.is_relative_path()) {
- DirAccess *d = DirAccess::create_for_path(p_dir);
- bool exists = d->dir_exists(p_dir);
- memdelete(d);
- return exists;
+ return DirAccess::exists(p_dir);
}
-
return d->dir_exists(p_dir);
}
@@ -1623,11 +1615,9 @@ Error Directory::rename(String p_from, String p_to) {
ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename.");
if (!p_from.is_relative_path()) {
- DirAccess *d = DirAccess::create_for_path(p_from);
- ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");
- Error err = d->rename(p_from, p_to);
- memdelete(d);
- return err;
+ DirAccessRef da = DirAccess::create_for_path(p_from);
+ ERR_FAIL_COND_V_MSG(!da->file_exists(p_from) && !da->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");
+ return da->rename(p_from, p_to);
}
ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist.");
@@ -1637,10 +1627,8 @@ Error Directory::rename(String p_from, String p_to) {
Error Directory::remove(String p_name) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
if (!p_name.is_relative_path()) {
- DirAccess *d = DirAccess::create_for_path(p_name);
- Error err = d->remove(p_name);
- memdelete(d);
- return err;
+ DirAccessRef da = DirAccess::create_for_path(p_name);
+ return da->remove(p_name);
}
return d->remove(p_name);
@@ -1663,7 +1651,6 @@ void Directory::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &Directory::make_dir_recursive);
ClassDB::bind_method(D_METHOD("file_exists", "path"), &Directory::file_exists);
ClassDB::bind_method(D_METHOD("dir_exists", "path"), &Directory::dir_exists);
- //ClassDB::bind_method(D_METHOD("get_modified_time","file"),&Directory::get_modified_time);
ClassDB::bind_method(D_METHOD("get_space_left"), &Directory::get_space_left);
ClassDB::bind_method(D_METHOD("copy", "from", "to"), &Directory::copy);
ClassDB::bind_method(D_METHOD("rename", "from", "to"), &Directory::rename);
diff --git a/core/core_bind.h b/core/core_bind.h
index 1ed243206b..4a7eb718f1 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -94,6 +94,7 @@ protected:
public:
enum SaverFlags {
+ FLAG_NONE = 0,
FLAG_RELATIVE_PATHS = 1,
FLAG_BUNDLE_RESOURCES = 2,
FLAG_CHANGE_PATH = 4,
@@ -105,7 +106,7 @@ public:
static ResourceSaver *get_singleton() { return singleton; }
- Error save(const String &p_path, const RES &p_resource, SaverFlags p_flags);
+ Error save(const String &p_path, const RES &p_resource, uint32_t p_flags);
Vector<String> get_recognized_extensions(const RES &p_resource);
ResourceSaver() { singleton = this; }
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 63e7323f7a..98b720ab65 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -168,6 +168,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_IMAGE_MASK);
BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TEXT_MASK);
+ BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPECIAL);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ESCAPE);
BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TAB);
@@ -422,6 +423,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, KPAD);
BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, GROUP_SWITCH);
+ BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MIDDLE);
@@ -472,6 +474,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, SDL_MAX);
BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, MAX);
+ BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_OFF);
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_ON);
BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, AFTERTOUCH);
@@ -588,6 +591,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALIZABLE_STRING);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE);
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index 7b032fb4cd..01e22e84b7 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -194,6 +194,7 @@ void _err_flush_stdout();
#define CRASH_BAD_INDEX(m_index, m_size) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -208,6 +209,7 @@ void _err_flush_stdout();
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -296,6 +298,7 @@ void _err_flush_stdout();
#define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size) \
if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", true); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -310,6 +313,7 @@ void _err_flush_stdout();
#define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg) \
if (unlikely((m_index) >= (m_size))) { \
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -559,6 +563,7 @@ void _err_flush_stdout();
#define CRASH_COND(m_cond) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -573,6 +578,7 @@ void _err_flush_stdout();
#define CRASH_COND_MSG(m_cond, m_msg) \
if (unlikely(m_cond)) { \
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \
+ _err_flush_stdout(); \
GENERATE_TRAP(); \
} else \
((void)0)
@@ -808,4 +814,20 @@ void _err_flush_stdout();
} else \
((void)0)
+/**
+ * This should be a 'free' assert for program flow and should not be needed in any releases,
+ * only used in dev builds.
+ */
+#ifdef DEV_ENABLED
+#define DEV_ASSERT(m_cond) \
+ if (unlikely(!(m_cond))) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed \"" _STR(m_cond) "\" is false."); \
+ _err_flush_stdout(); \
+ GENERATE_TRAP(); \
+ } else \
+ ((void)0)
+#else
+#define DEV_ASSERT(m_cond)
+#endif
+
#endif // ERROR_MACROS_H
diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp
index 385117eed1..d9ec42dc9d 100644
--- a/core/extension/gdnative_interface.cpp
+++ b/core/extension/gdnative_interface.cpp
@@ -80,7 +80,7 @@ static void gdnative_variant_call(GDNativeVariantPtr p_self, const GDNativeStrin
const Variant **args = (const Variant **)p_args;
Variant ret;
Callable::CallError error;
- self->call(*method, args, p_argcount, ret, error);
+ self->callp(*method, args, p_argcount, ret, error);
memnew_placement(r_return, Variant(ret));
if (r_error) {
@@ -152,7 +152,7 @@ static void gdnative_variant_set_indexed(GDNativeVariantPtr p_self, GDNativeInt
bool valid;
bool oob;
- self->set_indexed(p_index, value, valid, oob);
+ self->set_indexed(p_index, *value, valid, oob);
*r_valid = valid;
*r_oob = oob;
}
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
index 62934d1d73..76e87eaf23 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -194,6 +194,7 @@ typedef void *GDExtensionClassInstancePtr;
typedef GDNativeBool (*GDNativeExtensionClassSet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, const GDNativeVariantPtr p_value);
typedef GDNativeBool (*GDNativeExtensionClassGet)(GDExtensionClassInstancePtr p_instance, const GDNativeStringNamePtr p_name, GDNativeVariantPtr r_ret);
+typedef uint64_t (*GDNativeExtensionClassGetRID)(GDExtensionClassInstancePtr p_instance);
typedef struct {
uint32_t type;
@@ -228,6 +229,7 @@ typedef struct {
GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
GDNativeExtensionClassGetVirtual get_virtual_func;
+ GDNativeExtensionClassGetRID get_rid_func;
void *class_userdata;
} GDNativeExtensionClassCreationInfo;
diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp
index 325ccec6c4..1a39c937e8 100644
--- a/core/extension/native_extension.cpp
+++ b/core/extension/native_extension.cpp
@@ -158,6 +158,7 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr
extension->native_extension.create_instance = p_extension_funcs->create_instance_func;
extension->native_extension.free_instance = p_extension_funcs->free_instance_func;
extension->native_extension.get_virtual = p_extension_funcs->get_virtual_func;
+ extension->native_extension.get_rid = p_extension_funcs->get_rid_func;
ClassDB::register_extension_class(&extension->native_extension);
}
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 41083b4c47..ab94c00999 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -344,7 +344,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
{ "ui_filedialog_refresh", TTRC("Refresh") },
{ "ui_filedialog_show_hidden", TTRC("Show Hidden") },
{ "ui_swap_input_direction ", TTRC("Swap Input Direction") },
- { "", TTRC("")}
+ { "", ""}
/* clang-format on */
};
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 86d8dea3d9..73efdeb38e 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -250,6 +250,14 @@ DirAccess *DirAccess::create(AccessType p_access) {
DirAccess *da = create_func[p_access] ? create_func[p_access]() : nullptr;
if (da) {
da->_access_type = p_access;
+
+ // for ACCESS_RESOURCES and ACCESS_FILESYSTEM, current_dir already defaults to where game was started
+ // in case current directory is force changed elsewhere for ACCESS_RESOURCES
+ if (p_access == ACCESS_RESOURCES) {
+ da->change_dir("res://");
+ } else if (p_access == ACCESS_USERDATA) {
+ da->change_dir("user://");
+ }
}
return da;
@@ -414,8 +422,6 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_
}
bool DirAccess::exists(String p_dir) {
- DirAccess *da = DirAccess::create_for_path(p_dir);
- bool valid = da->change_dir(p_dir) == OK;
- memdelete(da);
- return valid;
+ DirAccessRef da = DirAccess::create_for_path(p_dir);
+ return da->change_dir(p_dir) == OK;
}
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 8154f5366c..d63453e947 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -134,7 +134,7 @@ struct DirAccessRef {
operator bool() const { return f != nullptr; }
- DirAccess *f;
+ DirAccess *f = nullptr;
DirAccessRef(DirAccess *fa) { f = fa; }
~DirAccessRef() {
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index cb6369ae3d..2b6f230434 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -128,7 +128,7 @@ void RotatedFileLogger::clear_old_backups() {
String basename = base_path.get_file().get_basename();
String extension = base_path.get_extension();
- DirAccess *da = DirAccess::open(base_path.get_base_dir());
+ DirAccessRef da = DirAccess::open(base_path.get_base_dir());
if (!da) {
return;
}
@@ -152,8 +152,6 @@ void RotatedFileLogger::clear_old_backups() {
da->remove(E->get());
}
}
-
- memdelete(da);
}
void RotatedFileLogger::rotate_file() {
@@ -167,18 +165,16 @@ void RotatedFileLogger::rotate_file() {
backup_name += "." + base_path.get_extension();
}
- DirAccess *da = DirAccess::open(base_path.get_base_dir());
+ DirAccessRef da = DirAccess::open(base_path.get_base_dir());
if (da) {
da->copy(base_path, backup_name);
- memdelete(da);
}
clear_old_backups();
}
} else {
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_USERDATA);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_USERDATA);
if (da) {
da->make_dir_recursive(base_path.get_base_dir());
- memdelete(da);
}
}
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 66d5c54b53..f90a6e9304 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -290,6 +290,21 @@ void Resource::_take_over_path(const String &p_path) {
}
RID Resource::get_rid() const {
+ if (get_script_instance()) {
+ Callable::CallError ce;
+ RID ret = get_script_instance()->callp(SNAME("_get_rid"), nullptr, 0, ce);
+ if (ce.error == Callable::CallError::CALL_OK && ret.is_valid()) {
+ return ret;
+ }
+ }
+ if (_get_extension() && _get_extension()->get_rid) {
+ RID ret;
+ ret.from_uint64(_get_extension()->get_rid(_get_extension_instance()));
+ if (ret.is_valid()) {
+ return ret;
+ }
+ }
+
return RID();
}
@@ -428,6 +443,11 @@ void Resource::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name");
+
+ MethodInfo get_rid_bind("_get_rid");
+ get_rid_bind.return_val.type = Variant::RID;
+
+ ::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true);
}
Resource::Resource() :
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index ee59a916f1..b65993e3dd 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1032,7 +1032,6 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi
String path = !p_original_path.is_empty() ? p_original_path : p_path;
loader.local_path = ProjectSettings::get_singleton()->localize_path(path);
loader.res_path = loader.local_path;
- //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
loader.open(f);
err = loader.load();
@@ -1086,17 +1085,14 @@ void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<Str
ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
loader.get_dependencies(f, p_dependencies, p_add_types);
}
Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const Map<String, String> &p_map) {
- //Error error=OK;
-
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_path + "'.");
- FileAccess *fw = nullptr; //=FileAccess::open(p_path+".depren");
+ FileAccess *fw = nullptr;
String local_path = p_path.get_base_dir();
@@ -1158,10 +1154,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) {
memdelete(f);
memdelete(fw);
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- da->remove(p_path + ".depren");
- memdelete(da);
- //use the old approach
+ {
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ da->remove(p_path + ".depren");
+ }
+
+ // Use the old approach.
WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'.");
@@ -1174,7 +1172,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
loader.remaps = p_map;
- //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
loader.open(f);
err = loader.load();
@@ -1304,10 +1301,9 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
return ERR_CANT_CREATE;
}
- DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
da->remove(p_path);
da->rename(p_path + ".depren", p_path);
- memdelete(da);
return OK;
}
@@ -1320,7 +1316,6 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
String r = loader.recognize(f);
return ClassDB::get_compatibility_remapped_class(r);
}
@@ -1339,7 +1334,6 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat
ResourceLoaderBinary loader;
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
loader.res_path = loader.local_path;
- //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) );
loader.open(f, true);
if (loader.error != OK) {
return ResourceUID::INVALID_ID; //could not read
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 2919a4cec0..ebc3be91a1 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -71,6 +71,7 @@ class ResourceSaver {
public:
enum SaverFlags {
+ FLAG_NONE = 0,
FLAG_RELATIVE_PATHS = 1,
FLAG_BUNDLE_RESOURCES = 2,
FLAG_CHANGE_PATH = 4,
@@ -80,7 +81,7 @@ public:
FLAG_REPLACE_SUBRESOURCE_PATHS = 64,
};
- static Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
+ static Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = (uint32_t)FLAG_NONE);
static void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions);
static void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front = false);
static void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);
diff --git a/core/math/bvh.h b/core/math/bvh.h
index a8e3cc7bbe..e686e27445 100644
--- a/core/math/bvh.h
+++ b/core/math/bvh.h
@@ -46,21 +46,35 @@
// Layer masks are implemented in the renderers as a later step, and light_cull_mask appears to be
// implemented in GLES3 but not GLES2. Layer masks are not yet implemented for directional lights.
+// In the physics, the pairable_type is based on 1 << p_object->get_type() where:
+// TYPE_AREA,
+// TYPE_BODY
+// and pairable_mask is either 0 if static, or set to all if non static
+
#include "bvh_tree.h"
+#include "core/os/mutex.h"
-#define BVHTREE_CLASS BVH_Tree<T, 2, MAX_ITEMS, USE_PAIRS, Bounds, Point>
+#define BVHTREE_CLASS BVH_Tree<T, NUM_TREES, 2, MAX_ITEMS, USER_PAIR_TEST_FUNCTION, USER_CULL_TEST_FUNCTION, USE_PAIRS, BOUNDS, POINT>
+#define BVH_LOCKED_FUNCTION BVHLockedFunction(&_mutex, BVH_THREAD_SAFE &&_thread_safe);
-template <class T, bool USE_PAIRS = false, int MAX_ITEMS = 32, class Bounds = AABB, class Point = Vector3>
+template <class T, int NUM_TREES = 1, bool USE_PAIRS = false, int MAX_ITEMS = 32, class USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, class USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, class BOUNDS = AABB, class POINT = Vector3, bool BVH_THREAD_SAFE = true>
class BVH_Manager {
public:
// note we are using uint32_t instead of BVHHandle, losing type safety, but this
// is for compatibility with octree
typedef void *(*PairCallback)(void *, uint32_t, T *, int, uint32_t, T *, int);
typedef void (*UnpairCallback)(void *, uint32_t, T *, int, uint32_t, T *, int, void *);
+ typedef void *(*CheckPairCallback)(void *, uint32_t, T *, int, uint32_t, T *, int, void *);
+
+ // allow locally toggling thread safety if the template has been compiled with BVH_THREAD_SAFE
+ void params_set_thread_safe(bool p_enable) {
+ _thread_safe = p_enable;
+ }
// these 2 are crucial for fine tuning, and can be applied manually
// see the variable declarations for more info.
void params_set_node_expansion(real_t p_value) {
+ BVH_LOCKED_FUNCTION
if (p_value >= 0.0) {
tree._node_expansion = p_value;
tree._auto_node_expansion = false;
@@ -70,43 +84,40 @@ public:
}
void params_set_pairing_expansion(real_t p_value) {
- if (p_value >= 0.0) {
- tree._pairing_expansion = p_value;
- tree._auto_pairing_expansion = false;
- } else {
- tree._auto_pairing_expansion = true;
- }
+ BVH_LOCKED_FUNCTION
+ tree.params_set_pairing_expansion(p_value);
}
void set_pair_callback(PairCallback p_callback, void *p_userdata) {
+ BVH_LOCKED_FUNCTION
pair_callback = p_callback;
pair_callback_userdata = p_userdata;
}
void set_unpair_callback(UnpairCallback p_callback, void *p_userdata) {
+ BVH_LOCKED_FUNCTION
unpair_callback = p_callback;
unpair_callback_userdata = p_userdata;
}
+ void set_check_pair_callback(CheckPairCallback p_callback, void *p_userdata) {
+ BVH_LOCKED_FUNCTION
+ check_pair_callback = p_callback;
+ check_pair_callback_userdata = p_userdata;
+ }
+
+ BVHHandle create(T *p_userdata, bool p_active = true, uint32_t p_tree_id = 0, uint32_t p_tree_collision_mask = 1, const BOUNDS &p_aabb = BOUNDS(), int p_subindex = 0) {
+ BVH_LOCKED_FUNCTION
- BVHHandle create(T *p_userdata, bool p_active, const Bounds &p_aabb = Bounds(), int p_subindex = 0, bool p_pairable = false, uint32_t p_pairable_type = 0, uint32_t p_pairable_mask = 1) {
// not sure if absolutely necessary to flush collisions here. It will cost performance to, instead
// of waiting for update, so only uncomment this if there are bugs.
if (USE_PAIRS) {
//_check_for_collisions();
}
-#ifdef TOOLS_ENABLED
- if (!USE_PAIRS) {
- if (p_pairable) {
- WARN_PRINT_ONCE("creating pairable item in BVH with USE_PAIRS set to false");
- }
- }
-#endif
-
- BVHHandle h = tree.item_add(p_userdata, p_active, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask);
+ BVHHandle h = tree.item_add(p_userdata, p_active, p_aabb, p_subindex, p_tree_id, p_tree_collision_mask);
if (USE_PAIRS) {
// for safety initialize the expanded AABB
- Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
+ BOUNDS &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
expanded_aabb = p_aabb;
expanded_aabb.grow_by(tree._pairing_expansion);
@@ -123,12 +134,18 @@ public:
////////////////////////////////////////////////////
// wrapper versions that use uint32_t instead of handle
// for backward compatibility. Less type safe
- void move(uint32_t p_handle, const Bounds &p_aabb) {
+ void move(uint32_t p_handle, const BOUNDS &p_aabb) {
BVHHandle h;
h.set(p_handle);
move(h, p_aabb);
}
+ void recheck_pairs(uint32_t p_handle) {
+ BVHHandle h;
+ h.set(p_handle);
+ recheck_pairs(h);
+ }
+
void erase(uint32_t p_handle) {
BVHHandle h;
h.set(p_handle);
@@ -141,7 +158,7 @@ public:
force_collision_check(h);
}
- bool activate(uint32_t p_handle, const Bounds &p_aabb, bool p_delay_collision_check = false) {
+ bool activate(uint32_t p_handle, const BOUNDS &p_aabb, bool p_delay_collision_check = false) {
BVHHandle h;
h.set(p_handle);
return activate(h, p_aabb, p_delay_collision_check);
@@ -153,16 +170,16 @@ public:
return deactivate(h);
}
- void set_pairable(uint32_t p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask, bool p_force_collision_check = true) {
+ void set_tree(uint32_t p_handle, uint32_t p_tree_id, uint32_t p_tree_collision_mask, bool p_force_collision_check = true) {
BVHHandle h;
h.set(p_handle);
- set_pairable(h, p_pairable, p_pairable_type, p_pairable_mask, p_force_collision_check);
+ set_tree(h, p_tree_id, p_tree_collision_mask, p_force_collision_check);
}
- bool is_pairable(uint32_t p_handle) const {
+ uint32_t get_tree_id(uint32_t p_handle) const {
BVHHandle h;
h.set(p_handle);
- return item_is_pairable(h);
+ return item_get_tree_id(h);
}
int get_subindex(uint32_t p_handle) const {
BVHHandle h;
@@ -178,7 +195,8 @@ public:
////////////////////////////////////////////////////
- void move(BVHHandle p_handle, const Bounds &p_aabb) {
+ void move(BVHHandle p_handle, const BOUNDS &p_aabb) {
+ BVH_LOCKED_FUNCTION
if (tree.item_move(p_handle, p_aabb)) {
if (USE_PAIRS) {
_add_changed_item(p_handle, p_aabb);
@@ -186,7 +204,12 @@ public:
}
}
+ void recheck_pairs(BVHHandle p_handle) {
+ force_collision_check(p_handle);
+ }
+
void erase(BVHHandle p_handle) {
+ BVH_LOCKED_FUNCTION
// call unpair and remove all references to the item
// before deleting from the tree
if (USE_PAIRS) {
@@ -200,11 +223,12 @@ public:
// use in conjunction with activate if you have deferred the collision check, and
// set pairable has never been called.
- // (deferred collision checks are a workaround for rendering server for historical reasons)
+ // (deferred collision checks are a workaround for visual server for historical reasons)
void force_collision_check(BVHHandle p_handle) {
+ BVH_LOCKED_FUNCTION
if (USE_PAIRS) {
// the aabb should already be up to date in the BVH
- Bounds aabb;
+ BOUNDS aabb;
item_get_AABB(p_handle, aabb);
// add it as changed even if aabb not different
@@ -218,7 +242,8 @@ public:
// these should be read as set_visible for render trees,
// but generically this makes items add or remove from the
// tree internally, to speed things up by ignoring inactive items
- bool activate(BVHHandle p_handle, const Bounds &p_aabb, bool p_delay_collision_check = false) {
+ bool activate(BVHHandle p_handle, const BOUNDS &p_aabb, bool p_delay_collision_check = false) {
+ BVH_LOCKED_FUNCTION
// sending the aabb here prevents the need for the BVH to maintain
// a redundant copy of the aabb.
// returns success
@@ -242,6 +267,7 @@ public:
}
bool deactivate(BVHHandle p_handle) {
+ BVH_LOCKED_FUNCTION
// returns success
if (tree.item_deactivate(p_handle)) {
// call unpair and remove all references to the item
@@ -258,12 +284,14 @@ public:
return false;
}
- bool get_active(BVHHandle p_handle) const {
+ bool get_active(BVHHandle p_handle) {
+ BVH_LOCKED_FUNCTION
return tree.item_get_active(p_handle);
}
// call e.g. once per frame (this does a trickle optimize)
void update() {
+ BVH_LOCKED_FUNCTION
tree.update();
_check_for_collisions();
#ifdef BVH_INTEGRITY_CHECKS
@@ -273,24 +301,26 @@ public:
// this can be called more frequently than per frame if necessary
void update_collisions() {
+ BVH_LOCKED_FUNCTION
_check_for_collisions();
}
// prefer calling this directly as type safe
- void set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask, bool p_force_collision_check = true) {
+ void set_tree(const BVHHandle &p_handle, uint32_t p_tree_id, uint32_t p_tree_collision_mask, bool p_force_collision_check = true) {
+ BVH_LOCKED_FUNCTION
// Returns true if the pairing state has changed.
- bool state_changed = tree.item_set_pairable(p_handle, p_pairable, p_pairable_type, p_pairable_mask);
+ bool state_changed = tree.item_set_tree(p_handle, p_tree_id, p_tree_collision_mask);
if (USE_PAIRS) {
// not sure if absolutely necessary to flush collisions here. It will cost performance to, instead
// of waiting for update, so only uncomment this if there are bugs.
//_check_for_collisions();
- if ((p_force_collision_check || state_changed) && get_active(p_handle)) {
+ if ((p_force_collision_check || state_changed) && tree.item_get_active(p_handle)) {
// when the pairable state changes, we need to force a collision check because newly pairable
// items may be in collision, and unpairable items might move out of collision.
// We cannot depend on waiting for the next update, because that may come much later.
- Bounds aabb;
+ BOUNDS aabb;
item_get_AABB(p_handle, aabb);
// passing false disables the optimization which prevents collision checks if
@@ -307,32 +337,33 @@ public:
}
// cull tests
- int cull_aabb(const Bounds &p_aabb, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
+ int cull_aabb(const BOUNDS &p_aabb, T **p_result_array, int p_result_max, const T *p_tester, uint32_t p_tree_collision_mask = 0xFFFFFFFF, int *p_subindex_array = nullptr) {
+ BVH_LOCKED_FUNCTION
typename BVHTREE_CLASS::CullParams params;
params.result_count_overall = 0;
params.result_max = p_result_max;
params.result_array = p_result_array;
params.subindex_array = p_subindex_array;
- params.mask = p_mask;
- params.pairable_type = 0;
- params.test_pairable_only = false;
+ params.tree_collision_mask = p_tree_collision_mask;
params.abb.from(p_aabb);
+ params.tester = p_tester;
tree.cull_aabb(params);
return params.result_count_overall;
}
- int cull_segment(const Point &p_from, const Point &p_to, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
+ int cull_segment(const POINT &p_from, const POINT &p_to, T **p_result_array, int p_result_max, const T *p_tester, uint32_t p_tree_collision_mask = 0xFFFFFFFF, int *p_subindex_array = nullptr) {
+ BVH_LOCKED_FUNCTION
typename BVHTREE_CLASS::CullParams params;
params.result_count_overall = 0;
params.result_max = p_result_max;
params.result_array = p_result_array;
params.subindex_array = p_subindex_array;
- params.mask = p_mask;
- params.pairable_type = 0;
+ params.tester = p_tester;
+ params.tree_collision_mask = p_tree_collision_mask;
params.segment.from = p_from;
params.segment.to = p_to;
@@ -342,15 +373,16 @@ public:
return params.result_count_overall;
}
- int cull_point(const Point &p_point, T **p_result_array, int p_result_max, int *p_subindex_array = nullptr, uint32_t p_mask = 0xFFFFFFFF) {
+ int cull_point(const POINT &p_point, T **p_result_array, int p_result_max, const T *p_tester, uint32_t p_tree_collision_mask = 0xFFFFFFFF, int *p_subindex_array = nullptr) {
+ BVH_LOCKED_FUNCTION
typename BVHTREE_CLASS::CullParams params;
params.result_count_overall = 0;
params.result_max = p_result_max;
params.result_array = p_result_array;
params.subindex_array = p_subindex_array;
- params.mask = p_mask;
- params.pairable_type = 0;
+ params.tester = p_tester;
+ params.tree_collision_mask = p_tree_collision_mask;
params.point = p_point;
@@ -358,7 +390,8 @@ public:
return params.result_count_overall;
}
- int cull_convex(const Vector<Plane> &p_convex, T **p_result_array, int p_result_max, uint32_t p_mask = 0xFFFFFFFF) {
+ int cull_convex(const Vector<Plane> &p_convex, T **p_result_array, int p_result_max, const T *p_tester, uint32_t p_tree_collision_mask = 0xFFFFFFFF) {
+ BVH_LOCKED_FUNCTION
if (!p_convex.size()) {
return 0;
}
@@ -373,8 +406,8 @@ public:
params.result_max = p_result_max;
params.result_array = p_result_array;
params.subindex_array = nullptr;
- params.mask = p_mask;
- params.pairable_type = 0;
+ params.tester = p_tester;
+ params.tree_collision_mask = p_tree_collision_mask;
params.hull.planes = &p_convex[0];
params.hull.num_planes = p_convex.size();
@@ -394,7 +427,7 @@ private:
return;
}
- Bounds bb;
+ BOUNDS bb;
typename BVHTREE_CLASS::CullParams params;
@@ -402,28 +435,23 @@ private:
params.result_max = INT_MAX;
params.result_array = nullptr;
params.subindex_array = nullptr;
- params.mask = 0xFFFFFFFF;
- params.pairable_type = 0;
for (unsigned int n = 0; n < changed_items.size(); n++) {
const BVHHandle &h = changed_items[n];
// use the expanded aabb for pairing
- const Bounds &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
+ const BOUNDS &expanded_aabb = tree._pairs[h.id()].expanded_aabb;
BVHABB_CLASS abb;
abb.from(expanded_aabb);
+ tree.item_fill_cullparams(h, params);
+
// find all the existing paired aabbs that are no longer
// paired, and send callbacks
_find_leavers(h, abb, p_full_check);
uint32_t changed_item_ref_id = h.id();
- // set up the test from this item.
- // this includes whether to test the non pairable tree,
- // and the item mask.
- tree.item_fill_cullparams(h, params);
-
params.abb = abb;
params.result_count_overall = 0; // might not be needed
@@ -456,7 +484,7 @@ private:
}
public:
- void item_get_AABB(BVHHandle p_handle, Bounds &r_aabb) {
+ void item_get_AABB(BVHHandle p_handle, BOUNDS &r_aabb) {
BVHABB_CLASS abb;
tree.item_get_ABB(p_handle, abb);
abb.to(r_aabb);
@@ -464,7 +492,7 @@ public:
private:
// supplemental funcs
- bool item_is_pairable(BVHHandle p_handle) const { return _get_extra(p_handle).pairable; }
+ uint32_t item_get_tree_id(BVHHandle p_handle) const { return _get_extra(p_handle).tree_id; }
T *item_get_userdata(BVHHandle p_handle) const { return _get_extra(p_handle).userdata; }
int item_get_subindex(BVHHandle p_handle) const { return _get_extra(p_handle).subindex; }
@@ -485,12 +513,35 @@ private:
void *ud_from = pairs_from.remove_pair_to(p_to);
pairs_to.remove_pair_to(p_from);
+#ifdef BVH_VERBOSE_PAIRING
+ print_line("_unpair " + itos(p_from.id()) + " from " + itos(p_to.id()));
+#endif
+
// callback
if (unpair_callback) {
unpair_callback(pair_callback_userdata, p_from, exa.userdata, exa.subindex, p_to, exb.userdata, exb.subindex, ud_from);
}
}
+ void *_recheck_pair(BVHHandle p_from, BVHHandle p_to, void *p_pair_data) {
+ tree._handle_sort(p_from, p_to);
+
+ typename BVHTREE_CLASS::ItemExtra &exa = tree._extra[p_from.id()];
+ typename BVHTREE_CLASS::ItemExtra &exb = tree._extra[p_to.id()];
+
+ // if the userdata is the same, no collisions should occur
+ if ((exa.userdata == exb.userdata) && exa.userdata) {
+ return p_pair_data;
+ }
+
+ // callback
+ if (check_pair_callback) {
+ return check_pair_callback(check_pair_callback_userdata, p_from, exa.userdata, exa.subindex, p_to, exb.userdata, exb.subindex, p_pair_data);
+ }
+
+ return p_pair_data;
+ }
+
// returns true if unpair
bool _find_leavers_process_pair(typename BVHTREE_CLASS::ItemPairs &p_pairs_from, const BVHABB_CLASS &p_abb_from, BVHHandle p_from, BVHHandle p_to, bool p_full_check) {
BVHABB_CLASS abb_to;
@@ -498,8 +549,8 @@ private:
// do they overlap?
if (p_abb_from.intersects(abb_to)) {
- // the full check for pairable / non pairable and mask changes is extra expense
- // this need not be done in most cases (for speed) except in the case where set_pairable is called
+ // the full check for pairable / non pairable (i.e. tree_id and tree_masks) and mask changes is extra expense
+ // this need not be done in most cases (for speed) except in the case where set_tree is called
// where the masks etc of the objects in question may have changed
if (!p_full_check) {
return false;
@@ -507,12 +558,13 @@ private:
const typename BVHTREE_CLASS::ItemExtra &exa = _get_extra(p_from);
const typename BVHTREE_CLASS::ItemExtra &exb = _get_extra(p_to);
- // one of the two must be pairable to still pair
- // if neither are pairable, we always unpair
- if (exa.pairable || exb.pairable) {
+ // Checking tree_ids and tree_collision_masks
+ if (exa.are_item_trees_compatible(exb)) {
+ bool pair_allowed = USER_PAIR_TEST_FUNCTION::user_pair_check(exa.userdata, exb.userdata);
+
// the masks must still be compatible to pair
- // i.e. if there is a hit between the two, then they should stay paired
- if (tree._cull_pairing_mask_test_hit(exa.pairable_mask, exa.pairable_type, exb.pairable_mask, exb.pairable_type)) {
+ // i.e. if there is a hit between the two and they intersect, then they should stay paired
+ if (pair_allowed) {
return false;
}
}
@@ -550,6 +602,11 @@ private:
const typename BVHTREE_CLASS::ItemExtra &exa = _get_extra(p_ha);
const typename BVHTREE_CLASS::ItemExtra &exb = _get_extra(p_hb);
+ // user collision callback
+ if (!USER_PAIR_TEST_FUNCTION::user_pair_check(exa.userdata, exb.userdata)) {
+ return;
+ }
+
// if the userdata is the same, no collisions should occur
if ((exa.userdata == exb.userdata) && exa.userdata) {
return;
@@ -573,6 +630,10 @@ private:
// callback
void *callback_userdata = nullptr;
+#ifdef BVH_VERBOSE_PAIRING
+ print_line("_pair " + itos(p_ha.id()) + " to " + itos(p_hb.id()));
+#endif
+
if (pair_callback) {
callback_userdata = pair_callback(pair_callback_userdata, p_ha, exa.userdata, exa.subindex, p_hb, exb.userdata, exb.subindex);
}
@@ -594,6 +655,32 @@ private:
}
}
+ // Send pair callbacks again for all existing pairs for the given handle.
+ void _recheck_pairs(BVHHandle p_handle) {
+ typename BVHTREE_CLASS::ItemPairs &from = tree._pairs[p_handle.id()];
+
+ // checking pair for every partner.
+ for (unsigned int n = 0; n < from.extended_pairs.size(); n++) {
+ typename BVHTREE_CLASS::ItemPairs::Link &pair = from.extended_pairs[n];
+ BVHHandle h_to = pair.handle;
+ void *new_pair_data = _recheck_pair(p_handle, h_to, pair.userdata);
+
+ if (new_pair_data != pair.userdata) {
+ pair.userdata = new_pair_data;
+
+ // Update pair data for the second item.
+ typename BVHTREE_CLASS::ItemPairs &to = tree._pairs[h_to.id()];
+ for (unsigned int to_index = 0; to_index < to.extended_pairs.size(); to_index++) {
+ typename BVHTREE_CLASS::ItemPairs::Link &to_pair = to.extended_pairs[to_index];
+ if (to_pair.handle == p_handle) {
+ to_pair.userdata = new_pair_data;
+ break;
+ }
+ }
+ }
+ }
+ }
+
private:
const typename BVHTREE_CLASS::ItemExtra &_get_extra(BVHHandle p_handle) const {
return tree._extra[p_handle.id()];
@@ -607,19 +694,24 @@ private:
_tick++;
}
- void _add_changed_item(BVHHandle p_handle, const Bounds &aabb, bool p_check_aabb = true) {
+ void _add_changed_item(BVHHandle p_handle, const BOUNDS &aabb, bool p_check_aabb = true) {
// Note that non pairable items can pair with pairable,
// so all types must be added to the list
+#ifdef BVH_EXPAND_LEAF_AABBS
+ // if using expanded AABB in the leaf, the redundancy check will already have been made
+ BOUNDS &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
+ item_get_AABB(p_handle, expanded_aabb);
+#else
// aabb check with expanded aabb. This greatly decreases processing
// at the cost of slightly less accurate pairing checks
// Note this pairing AABB is separate from the AABB in the actual tree
- Bounds &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
+ BOUNDS &expanded_aabb = tree._pairs[p_handle.id()].expanded_aabb;
// passing p_check_aabb false disables the optimization which prevents collision checks if
// the aabb hasn't changed. This is needed where set_pairable has been called, but the position
// has not changed.
- if (p_check_aabb && expanded_aabb.encloses(aabb)) {
+ if (p_check_aabb && tree.expanded_aabb_encloses_not_shrink(expanded_aabb, aabb)) {
return;
}
@@ -627,6 +719,7 @@ private:
// this tick, because it is vital that the AABB is kept up to date
expanded_aabb = aabb;
expanded_aabb.grow_by(tree._pairing_expansion);
+#endif
// this code is to ensure that changed items only appear once on the updated list
// collision checking them multiple times is not needed, and repeats the same thing
@@ -670,8 +763,10 @@ private:
PairCallback pair_callback;
UnpairCallback unpair_callback;
+ CheckPairCallback check_pair_callback;
void *pair_callback_userdata;
void *unpair_callback_userdata;
+ void *check_pair_callback_userdata;
BVHTREE_CLASS tree;
@@ -680,6 +775,38 @@ private:
LocalVector<BVHHandle, uint32_t, true> changed_items;
uint32_t _tick;
+ class BVHLockedFunction {
+ public:
+ BVHLockedFunction(Mutex *p_mutex, bool p_thread_safe) {
+ // will be compiled out if not set in template
+ if (p_thread_safe) {
+ _mutex = p_mutex;
+
+ if (_mutex->try_lock() != OK) {
+ WARN_PRINT("Info : multithread BVH access detected (benign)");
+ _mutex->lock();
+ }
+
+ } else {
+ _mutex = nullptr;
+ }
+ }
+ ~BVHLockedFunction() {
+ // will be compiled out if not set in template
+ if (_mutex) {
+ _mutex->unlock();
+ }
+ }
+
+ private:
+ Mutex *_mutex;
+ };
+
+ Mutex _mutex;
+
+ // local toggle for turning on and off thread safety in project settings
+ bool _thread_safe;
+
public:
BVH_Manager() {
_tick = 1; // start from 1 so items with 0 indicate never updated
@@ -687,6 +814,7 @@ public:
unpair_callback = nullptr;
pair_callback_userdata = nullptr;
unpair_callback_userdata = nullptr;
+ _thread_safe = BVH_THREAD_SAFE;
}
};
diff --git a/core/math/bvh_abb.h b/core/math/bvh_abb.h
index 009032d34d..8a44f1c4da 100644
--- a/core/math/bvh_abb.h
+++ b/core/math/bvh_abb.h
@@ -32,7 +32,7 @@
#define BVH_ABB_H
// special optimized version of axis aligned bounding box
-template <class Bounds = AABB, class Point = Vector3>
+template <class BOUNDS = AABB, class POINT = Vector3>
struct BVH_ABB {
struct ConvexHull {
// convex hulls (optional)
@@ -43,8 +43,8 @@ struct BVH_ABB {
};
struct Segment {
- Point from;
- Point to;
+ POINT from;
+ POINT to;
};
enum IntersectResult {
@@ -54,47 +54,47 @@ struct BVH_ABB {
};
// we store mins with a negative value in order to test them with SIMD
- Point min;
- Point neg_max;
+ POINT min;
+ POINT neg_max;
bool operator==(const BVH_ABB &o) const { return (min == o.min) && (neg_max == o.neg_max); }
bool operator!=(const BVH_ABB &o) const { return (*this == o) == false; }
- void set(const Point &_min, const Point &_max) {
+ void set(const POINT &_min, const POINT &_max) {
min = _min;
neg_max = -_max;
}
// to and from standard AABB
- void from(const Bounds &p_aabb) {
+ void from(const BOUNDS &p_aabb) {
min = p_aabb.position;
neg_max = -(p_aabb.position + p_aabb.size);
}
- void to(Bounds &r_aabb) const {
+ void to(BOUNDS &r_aabb) const {
r_aabb.position = min;
r_aabb.size = calculate_size();
}
void merge(const BVH_ABB &p_o) {
- for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
neg_max[axis] = MIN(neg_max[axis], p_o.neg_max[axis]);
min[axis] = MIN(min[axis], p_o.min[axis]);
}
}
- Point calculate_size() const {
+ POINT calculate_size() const {
return -neg_max - min;
}
- Point calculate_centre() const {
- return Point((calculate_size() * 0.5) + min);
+ POINT calculate_centre() const {
+ return POINT((calculate_size() * 0.5) + min);
}
real_t get_proximity_to(const BVH_ABB &p_b) const {
- const Point d = (min - neg_max) - (p_b.min - p_b.neg_max);
+ const POINT d = (min - neg_max) - (p_b.min - p_b.neg_max);
real_t proximity = 0.0;
- for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
proximity += Math::abs(d[axis]);
}
return proximity;
@@ -104,7 +104,7 @@ struct BVH_ABB {
return (get_proximity_to(p_a) < get_proximity_to(p_b) ? 0 : 1);
}
- uint32_t find_cutting_planes(const BVH_ABB::ConvexHull &p_hull, uint32_t *p_plane_ids) const {
+ uint32_t find_cutting_planes(const typename BVH_ABB::ConvexHull &p_hull, uint32_t *p_plane_ids) const {
uint32_t count = 0;
for (int n = 0; n < p_hull.num_planes; n++) {
@@ -162,7 +162,7 @@ struct BVH_ABB {
}
bool intersects_convex_partial(const ConvexHull &p_hull) const {
- Bounds bb;
+ BOUNDS bb;
to(bb);
return bb.intersects_convex_shape(p_hull.planes, p_hull.num_planes, p_hull.points, p_hull.num_points);
}
@@ -182,7 +182,7 @@ struct BVH_ABB {
bool is_within_convex(const ConvexHull &p_hull) const {
// use half extents routine
- Bounds bb;
+ BOUNDS bb;
to(bb);
return bb.inside_convex_shape(p_hull.planes, p_hull.num_planes);
}
@@ -197,12 +197,12 @@ struct BVH_ABB {
}
bool intersects_segment(const Segment &p_s) const {
- Bounds bb;
+ BOUNDS bb;
to(bb);
return bb.intersects_segment(p_s.from, p_s.to);
}
- bool intersects_point(const Point &p_pt) const {
+ bool intersects_point(const POINT &p_pt) const {
if (_any_lessthan(-p_pt, neg_max)) {
return false;
}
@@ -212,6 +212,7 @@ struct BVH_ABB {
return true;
}
+ // Very hot in profiling, make sure optimized
bool intersects(const BVH_ABB &p_o) const {
if (_any_morethan(p_o.min, -neg_max)) {
return false;
@@ -222,6 +223,17 @@ struct BVH_ABB {
return true;
}
+ // for pre-swizzled tester (this object)
+ bool intersects_swizzled(const BVH_ABB &p_o) const {
+ if (_any_lessthan(min, p_o.min)) {
+ return false;
+ }
+ if (_any_lessthan(neg_max, p_o.neg_max)) {
+ return false;
+ }
+ return true;
+ }
+
bool is_other_within(const BVH_ABB &p_o) const {
if (_any_lessthan(p_o.neg_max, neg_max)) {
return false;
@@ -232,20 +244,20 @@ struct BVH_ABB {
return true;
}
- void grow(const Point &p_change) {
+ void grow(const POINT &p_change) {
neg_max -= p_change;
min -= p_change;
}
void expand(real_t p_change) {
- Point change;
+ POINT change;
change.set_all(p_change);
grow(change);
}
// Actually surface area metric.
float get_area() const {
- Point d = calculate_size();
+ POINT d = calculate_size();
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
}
@@ -254,8 +266,8 @@ struct BVH_ABB {
min = neg_max;
}
- bool _any_morethan(const Point &p_a, const Point &p_b) const {
- for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
+ bool _any_morethan(const POINT &p_a, const POINT &p_b) const {
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
if (p_a[axis] > p_b[axis]) {
return true;
}
@@ -263,8 +275,8 @@ struct BVH_ABB {
return false;
}
- bool _any_lessthan(const Point &p_a, const Point &p_b) const {
- for (int axis = 0; axis < Point::AXIS_COUNT; ++axis) {
+ bool _any_lessthan(const POINT &p_a, const POINT &p_b) const {
+ for (int axis = 0; axis < POINT::AXIS_COUNT; ++axis) {
if (p_a[axis] < p_b[axis]) {
return true;
}
diff --git a/core/math/bvh_cull.inc b/core/math/bvh_cull.inc
index ab468bfd29..11f50e41e6 100644
--- a/core/math/bvh_cull.inc
+++ b/core/math/bvh_cull.inc
@@ -9,20 +9,22 @@ struct CullParams {
T **result_array;
int *subindex_array;
- // nobody truly understands how masks are intended to work.
- uint32_t mask;
- uint32_t pairable_type;
+ // We now process masks etc in a user template function,
+ // and these for simplicity assume even for cull tests there is a
+ // testing object (which has masks etc) for the user cull checks.
+ // This means for cull tests on their own, the client will usually
+ // want to create a dummy object, just in order to specify masks etc.
+ const T *tester;
// optional components for different tests
- Point point;
+ POINT point;
BVHABB_CLASS abb;
typename BVHABB_CLASS::ConvexHull hull;
typename BVHABB_CLASS::Segment segment;
- // when collision testing, non pairable moving items
- // only need to be tested against the pairable tree.
- // collisions with other non pairable items are irrelevant.
- bool test_pairable_only;
+ // When collision testing, we can specify which tree ids
+ // to collide test against with the tree_collision_mask.
+ uint32_t tree_collision_mask;
};
private:
@@ -58,11 +60,22 @@ int cull_convex(CullParams &r_params, bool p_translate_hits = true) {
_cull_hits.clear();
r_params.result_count = 0;
+ uint32_t tree_test_mask = 0;
+
for (int n = 0; n < NUM_TREES; n++) {
+ tree_test_mask <<= 1;
+ if (!tree_test_mask) {
+ tree_test_mask = 1;
+ }
+
if (_root_node_id[n] == BVHCommon::INVALID) {
continue;
}
+ if (!(r_params.tree_collision_mask & tree_test_mask)) {
+ continue;
+ }
+
_cull_convex_iterative(_root_node_id[n], r_params);
}
@@ -77,11 +90,22 @@ int cull_segment(CullParams &r_params, bool p_translate_hits = true) {
_cull_hits.clear();
r_params.result_count = 0;
+ uint32_t tree_test_mask = 0;
+
for (int n = 0; n < NUM_TREES; n++) {
+ tree_test_mask <<= 1;
+ if (!tree_test_mask) {
+ tree_test_mask = 1;
+ }
+
if (_root_node_id[n] == BVHCommon::INVALID) {
continue;
}
+ if (!(r_params.tree_collision_mask & tree_test_mask)) {
+ continue;
+ }
+
_cull_segment_iterative(_root_node_id[n], r_params);
}
@@ -96,11 +120,22 @@ int cull_point(CullParams &r_params, bool p_translate_hits = true) {
_cull_hits.clear();
r_params.result_count = 0;
+ uint32_t tree_test_mask = 0;
+
for (int n = 0; n < NUM_TREES; n++) {
+ tree_test_mask <<= 1;
+ if (!tree_test_mask) {
+ tree_test_mask = 1;
+ }
+
if (_root_node_id[n] == BVHCommon::INVALID) {
continue;
}
+ if (!(r_params.tree_collision_mask & tree_test_mask)) {
+ continue;
+ }
+
_cull_point_iterative(_root_node_id[n], r_params);
}
@@ -115,12 +150,20 @@ int cull_aabb(CullParams &r_params, bool p_translate_hits = true) {
_cull_hits.clear();
r_params.result_count = 0;
+ uint32_t tree_test_mask = 0;
+
for (int n = 0; n < NUM_TREES; n++) {
+ tree_test_mask <<= 1;
+ if (!tree_test_mask) {
+ tree_test_mask = 1;
+ }
+
if (_root_node_id[n] == BVHCommon::INVALID) {
continue;
}
- if ((n == 0) && r_params.test_pairable_only) {
+ // the tree collision mask determines which trees to collide test against
+ if (!(r_params.tree_collision_mask & tree_test_mask)) {
continue;
}
@@ -142,22 +185,6 @@ bool _cull_hits_full(const CullParams &p) {
return (int)_cull_hits.size() >= p.result_max;
}
-// write this logic once for use in all routines
-// double check this as a possible source of bugs in future.
-bool _cull_pairing_mask_test_hit(uint32_t p_maskA, uint32_t p_typeA, uint32_t p_maskB, uint32_t p_typeB) const {
- // double check this as a possible source of bugs in future.
- bool A_match_B = p_maskA & p_typeB;
-
- if (!A_match_B) {
- bool B_match_A = p_maskB & p_typeA;
- if (!B_match_A) {
- return false;
- }
- }
-
- return true;
-}
-
void _cull_hit(uint32_t p_ref_id, CullParams &p) {
// take into account masks etc
// this would be more efficient to do before plane checks,
@@ -165,7 +192,8 @@ void _cull_hit(uint32_t p_ref_id, CullParams &p) {
if (USE_PAIRS) {
const ItemExtra &ex = _extra[p_ref_id];
- if (!_cull_pairing_mask_test_hit(p.mask, p.pairable_type, ex.pairable_mask, ex.pairable_type)) {
+ // user supplied function (for e.g. pairable types and pairable masks in the render tree)
+ if (!USER_CULL_TEST_FUNCTION::user_cull_check(p.tester, ex.userdata)) {
return;
}
}
@@ -294,6 +322,7 @@ bool _cull_point_iterative(uint32_t p_node_id, CullParams &r_params) {
return true;
}
+// Note: This is a very hot loop profiling wise. Take care when changing this and profile.
bool _cull_aabb_iterative(uint32_t p_node_id, CullParams &r_params, bool p_fully_within = false) {
// our function parameters to keep on a stack
struct CullAABBParams {
@@ -336,16 +365,26 @@ bool _cull_aabb_iterative(uint32_t p_node_id, CullParams &r_params, bool p_fully
_cull_hit(child_id, r_params);
}
} else {
- for (int n = 0; n < leaf.num_items; n++) {
+ // This section is the hottest area in profiling, so
+ // is optimized highly
+ // get this into a local register and preconverted to correct type
+ int leaf_num_items = leaf.num_items;
+
+ BVHABB_CLASS swizzled_tester;
+ swizzled_tester.min = -r_params.abb.neg_max;
+ swizzled_tester.neg_max = -r_params.abb.min;
+
+ for (int n = 0; n < leaf_num_items; n++) {
const BVHABB_CLASS &aabb = leaf.get_aabb(n);
- if (aabb.intersects(r_params.abb)) {
+ if (swizzled_tester.intersects_swizzled(aabb)) {
uint32_t child_id = leaf.get_item_ref_id(n);
// register hit
_cull_hit(child_id, r_params);
}
}
+
} // not fully within
} else {
if (!cap.fully_within) {
diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc
index 896c36ecf1..2e519ceb3d 100644
--- a/core/math/bvh_debug.inc
+++ b/core/math/bvh_debug.inc
@@ -7,12 +7,12 @@ void _debug_recursive_print_tree(int p_tree_id) const {
}
String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
- Point size = aabb.calculate_size();
+ POINT size = aabb.calculate_size();
String sz;
float vol = 0.0;
- for (int i = 0; i < Point::AXES_COUNT; ++i) {
+ for (int i = 0; i < POINT::AXIS_COUNT; ++i) {
sz += "(";
sz += itos(aabb.min[i]);
sz += " ~ ";
diff --git a/core/math/bvh_logic.inc b/core/math/bvh_logic.inc
index c65002a9fd..dd3b135bb5 100644
--- a/core/math/bvh_logic.inc
+++ b/core/math/bvh_logic.inc
@@ -42,9 +42,9 @@ BVHABB_CLASS _logic_abb_merge(const BVHABB_CLASS &a, const BVHABB_CLASS &b) {
//--------------------------------------------------------------------------------------------------
/**
- * @file q3DynamicAABBTree.h
- * @author Randy Gaul
- * @date 10/10/2014
+ * @file q3DynamicAABBTree.h
+ * @author Randy Gaul
+ * @date 10/10/2014
* Copyright (c) 2014 Randy Gaul http://www.randygaul.net
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
@@ -75,11 +75,11 @@ int32_t _logic_balance(int32_t iA, uint32_t p_tree_id) {
return iA;
}
- /* A
- * / \
- * B C
- * / \ / \
- * D E F G
+ /* A
+ * / \
+ * B C
+ * / \ / \
+ * D E F G
*/
CRASH_COND(A->num_children != 2);
diff --git a/core/math/bvh_misc.inc b/core/math/bvh_misc.inc
index 71aa0e4fe0..9b35a1d36d 100644
--- a/core/math/bvh_misc.inc
+++ b/core/math/bvh_misc.inc
@@ -1,11 +1,7 @@
int _handle_get_tree_id(BVHHandle p_handle) const {
if (USE_PAIRS) {
- int tree = 0;
- if (_extra[p_handle.id()].pairable) {
- tree = 1;
- }
- return tree;
+ return _extra[p_handle.id()].tree_id;
}
return 0;
}
diff --git a/core/math/bvh_pair.inc b/core/math/bvh_pair.inc
index a12acec2b6..7b9c7ce6ae 100644
--- a/core/math/bvh_pair.inc
+++ b/core/math/bvh_pair.inc
@@ -14,10 +14,10 @@ struct ItemPairs {
void clear() {
num_pairs = 0;
extended_pairs.reset();
- expanded_aabb = Bounds();
+ expanded_aabb = BOUNDS();
}
- Bounds expanded_aabb;
+ BOUNDS expanded_aabb;
// maybe we can just use the number in the vector TODO
int32_t num_pairs;
@@ -59,4 +59,14 @@ struct ItemPairs {
return userdata;
}
+
+ // experiment : scale the pairing expansion by the number of pairs.
+ // when the number of pairs is high, the density is high and a lower collision margin is better.
+ // when there are few local pairs, a larger margin is more optimal.
+ real_t scale_expansion_margin(real_t p_margin) const {
+ real_t x = real_t(num_pairs) * (1.0 / 9.0);
+ x = MIN(x, 1.0);
+ x = 1.0 - x;
+ return p_margin * x;
+ }
};
diff --git a/core/math/bvh_public.inc b/core/math/bvh_public.inc
index 2c1e406712..36b0bfeb13 100644
--- a/core/math/bvh_public.inc
+++ b/core/math/bvh_public.inc
@@ -1,5 +1,5 @@
public:
-BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p_subindex, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask, bool p_invisible = false) {
+BVHHandle item_add(T *p_userdata, bool p_active, const BOUNDS &p_aabb, int32_t p_subindex, uint32_t p_tree_id, uint32_t p_tree_collision_mask, bool p_invisible = false) {
#ifdef BVH_VERBOSE_TREE
VERBOSE_PRINT("\nitem_add BEFORE");
_debug_recursive_print_tree(0);
@@ -9,6 +9,13 @@ BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p
BVHABB_CLASS abb;
abb.from(p_aabb);
+ // NOTE that we do not expand the AABB for the first create even if
+ // leaf expansion is switched on. This is for two reasons:
+ // (1) We don't know if this object will move in future, in which case a non-expanded
+ // bound would be better...
+ // (2) We don't yet know how many objects will be paired, which is used to modify
+ // the expansion margin.
+
// handle to be filled with the new item ref
BVHHandle handle;
@@ -40,29 +47,17 @@ BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p
extra->active_ref_id = _active_refs.size();
_active_refs.push_back(ref_id);
- if (USE_PAIRS) {
- extra->pairable_mask = p_pairable_mask;
- extra->pairable_type = p_pairable_type;
- extra->pairable = p_pairable;
- } else {
- // just for safety, in case this gets queried etc
- extra->pairable = 0;
- p_pairable = false;
- }
+ extra->tree_id = p_tree_id;
+ extra->tree_collision_mask = p_tree_collision_mask;
// assign to handle to return
handle.set_id(ref_id);
- uint32_t tree_id = 0;
- if (p_pairable) {
- tree_id = 1;
- }
-
- create_root_node(tree_id);
+ create_root_node(p_tree_id);
// we must choose where to add to tree
if (p_active) {
- ref->tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
+ ref->tnode_id = _logic_choose_item_add_node(_root_node_id[p_tree_id], abb);
bool refit = _node_add_item(ref->tnode_id, ref_id, abb);
@@ -70,7 +65,7 @@ BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p
// only need to refit from the parent
const TNode &add_node = _nodes[ref->tnode_id];
if (add_node.parent_id != BVHCommon::INVALID) {
- refit_upward_and_balance(add_node.parent_id, tree_id);
+ refit_upward_and_balance(add_node.parent_id, p_tree_id);
}
}
} else {
@@ -103,7 +98,7 @@ void _debug_print_refs() {
}
// returns false if noop
-bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
+bool item_move(BVHHandle p_handle, const BOUNDS &p_aabb) {
uint32_t ref_id = p_handle.id();
// get the reference
@@ -115,10 +110,19 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
BVHABB_CLASS abb;
abb.from(p_aabb);
+#ifdef BVH_EXPAND_LEAF_AABBS
+ if (USE_PAIRS) {
+ // scale the pairing expansion by the number of pairs.
+ abb.expand(_pairs[ref_id].scale_expansion_margin(_pairing_expansion));
+ } else {
+ abb.expand(_pairing_expansion);
+ }
+#endif
+
BVH_ASSERT(ref.tnode_id != BVHCommon::INVALID);
TNode &tnode = _nodes[ref.tnode_id];
- // does it fit within the current aabb?
+ // does it fit within the current leaf aabb?
if (tnode.aabb.is_other_within(abb)) {
// do nothing .. fast path .. not moved enough to need refit
@@ -129,9 +133,24 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
BVHABB_CLASS &leaf_abb = leaf.get_aabb(ref.item_id);
// no change?
+#ifdef BVH_EXPAND_LEAF_AABBS
+ BOUNDS leaf_aabb;
+ leaf_abb.to(leaf_aabb);
+
+ // This test should pass in a lot of cases, and by returning false we can avoid
+ // collision pairing checks later, which greatly reduces processing.
+ if (expanded_aabb_encloses_not_shrink(leaf_aabb, p_aabb)) {
+ return false;
+ }
+#else
if (leaf_abb == abb) {
return false;
}
+#endif
+
+#ifdef BVH_VERBOSE_MOVES
+ print_line("item_move " + itos(p_handle.id()) + "(within tnode aabb) : " + _debug_aabb_to_string(abb));
+#endif
leaf_abb = abb;
_integrity_check_all();
@@ -139,6 +158,10 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
return true;
}
+#ifdef BVH_VERBOSE_MOVES
+ print_line("item_move " + itos(p_handle.id()) + "(outside tnode aabb) : " + _debug_aabb_to_string(abb));
+#endif
+
uint32_t tree_id = _handle_get_tree_id(p_handle);
// remove and reinsert
@@ -206,7 +229,7 @@ void item_remove(BVHHandle p_handle) {
}
// returns success
-bool item_activate(BVHHandle p_handle, const Bounds &p_aabb) {
+bool item_activate(BVHHandle p_handle, const BOUNDS &p_aabb) {
uint32_t ref_id = p_handle.id();
ItemRef &ref = _refs[ref_id];
if (ref.is_active()) {
@@ -260,12 +283,14 @@ void item_fill_cullparams(BVHHandle p_handle, CullParams &r_params) const {
uint32_t ref_id = p_handle.id();
const ItemExtra &extra = _extra[ref_id];
- // testing from a non pairable item, we only want to test pairable items
- r_params.test_pairable_only = extra.pairable == 0;
+ // which trees does this item want to collide detect against?
+ r_params.tree_collision_mask = extra.tree_collision_mask;
- // we take into account the mask of the item testing from
- r_params.mask = extra.pairable_mask;
- r_params.pairable_type = extra.pairable_type;
+ // The testing user defined object is passed to the user defined cull check function
+ // for masks etc. This is usually a dummy object of type T with masks set.
+ // However, if not using the cull_check callback (i.e. returning true), you can pass
+ // a nullptr instead of dummy object, as it will not be used.
+ r_params.tester = extra.userdata;
}
bool item_is_pairable(const BVHHandle &p_handle) {
@@ -285,7 +310,7 @@ void item_get_ABB(const BVHHandle &p_handle, BVHABB_CLASS &r_abb) {
r_abb = leaf.get_aabb(ref.item_id);
}
-bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pairable_type, uint32_t p_pairable_mask) {
+bool item_set_tree(const BVHHandle &p_handle, uint32_t p_tree_id, uint32_t p_tree_collision_mask) {
// change tree?
uint32_t ref_id = p_handle.id();
@@ -293,13 +318,15 @@ bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
ItemRef &ref = _refs[ref_id];
bool active = ref.is_active();
- bool pairable_changed = (ex.pairable != 0) != p_pairable;
- bool state_changed = pairable_changed || (ex.pairable_type != p_pairable_type) || (ex.pairable_mask != p_pairable_mask);
+ bool tree_changed = ex.tree_id != p_tree_id;
+ bool mask_changed = ex.tree_collision_mask != p_tree_collision_mask;
+ bool state_changed = tree_changed | mask_changed;
- ex.pairable_type = p_pairable_type;
- ex.pairable_mask = p_pairable_mask;
+ // Keep an eye on this for bugs of not noticing changes to objects,
+ // especially when changing client user masks that will not be detected as a change
+ // in the BVH. You may need to force a collision check in this case with recheck_pairs().
- if (active && pairable_changed) {
+ if (active && (tree_changed | mask_changed)) {
// record abb
TNode &tnode = _nodes[ref.tnode_id];
TLeaf &leaf = _node_get_leaf(tnode);
@@ -313,7 +340,8 @@ bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
// we must set the pairable AFTER getting the current tree
// because the pairable status determines which tree
- ex.pairable = p_pairable;
+ ex.tree_id = p_tree_id;
+ ex.tree_collision_mask = p_tree_collision_mask;
// add to new tree
tree_id = _handle_get_tree_id(p_handle);
@@ -333,7 +361,8 @@ bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
}
} else {
// always keep this up to date
- ex.pairable = p_pairable;
+ ex.tree_id = p_tree_id;
+ ex.tree_collision_mask = p_tree_collision_mask;
}
return state_changed;
@@ -403,7 +432,7 @@ void update() {
// if there are no nodes, do nothing, but if there are...
if (bound_valid) {
- Bounds bb;
+ BOUNDS bb;
world_bound.to(bb);
real_t size = bb.get_longest_axis_size();
@@ -421,3 +450,50 @@ void update() {
}
#endif
}
+
+void params_set_pairing_expansion(real_t p_value) {
+ if (p_value < 0.0) {
+#ifdef BVH_ALLOW_AUTO_EXPANSION
+ _auto_pairing_expansion = true;
+#endif
+ return;
+ }
+#ifdef BVH_ALLOW_AUTO_EXPANSION
+ _auto_pairing_expansion = false;
+#endif
+
+ _pairing_expansion = p_value;
+
+ // calculate shrinking threshold
+ const real_t fudge_factor = 1.1;
+ _aabb_shrinkage_threshold = _pairing_expansion * POINT::AXIS_COUNT * 2.0 * fudge_factor;
+}
+
+// This routine is not just an enclose check, it also checks for special case of shrinkage
+bool expanded_aabb_encloses_not_shrink(const BOUNDS &p_expanded_aabb, const BOUNDS &p_aabb) const {
+ if (!p_expanded_aabb.encloses(p_aabb)) {
+ return false;
+ }
+
+ // Check for special case of shrinkage. If the aabb has shrunk
+ // significantly we want to create a new expanded bound, because
+ // the previous expanded bound will have diverged significantly.
+ const POINT &exp_size = p_expanded_aabb.size;
+ const POINT &new_size = p_aabb.size;
+
+ real_t exp_l = 0.0;
+ real_t new_l = 0.0;
+
+ for (int i = 0; i < POINT::AXIS_COUNT; ++i) {
+ exp_l += exp_size[i];
+ new_l += new_size[i];
+ }
+
+ // is difference above some metric
+ real_t diff = exp_l - new_l;
+ if (diff < _aabb_shrinkage_threshold) {
+ return true;
+ }
+
+ return false;
+}
diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc
index f19ee8a7da..ff07166d4a 100644
--- a/core/math/bvh_split.inc
+++ b/core/math/bvh_split.inc
@@ -25,16 +25,16 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
return;
}
- Point centre = full_bound.calculate_centre();
- Point size = full_bound.calculate_size();
+ POINT centre = full_bound.calculate_centre();
+ POINT size = full_bound.calculate_size();
- int order[Point::AXIS_COUNT];
+ int order[POINT::AXIS_COUNT];
order[0] = size.min_axis_index();
- order[Point::AXIS_COUNT - 1] = size.max_axis_index();
+ order[POINT::AXIS_COUNT - 1] = size.max_axis_index();
- static_assert(Point::AXIS_COUNT <= 3);
- if (Point::AXIS_COUNT == 3) {
+ static_assert(POINT::AXIS_COUNT <= 3, "BVH POINT::AXIS_COUNT has unexpected size");
+ if (POINT::AXIS_COUNT == 3) {
order[1] = 3 - (order[0] + order[2]);
}
@@ -58,7 +58,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
// detect when split on longest axis failed
int min_threshold = MAX_ITEMS / 4;
- int min_group_size[Point::AXIS_COUNT];
+ int min_group_size[POINT::AXIS_COUNT];
min_group_size[0] = MIN(num_a, num_b);
if (min_group_size[0] < min_threshold) {
// slow but sure .. first move everything back into a
@@ -68,7 +68,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
num_b = 0;
// now calculate the best split
- for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
+ for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
split_axis = order[axis];
int count = 0;
@@ -86,7 +86,7 @@ void _split_leaf_sort_groups_simple(int &num_a, int &num_b, uint16_t *group_a, u
// best axis
int best_axis = 0;
int best_min = min_group_size[0];
- for (int axis = 1; axis < Point::AXIS_COUNT; axis++) {
+ for (int axis = 1; axis < POINT::AXIS_COUNT; axis++) {
if (min_group_size[axis] > best_min) {
best_min = min_group_size[axis];
best_axis = axis;
diff --git a/core/math/bvh_structs.inc b/core/math/bvh_structs.inc
index 1d1e0e6468..b0d9ae3615 100644
--- a/core/math/bvh_structs.inc
+++ b/core/math/bvh_structs.inc
@@ -14,25 +14,38 @@ struct ItemRef {
// extra info kept in separate parallel list to the references,
// as this is less used as keeps cache better
struct ItemExtra {
- uint32_t last_updated_tick;
- uint32_t pairable;
- uint32_t pairable_mask;
- uint32_t pairable_type;
+ // Before doing user defined pairing checks (especially in the find_leavers function),
+ // we may want to check that two items have compatible tree ids and tree masks,
+ // as if they are incompatible they should not pair / collide.
+ bool are_item_trees_compatible(const ItemExtra &p_other) const {
+ uint32_t other_type = 1 << p_other.tree_id;
+ if (tree_collision_mask & other_type) {
+ return true;
+ }
+ uint32_t our_type = 1 << tree_id;
+ if (p_other.tree_collision_mask & our_type) {
+ return true;
+ }
+ return false;
+ }
+
+ // There can be multiple user defined trees
+ uint32_t tree_id;
+ // Defines which trees this item should collision check against.
+ // 1 << tree_id, and normally items would collide against there own
+ // tree (but not always).
+ uint32_t tree_collision_mask;
+
+ uint32_t last_updated_tick;
int32_t subindex;
+ T *userdata;
+
// the active reference is a separate list of which references
// are active so that we can slowly iterate through it over many frames for
// slow optimize.
uint32_t active_ref_id;
-
- T *userdata;
-};
-
-// this is an item OR a child node depending on whether a leaf node
-struct Item {
- BVHABB_CLASS aabb;
- uint32_t item_ref_id;
};
// tree leaf
@@ -133,13 +146,13 @@ struct TNode {
// instead of using linked list we maintain
// item references (for quick lookup)
-PooledList<ItemRef, true> _refs;
-PooledList<ItemExtra, true> _extra;
+PooledList<ItemRef, uint32_t, true> _refs;
+PooledList<ItemExtra, uint32_t, true> _extra;
PooledList<ItemPairs> _pairs;
// these 2 are not in sync .. nodes != leaves!
-PooledList<TNode, true> _nodes;
-PooledList<TLeaf, true> _leaves;
+PooledList<TNode, uint32_t, true> _nodes;
+PooledList<TLeaf, uint32_t, true> _leaves;
// we can maintain an un-ordered list of which references are active,
// in order to do a slow incremental optimize of the tree over each frame.
@@ -152,15 +165,11 @@ uint32_t _current_active_ref = 0;
// for pairing collision detection
LocalVector<uint32_t, uint32_t, true> _cull_hits;
-// we now have multiple root nodes, allowing us to store
-// more than 1 tree. This can be more efficient, while sharing the same
-// common lists
-enum { NUM_TREES = 2,
-};
-
-// Tree 0 - Non pairable
-// Tree 1 - Pairable
-// This is more efficient because in physics we only need check non pairable against the pairable tree.
+// We can now have a user definable number of trees.
+// This allows using e.g. a non-pairable and pairable tree,
+// which can be more efficient for example, if we only need check non pairable against the pairable tree.
+// It also may be more efficient in terms of separating static from dynamic objects, by reducing housekeeping.
+// However this is a trade off, as there is a cost of traversing two trees.
uint32_t _root_node_id[NUM_TREES];
// these values may need tweaking according to the project
@@ -177,4 +186,14 @@ bool _auto_node_expansion = true;
// larger values gives more 'sticky' pairing, and is less likely to exhibit tunneling
// we can either use auto mode, where the expansion is based on the root node size, or specify manually
real_t _pairing_expansion = 0.1;
+
+#ifdef BVH_ALLOW_AUTO_EXPANSION
bool _auto_pairing_expansion = true;
+#endif
+
+// when using an expanded bound, we must detect the condition where a new AABB
+// is significantly smaller than the expanded bound, as this is a special case where we
+// should override the optimization and create a new expanded bound.
+// This threshold is derived from the _pairing_expansion, and should be recalculated
+// if _pairing_expansion is changed.
+real_t _aabb_shrinkage_threshold = 0.0;
diff --git a/core/math/bvh_tree.h b/core/math/bvh_tree.h
index c948d83456..da9b307778 100644
--- a/core/math/bvh_tree.h
+++ b/core/math/bvh_tree.h
@@ -48,12 +48,17 @@
#include "core/templates/pooled_list.h"
#include <limits.h>
-#define BVHABB_CLASS BVH_ABB<Bounds, Point>
+#define BVHABB_CLASS BVH_ABB<BOUNDS, POINT>
+
+// not sure if this is better yet so making optional
+#define BVH_EXPAND_LEAF_AABBS
// never do these checks in release
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
//#define BVH_VERBOSE
//#define BVH_VERBOSE_TREE
+//#define BVH_VERBOSE_PAIRING
+//#define BVH_VERBOSE_MOVES
//#define BVH_VERBOSE_FRAME
//#define BVH_CHECKS
@@ -148,7 +153,25 @@ public:
}
};
-template <class T, int MAX_CHILDREN, int MAX_ITEMS, bool USE_PAIRS = false, class Bounds = AABB, class Point = Vector3>
+template <class T>
+class BVH_DummyPairTestFunction {
+public:
+ static bool user_collision_check(T *p_a, T *p_b) {
+ // return false if no collision, decided by masks etc
+ return true;
+ }
+};
+
+template <class T>
+class BVH_DummyCullTestFunction {
+public:
+ static bool user_cull_check(T *p_a, T *p_b) {
+ // return false if no collision
+ return true;
+ }
+};
+
+template <class T, int NUM_TREES, int MAX_CHILDREN, int MAX_ITEMS, class USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, class USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, bool USE_PAIRS = false, class BOUNDS = AABB, class POINT = Vector3>
class BVH_Tree {
friend class BVH;
@@ -165,6 +188,11 @@ public:
// (as these ids are stored as negative numbers in the node)
uint32_t dummy_leaf_id;
_leaves.request(dummy_leaf_id);
+
+ // In many cases you may want to change this default in the client code,
+ // or expose this value to the user.
+ // This default may make sense for a typically scaled 3d game, but maybe not for 2d on a pixel scale.
+ params_set_pairing_expansion(0.1);
}
private:
@@ -234,7 +262,7 @@ private:
change_root_node(sibling_id, p_tree_id);
// delete the old root node as no longer needed
- _nodes.free(p_parent_id);
+ node_free_node_and_leaf(p_parent_id);
}
return;
@@ -247,7 +275,19 @@ private:
}
// put the node on the free list to recycle
- _nodes.free(p_parent_id);
+ node_free_node_and_leaf(p_parent_id);
+ }
+
+ // A node can either be a node, or a node AND a leaf combo.
+ // Both must be deleted to prevent a leak.
+ void node_free_node_and_leaf(uint32_t p_node_id) {
+ TNode &node = _nodes[p_node_id];
+ if (node.is_leaf()) {
+ int leaf_id = node.get_leaf_id();
+ _leaves.free(leaf_id);
+ }
+
+ _nodes.free(p_node_id);
}
void change_root_node(uint32_t p_new_root_id, uint32_t p_tree_id) {
@@ -339,7 +379,7 @@ private:
refit_upward(parent_id);
// put the node on the free list to recycle
- _nodes.free(owner_node_id);
+ node_free_node_and_leaf(owner_node_id);
}
// else if no parent, it is the root node. Do not delete
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 0ddac9744e..9dd1257474 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -1440,7 +1440,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
}
Callable::CallError ce;
- base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
+ base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = vformat(RTR("On call to '%s':"), String(call->method));
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 8c0b87cf4a..44340b97ae 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -322,7 +322,7 @@ public:
// double only, as these functions are mainly used by the editor and not performance-critical,
static double ease(double p_x, double p_c);
static int step_decimals(double p_step);
- static int range_step_decimals(double p_step);
+ static int range_step_decimals(double p_step); // For editor use only.
static double snapped(double p_value, double p_step);
static uint32_t larger_prime(uint32_t p_val);
diff --git a/core/math/vector2.h b/core/math/vector2.h
index a2680b84fc..bd67299f33 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -31,6 +31,7 @@
#ifndef VECTOR2_H
#define VECTOR2_H
+#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
class String;
@@ -60,9 +61,11 @@ struct _NO_DISCARD_ Vector2 {
};
_FORCE_INLINE_ real_t &operator[](int p_idx) {
+ DEV_ASSERT((unsigned int)p_idx < 2);
return coord[p_idx];
}
_FORCE_INLINE_ const real_t &operator[](int p_idx) const {
+ DEV_ASSERT((unsigned int)p_idx < 2);
return coord[p_idx];
}
diff --git a/core/math/vector2i.h b/core/math/vector2i.h
index 3f5f12d4dd..13b70031bd 100644
--- a/core/math/vector2i.h
+++ b/core/math/vector2i.h
@@ -31,6 +31,7 @@
#ifndef VECTOR2I_H
#define VECTOR2I_H
+#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
class String;
@@ -58,9 +59,11 @@ struct _NO_DISCARD_ Vector2i {
};
_FORCE_INLINE_ int32_t &operator[](int p_idx) {
+ DEV_ASSERT((unsigned int)p_idx < 2);
return coord[p_idx];
}
_FORCE_INLINE_ const int32_t &operator[](int p_idx) const {
+ DEV_ASSERT((unsigned int)p_idx < 2);
return coord[p_idx];
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 89b0095741..b22ebeaf0a 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -59,10 +59,12 @@ struct _NO_DISCARD_ Vector3 {
};
_FORCE_INLINE_ const real_t &operator[](const int p_axis) const {
+ DEV_ASSERT((unsigned int)p_axis < 3);
return coord[p_axis];
}
_FORCE_INLINE_ real_t &operator[](const int p_axis) {
+ DEV_ASSERT((unsigned int)p_axis < 3);
return coord[p_axis];
}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 2a4c7e2e97..b49c1142ed 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -31,6 +31,7 @@
#ifndef VECTOR3I_H
#define VECTOR3I_H
+#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
class String;
@@ -54,10 +55,12 @@ struct _NO_DISCARD_ Vector3i {
};
_FORCE_INLINE_ const int32_t &operator[](const int p_axis) const {
+ DEV_ASSERT((unsigned int)p_axis < 3);
return coord[p_axis];
}
_FORCE_INLINE_ int32_t &operator[](const int p_axis) {
+ DEV_ASSERT((unsigned int)p_axis < 3);
return coord[p_axis];
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index c29316c089..08e12dfcaa 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -557,6 +557,19 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance));
}
+bool ClassDB::is_virtual(const StringName &p_class) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *ti = classes.getptr(p_class);
+ ERR_FAIL_COND_V_MSG(!ti, false, "Cannot get class '" + String(p_class) + "'.");
+#ifdef TOOLS_ENABLED
+ if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
+#endif
+ return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance) && ti->is_virtual);
+}
+
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
OBJTYPE_WLOCK;
@@ -1197,7 +1210,7 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const
if (psg->_setptr) {
psg->_setptr->call(p_object, arg, 2, ce);
} else {
- p_object->call(psg->setter, arg, 2, ce);
+ p_object->callp(psg->setter, arg, 2, ce);
}
} else {
@@ -1205,7 +1218,7 @@ bool ClassDB::set_property(Object *p_object, const StringName &p_property, const
if (psg->_setptr) {
psg->_setptr->call(p_object, arg, 1, ce);
} else {
- p_object->call(psg->setter, arg, 1, ce);
+ p_object->callp(psg->setter, arg, 1, ce);
}
}
@@ -1238,14 +1251,14 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia
Variant index = psg->index;
const Variant *arg[1] = { &index };
Callable::CallError ce;
- r_value = p_object->call(psg->getter, arg, 1, ce);
+ r_value = p_object->callp(psg->getter, arg, 1, ce);
} else {
Callable::CallError ce;
if (psg->_getptr) {
r_value = psg->_getptr->call(p_object, nullptr, 0, ce);
} else {
- r_value = p_object->call(psg->getter, nullptr, 0, ce);
+ r_value = p_object->callp(psg->getter, nullptr, 0, ce);
}
}
return true;
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 5d258a29bf..580ae3582f 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -118,6 +118,7 @@ public:
StringName name;
bool disabled = false;
bool exposed = false;
+ bool is_virtual = false;
Object *(*creation_func)() = nullptr;
ClassInfo() {}
@@ -156,20 +157,21 @@ public:
}
template <class T>
- static void register_class() {
+ static void register_class(bool p_virtual = false) {
GLOBAL_LOCK_FUNCTION;
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_COND(!t);
t->creation_func = &creator<T>;
t->exposed = true;
+ t->is_virtual = p_virtual;
t->class_ptr = T::get_class_ptr_static();
t->api = current_api;
T::register_custom_data_to_otdb();
}
template <class T>
- static void register_virtual_class() {
+ static void register_abstract_class() {
GLOBAL_LOCK_FUNCTION;
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
@@ -210,6 +212,7 @@ public:
static bool class_exists(const StringName &p_class);
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
static bool can_instantiate(const StringName &p_class);
+ static bool is_virtual(const StringName &p_class);
static Object *instantiate(const StringName &p_class);
static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance);
@@ -436,9 +439,13 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) {
if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
::ClassDB::register_class<m_class>(); \
}
-#define GDREGISTER_VIRTUAL_CLASS(m_class) \
- if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
- ::ClassDB::register_virtual_class<m_class>(); \
+#define GDREGISTER_VIRTUAL_CLASS(m_class) \
+ if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
+ ::ClassDB::register_class<m_class>(true); \
+ }
+#define GDREGISTER_ABSTRACT_CLASS(m_class) \
+ if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \
+ ::ClassDB::register_abstract_class<m_class>(); \
}
#include "core/disabled_classes.gen.h"
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 5de1b49026..2e909b8042 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -3,12 +3,13 @@ proto = """
StringName _gdvirtual_##m_name##_sn = #m_name;\\
mutable bool _gdvirtual_##m_name##_initialized = false;\\
mutable GDNativeExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\
+template<bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
ScriptInstance *script_instance = ((Object*)(this))->get_script_instance();\\
if (script_instance) {\\
Callable::CallError ce; \\
$CALLSIARGS\\
- $CALLSIBEGINscript_instance->call(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
+ $CALLSIBEGINscript_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
if (ce.error == Callable::CallError::CALL_OK) {\\
$CALLSIRET\\
return true;\\
@@ -25,6 +26,10 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
$CALLPTRRET\\
return true;\\
}\\
+ \\
+ if (required) {\\
+ WARN_PRINT_ONCE("Required virtual method: "+get_class()+"::" + #m_name + " must be overriden before calling.");\\
+ }\\
\\
return false;\\
}\\
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 3c828eabd9..79c36ac81f 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/core_string_names.h"
+#include "core/object/class_db.h"
#include "core/object/script_language.h"
MessageQueue *MessageQueue::singleton = nullptr;
@@ -40,23 +41,8 @@ MessageQueue *MessageQueue::get_singleton() {
return singleton;
}
-Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
- return push_callable(Callable(p_id, p_method), p_args, p_argcount, p_show_error);
-}
-
-Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
-
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- return push_call(p_id, p_method, argptr, argc, false);
+Error MessageQueue::push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
+ return push_callablep(Callable(p_id, p_method), p_args, p_argcount, p_show_error);
}
Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value) {
@@ -113,8 +99,8 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {
return OK;
}
-Error MessageQueue::push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
- return push_call(p_object->get_instance_id(), p_method, VARIANT_ARG_PASS);
+Error MessageQueue::push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error) {
+ return push_callp(p_object->get_instance_id(), p_method, p_args, p_argcount, p_show_error);
}
Error MessageQueue::push_notification(Object *p_object, int p_notification) {
@@ -125,7 +111,7 @@ Error MessageQueue::push_set(Object *p_object, const StringName &p_prop, const V
return push_set(p_object->get_instance_id(), p_prop, p_value);
}
-Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) {
+Error MessageQueue::push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error) {
_THREAD_SAFE_METHOD_
int room_needed = sizeof(Message) + sizeof(Variant) * p_argcount;
@@ -155,21 +141,6 @@ Error MessageQueue::push_callable(const Callable &p_callable, const Variant **p_
return OK;
}
-Error MessageQueue::push_callable(const Callable &p_callable, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
-
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- return push_callable(p_callable, argptr, argc);
-}
-
void MessageQueue::statistics() {
Map<StringName, int> set_count;
Map<int, int> notify_count;
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
index a4449cf473..eaab01d0aa 100644
--- a/core/object/message_queue.h
+++ b/core/object/message_queue.h
@@ -31,8 +31,11 @@
#ifndef MESSAGE_QUEUE_H
#define MESSAGE_QUEUE_H
-#include "core/object/class_db.h"
+#include "core/object/object_id.h"
#include "core/os/thread_safe.h"
+#include "core/variant/variant.h"
+
+class Object;
class MessageQueue {
_THREAD_SAFE_CLASS_
@@ -73,14 +76,42 @@ class MessageQueue {
public:
static MessageQueue *get_singleton();
- Error push_call(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
- Error push_call(ObjectID p_id, const StringName &p_method, VARIANT_ARG_LIST);
+ Error push_callp(ObjectID p_id, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ template <typename... VarArgs>
+ Error push_call(ObjectID p_id, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ return push_callp(p_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
Error push_notification(ObjectID p_id, int p_notification);
Error push_set(ObjectID p_id, const StringName &p_prop, const Variant &p_value);
- Error push_callable(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false);
- Error push_callable(const Callable &p_callable, VARIANT_ARG_LIST);
+ Error push_callablep(const Callable &p_callable, const Variant **p_args, int p_argcount, bool p_show_error = false);
+
+ template <typename... VarArgs>
+ Error push_callable(const Callable &p_callable, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ return push_callablep(p_callable, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
+ Error push_callp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
+ template <typename... VarArgs>
+ Error push_call(Object *p_object, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ return push_callp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
- Error push_call(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
Error push_notification(Object *p_object, int p_notification);
Error push_set(Object *p_object, const StringName &p_prop, const Variant &p_value);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index a8a49bd22e..096edd4e60 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -624,8 +624,12 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
}
if (_extension) {
- p_list->push_back(PropertyInfo(Variant::NIL, _extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
- ClassDB::get_property_list(_extension->class_name, p_list, true, this);
+ const ObjectNativeExtension *current_extension = _extension;
+ while (current_extension) {
+ p_list->push_back(PropertyInfo(Variant::NIL, current_extension->class_name, PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+ ClassDB::get_property_list(current_extension->class_name, p_list, true, this);
+ current_extension = current_extension->parent;
+ }
}
if (_extension && _extension->get_property_list) {
@@ -679,7 +683,7 @@ Variant Object::_call_bind(const Variant **p_args, int p_argcount, Callable::Cal
StringName method = *p_args[0];
- return call(method, &p_args[1], p_argcount - 1, r_error);
+ return callp(method, &p_args[1], p_argcount - 1, r_error);
}
Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@@ -700,7 +704,7 @@ Variant Object::_call_deferred_bind(const Variant **p_args, int p_argcount, Call
StringName method = *p_args[0];
- MessageQueue::get_singleton()->push_call(get_instance_id(), method, &p_args[1], p_argcount - 1, true);
+ MessageQueue::get_singleton()->push_callp(get_instance_id(), method, &p_args[1], p_argcount - 1, true);
return Variant();
}
@@ -750,31 +754,14 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) {
}
Callable::CallError ce;
- Variant ret = call(p_method, argptrs, p_args.size(), ce);
+ Variant ret = callp(p_method, argptrs, p_args.size(), ce);
if (ce.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
}
return ret;
}
-Variant Object::call(const StringName &p_name, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- Callable::CallError error;
-
- Variant ret = call(p_name, argptr, argc, error);
- return ret;
-}
-
-Variant Object::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK;
if (p_method == CoreStringNames::get_singleton()->_free) {
@@ -808,7 +795,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
OBJ_DEBUG_LOCK
if (script_instance) {
- ret = script_instance->call(p_method, p_args, p_argcount, r_error);
+ ret = script_instance->callp(p_method, p_args, p_argcount, r_error);
//force jumptable
switch (r_error.error) {
case Callable::CallError::CALL_OK:
@@ -1027,12 +1014,12 @@ Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::C
args = &p_args[1];
}
- emit_signal(signal, args, argc);
+ emit_signalp(signal, args, argc);
return Variant();
}
-Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) {
+Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) {
if (_block_signals) {
return ERR_CANT_ACQUIRE_RESOURCE; //no emit, signals blocked
}
@@ -1091,7 +1078,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
}
if (c.flags & CONNECT_DEFERRED) {
- MessageQueue::get_singleton()->push_callable(c.callable, args, argc, true);
+ MessageQueue::get_singleton()->push_callablep(c.callable, args, argc, true);
} else {
Callable::CallError ce;
_emitting = true;
@@ -1139,21 +1126,6 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
return err;
}
-Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
-
- int argc = 0;
-
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- return emit_signal(p_name, argptr, argc);
-}
-
void Object::_add_user_signal(const String &p_name, const Array &p_args) {
// this version of add_user_signal is meant to be used from scripts or external apis
// without access to ADD_SIGNAL in bind_methods
@@ -1648,10 +1620,6 @@ void Object::_bind_methods() {
BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED);
}
-void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) {
- MessageQueue::get_singleton()->push_call(this, p_method, VARIANT_ARG_PASS);
-}
-
void Object::set_deferred(const StringName &p_property, const Variant &p_value) {
MessageQueue::get_singleton()->push_set(this, p_property, p_value);
}
diff --git a/core/object/object.h b/core/object/object.h
index b5be1cf0e7..6b4f1c81e6 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -32,6 +32,7 @@
#define OBJECT_H
#include "core/extension/gdnative_interface.h"
+#include "core/object/message_queue.h"
#include "core/object/object_id.h"
#include "core/os/rw_lock.h"
#include "core/os/spin_lock.h"
@@ -44,14 +45,6 @@
#include "core/variant/callable_bind.h"
#include "core/variant/variant.h"
-#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant()
-#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5, p_arg6, p_arg7, p_arg8
-#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8
-#define VARIANT_ARG_MAX 8
-#define VARIANT_ARGPTRS const Variant *argptr[8] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5, &p_arg6, &p_arg7, &p_arg8 };
-#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4], *argptr[5], *argptr[6]], *argptr[7]
-#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4], m_arr[5], m_arr[6], m_arr[7]
-
enum PropertyHint {
PROPERTY_HINT_NONE, ///< no hint provided.
PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range.
@@ -95,6 +88,7 @@ enum PropertyHint {
PROPERTY_HINT_ARRAY_TYPE,
PROPERTY_HINT_INT_IS_POINTER,
PROPERTY_HINT_LOCALE_ID,
+ PROPERTY_HINT_LOCALIZABLE_STRING,
PROPERTY_HINT_MAX,
// When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit
};
@@ -262,6 +256,7 @@ struct ObjectNativeExtension {
GDNativeExtensionClassToString to_string;
GDNativeExtensionClassReference reference;
GDNativeExtensionClassReference unreference;
+ GDNativeExtensionClassGetRID get_rid;
_FORCE_INLINE_ bool is_class(const String &p_class) const {
const ObjectNativeExtension *e = this;
@@ -280,8 +275,12 @@ struct ObjectNativeExtension {
GDNativeExtensionClassGetVirtual get_virtual;
};
-#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__)
-#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__)
+#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
+#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__)
+
+#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__)
+#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__VA_ARGS__)
+
#ifdef DEBUG_METHODS_ENABLED
#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__));
#else
@@ -734,8 +733,18 @@ public:
bool has_method(const StringName &p_method) const;
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- Variant call(const StringName &p_name, VARIANT_ARG_LIST); // C++ helper
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+
+ template <typename... VarArgs>
+ Variant call(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ Callable::CallError cerr;
+ return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
+ }
void notification(int p_notification, bool p_reversed = false);
virtual String to_string();
@@ -769,8 +778,18 @@ public:
void set_script_and_instance(const Variant &p_script, ScriptInstance *p_instance);
void add_user_signal(const MethodInfo &p_signal);
- Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST);
- Error emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount);
+
+ template <typename... VarArgs>
+ Error emit_signal(const StringName &p_name, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
+ Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount);
bool has_signal(const StringName &p_name) const;
void get_signal_list(List<MethodInfo> *p_signals) const;
void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
@@ -782,7 +801,11 @@ public:
void disconnect(const StringName &p_signal, const Callable &p_callable);
bool is_connected(const StringName &p_signal, const Callable &p_callable) const;
- void call_deferred(const StringName &p_method, VARIANT_ARG_LIST);
+ template <typename... VarArgs>
+ void call_deferred(const StringName &p_name, VarArgs... p_args) {
+ MessageQueue::get_singleton()->push_call(this, p_name, p_args...);
+ }
+
void set_deferred(const StringName &p_property, const Variant &p_value);
void set_block_signals(bool p_block);
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index b14296b815..11440c37fe 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -310,20 +310,6 @@ void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state)
}
}
-Variant ScriptInstance::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- Callable::CallError error;
- return call(p_method, argptr, argc, error);
-}
-
void ScriptInstance::property_set_fallback(const StringName &, const Variant &, bool *r_valid) {
if (r_valid) {
*r_valid = false;
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 4b18d9a5e8..2122f785b6 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -176,8 +176,20 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const = 0;
virtual bool has_method(const StringName &p_method) const = 0;
- virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST);
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
+
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
+
+ template <typename... VarArgs>
+ Variant call(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ Callable::CallError cerr;
+ return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
+ }
+
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid) {
@@ -419,8 +431,8 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
- virtual Variant call(const StringName &p_method, VARIANT_ARG_LIST) { return Variant(); }
- virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+
+ virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
}
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index b78328fb42..ee8eb97a93 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -126,8 +126,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
force_keep_in_merge_ends = false;
}
-void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS
+void UndoRedo::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
ERR_FAIL_COND(p_object == nullptr);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
@@ -140,14 +139,13 @@ void UndoRedo::add_do_method(Object *p_object, const StringName &p_method, VARIA
do_op.type = Operation::TYPE_METHOD;
do_op.name = p_method;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- do_op.args[i] = *argptr[i];
+ for (int i = 0; i < p_argcount; i++) {
+ do_op.args.push_back(*p_args[i]);
}
actions.write[current_action + 1].do_ops.push_back(do_op);
}
-void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS
+void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) {
ERR_FAIL_COND(p_object == nullptr);
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
@@ -167,8 +165,8 @@ void UndoRedo::add_undo_method(Object *p_object, const StringName &p_method, VAR
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_method;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- undo_op.args[i] = *argptr[i];
+ for (int i = 0; i < p_argcount; i++) {
+ undo_op.args.push_back(*p_args[i]);
}
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}
@@ -185,7 +183,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c
do_op.type = Operation::TYPE_PROPERTY;
do_op.name = p_property;
- do_op.args[0] = p_value;
+ do_op.args.push_back(p_value);
actions.write[current_action + 1].do_ops.push_back(do_op);
}
@@ -208,7 +206,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property,
undo_op.type = Operation::TYPE_PROPERTY;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_property;
- undo_op.args[0] = p_value;
+ undo_op.args.push_back(p_value);
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}
@@ -314,23 +312,18 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
switch (op.type) {
case Operation::TYPE_METHOD: {
+ int argc = op.args.size();
Vector<const Variant *> argptrs;
- argptrs.resize(VARIANT_ARG_MAX);
- int argc = 0;
+ argptrs.resize(argc);
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (op.args[i].get_type() == Variant::NIL) {
- break;
- }
+ for (int i = 0; i < argc; i++) {
argptrs.write[i] = &op.args[i];
- argc++;
}
- argptrs.resize(argc);
Callable::CallError ce;
- obj->call(op.name, (const Variant **)argptrs.ptr(), argc, ce);
+ obj->callp(op.name, (const Variant **)argptrs.ptr(), argc, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT("Error calling method from signal '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
+ ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce));
}
#ifdef TOOLS_ENABLED
Resource *res = Object::cast_to<Resource>(obj);
@@ -341,7 +334,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
#endif
if (method_callback) {
- method_callback(method_callbck_ud, obj, op.name, VARIANT_ARGS_FROM_ARRAY(op.args));
+ method_callback(method_callback_ud, obj, op.name, (const Variant **)argptrs.ptr(), argc);
}
} break;
case Operation::TYPE_PROPERTY: {
@@ -439,7 +432,7 @@ void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void
void UndoRedo::set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud) {
method_callback = p_method_callback;
- method_callbck_ud = p_ud;
+ method_callback_ud = p_ud;
}
void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_callback, void *p_ud) {
@@ -477,14 +470,7 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl
Object *object = *p_args[0];
StringName method = *p_args[1];
- Variant v[VARIANT_ARG_MAX];
-
- for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
- v[i] = *p_args[i + 2];
- }
-
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- add_do_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+ add_do_methodp(object, method, p_args + 2, p_argcount - 2);
return Variant();
}
@@ -514,14 +500,7 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla
Object *object = *p_args[0];
StringName method = *p_args[1];
- Variant v[VARIANT_ARG_MAX];
-
- for (int i = 0; i < MIN(VARIANT_ARG_MAX, p_argcount - 2); ++i) {
- v[i] = *p_args[i + 2];
- }
-
- static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8");
- add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+ add_undo_methodp(object, method, p_args + 2, p_argcount - 2);
return Variant();
}
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 5eede74e2d..ecd7a21167 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -49,7 +49,7 @@ public:
Variant _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, VARIANT_ARG_DECLARE);
+ typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
private:
@@ -65,7 +65,7 @@ private:
Ref<RefCounted> ref;
ObjectID object;
StringName name;
- Variant args[VARIANT_ARG_MAX];
+ Vector<Variant> args;
void delete_reference();
};
@@ -92,7 +92,7 @@ private:
CommitNotifyCallback callback = nullptr;
void *callback_ud = nullptr;
- void *method_callbck_ud = nullptr;
+ void *method_callback_ud = nullptr;
void *prop_callback_ud = nullptr;
MethodNotifyCallback method_callback = nullptr;
@@ -106,8 +106,30 @@ protected:
public:
void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE);
- void add_do_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
- void add_undo_method(Object *p_object, const StringName &p_method, VARIANT_ARG_LIST);
+ void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount);
+ void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount);
+
+ template <typename... VarArgs>
+ void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+
+ add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+ template <typename... VarArgs>
+ void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+
+ add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
+ }
+
void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value);
void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value);
void add_do_reference(Object *p_object);
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 9837b6e0aa..2e5db145a4 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -328,17 +328,13 @@ void OS::yield() {
void OS::ensure_user_data_dir() {
String dd = get_user_data_dir();
- DirAccess *da = DirAccess::open(dd);
- if (da) {
- memdelete(da);
+ if (DirAccess::exists(dd)) {
return;
}
- da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
Error err = da->make_dir_recursive(dd);
ERR_FAIL_COND_MSG(err != OK, "Error attempting to create data dir: " + dd + ".");
-
- memdelete(da);
}
String OS::get_model_name() const {
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index f80e8f4bb3..c8072b7280 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#include "platform_config.h"
#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h
#include "thread.h"
diff --git a/core/os/thread.h b/core/os/thread.h
index f4e46059ad..3382dd81f9 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#include "platform_config.h"
// Define PLATFORM_THREAD_OVERRIDE in your platform's `platform_config.h`
// to use a custom Thread implementation defined in `platform/[your_platform]/platform_thread.h`
// Overriding the platform implementation is required in some proprietary platforms
diff --git a/core/os/time.cpp b/core/os/time.cpp
index 5eae94279a..e339805475 100644
--- a/core/os/time.cpp
+++ b/core/os/time.cpp
@@ -270,7 +270,7 @@ Dictionary Time::get_datetime_dict_from_string(String p_datetime, bool p_weekday
return dict;
}
-String Time::get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space) const {
+String Time::get_datetime_string_from_dict(const Dictionary p_datetime, bool p_use_space) const {
ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), "", "Invalid datetime Dictionary: Dictionary is empty.");
EXTRACT_FROM_DICTIONARY
// vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
@@ -283,7 +283,7 @@ String Time::get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_spa
return timestamp;
}
-int64_t Time::get_unix_time_from_datetime_dict(Dictionary p_datetime) const {
+int64_t Time::get_unix_time_from_datetime_dict(const Dictionary p_datetime) const {
ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
EXTRACT_FROM_DICTIONARY
VALIDATE_YMDHMS
@@ -298,6 +298,21 @@ int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const {
return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
}
+String Time::get_offset_string_from_offset_minutes(int64_t p_offset_minutes) const {
+ String sign;
+ if (p_offset_minutes < 0) {
+ sign = "-";
+ p_offset_minutes = -p_offset_minutes;
+ } else {
+ sign = "+";
+ }
+ // These two lines can be optimized to one instruction on x86 and others.
+ // Note that % is acceptable here only because we ensure it's positive.
+ int64_t offset_hours = p_offset_minutes / 60;
+ int64_t offset_minutes = p_offset_minutes % 60;
+ return vformat("%s%02d:%02d", sign, offset_hours, offset_minutes);
+}
+
Dictionary Time::get_datetime_dict_from_system(bool p_utc) const {
OS::Date date = OS::get_singleton()->get_date(p_utc);
OS::Time time = OS::get_singleton()->get_time(p_utc);
@@ -389,6 +404,7 @@ void Time::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_datetime_string_from_dict", "datetime", "use_space"), &Time::get_datetime_string_from_dict);
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_dict", "datetime"), &Time::get_unix_time_from_datetime_dict);
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_string", "datetime"), &Time::get_unix_time_from_datetime_string);
+ ClassDB::bind_method(D_METHOD("get_offset_string_from_offset_minutes", "offset_minutes"), &Time::get_offset_string_from_offset_minutes);
ClassDB::bind_method(D_METHOD("get_datetime_dict_from_system", "utc"), &Time::get_datetime_dict_from_system, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_date_dict_from_system", "utc"), &Time::get_date_dict_from_system, DEFVAL(false));
diff --git a/core/os/time.h b/core/os/time.h
index 3f00ba1478..0021c0ac6f 100644
--- a/core/os/time.h
+++ b/core/os/time.h
@@ -86,9 +86,10 @@ public:
String get_date_string_from_unix_time(int64_t p_unix_time_val) const;
String get_time_string_from_unix_time(int64_t p_unix_time_val) const;
Dictionary get_datetime_dict_from_string(String p_datetime, bool p_weekday = true) const;
- String get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space = false) const;
- int64_t get_unix_time_from_datetime_dict(Dictionary p_datetime) const;
+ String get_datetime_string_from_dict(const Dictionary p_datetime, bool p_use_space = false) const;
+ int64_t get_unix_time_from_datetime_dict(const Dictionary p_datetime) const;
int64_t get_unix_time_from_datetime_string(String p_datetime) const;
+ String get_offset_string_from_offset_minutes(int64_t p_offset_minutes) const;
// Methods that get information from OS.
Dictionary get_datetime_dict_from_system(bool p_utc = false) const;
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 63aa8050c4..2aa47c6c96 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -141,7 +141,7 @@ void register_core_types() {
GDREGISTER_CLASS(Object);
- GDREGISTER_VIRTUAL_CLASS(Script);
+ GDREGISTER_ABSTRACT_CLASS(Script);
GDREGISTER_CLASS(RefCounted);
GDREGISTER_CLASS(WeakRef);
@@ -149,12 +149,12 @@ void register_core_types() {
GDREGISTER_CLASS(Image);
GDREGISTER_CLASS(Shortcut);
- GDREGISTER_VIRTUAL_CLASS(InputEvent);
- GDREGISTER_VIRTUAL_CLASS(InputEventWithModifiers);
- GDREGISTER_VIRTUAL_CLASS(InputEventFromWindow);
+ GDREGISTER_ABSTRACT_CLASS(InputEvent);
+ GDREGISTER_ABSTRACT_CLASS(InputEventWithModifiers);
+ GDREGISTER_ABSTRACT_CLASS(InputEventFromWindow);
GDREGISTER_CLASS(InputEventKey);
GDREGISTER_CLASS(InputEventShortcut);
- GDREGISTER_VIRTUAL_CLASS(InputEventMouse);
+ GDREGISTER_ABSTRACT_CLASS(InputEventMouse);
GDREGISTER_CLASS(InputEventMouseButton);
GDREGISTER_CLASS(InputEventMouseMotion);
GDREGISTER_CLASS(InputEventJoypadButton);
@@ -162,21 +162,21 @@ void register_core_types() {
GDREGISTER_CLASS(InputEventScreenDrag);
GDREGISTER_CLASS(InputEventScreenTouch);
GDREGISTER_CLASS(InputEventAction);
- GDREGISTER_VIRTUAL_CLASS(InputEventGesture);
+ GDREGISTER_ABSTRACT_CLASS(InputEventGesture);
GDREGISTER_CLASS(InputEventMagnifyGesture);
GDREGISTER_CLASS(InputEventPanGesture);
GDREGISTER_CLASS(InputEventMIDI);
// Network
- GDREGISTER_VIRTUAL_CLASS(IP);
+ GDREGISTER_ABSTRACT_CLASS(IP);
- GDREGISTER_VIRTUAL_CLASS(StreamPeer);
+ GDREGISTER_ABSTRACT_CLASS(StreamPeer);
GDREGISTER_CLASS(StreamPeerExtension);
GDREGISTER_CLASS(StreamPeerBuffer);
GDREGISTER_CLASS(StreamPeerTCP);
GDREGISTER_CLASS(TCPServer);
- GDREGISTER_VIRTUAL_CLASS(PacketPeer);
+ GDREGISTER_ABSTRACT_CLASS(PacketPeer);
GDREGISTER_CLASS(PacketPeerExtension);
GDREGISTER_CLASS(PacketPeerStream);
GDREGISTER_CLASS(PacketPeerUDP);
@@ -200,7 +200,7 @@ void register_core_types() {
resource_format_loader_crypto.instantiate();
ResourceLoader::add_resource_format_loader(resource_format_loader_crypto);
- GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer);
+ GDREGISTER_ABSTRACT_CLASS(MultiplayerPeer);
GDREGISTER_CLASS(MultiplayerPeerExtension);
GDREGISTER_CLASS(MultiplayerAPI);
GDREGISTER_CLASS(MainLoop);
@@ -226,19 +226,19 @@ void register_core_types() {
GDREGISTER_CLASS(PCKPacker);
GDREGISTER_CLASS(PackedDataContainer);
- GDREGISTER_VIRTUAL_CLASS(PackedDataContainerRef);
+ GDREGISTER_ABSTRACT_CLASS(PackedDataContainerRef);
GDREGISTER_CLASS(AStar);
GDREGISTER_CLASS(AStar2D);
GDREGISTER_CLASS(EncodedObjectAsID);
GDREGISTER_CLASS(RandomNumberGenerator);
- GDREGISTER_VIRTUAL_CLASS(ResourceImporter);
+ GDREGISTER_ABSTRACT_CLASS(ResourceImporter);
GDREGISTER_CLASS(NativeExtension);
- GDREGISTER_VIRTUAL_CLASS(NativeExtensionManager);
+ GDREGISTER_ABSTRACT_CLASS(NativeExtensionManager);
- GDREGISTER_VIRTUAL_CLASS(ResourceUID);
+ GDREGISTER_ABSTRACT_CLASS(ResourceUID);
GDREGISTER_CLASS(EngineProfiler);
@@ -276,7 +276,7 @@ void register_core_settings() {
void register_core_singletons() {
GDREGISTER_CLASS(ProjectSettings);
- GDREGISTER_VIRTUAL_CLASS(IP);
+ GDREGISTER_ABSTRACT_CLASS(IP);
GDREGISTER_CLASS(core_bind::Geometry2D);
GDREGISTER_CLASS(core_bind::Geometry3D);
GDREGISTER_CLASS(core_bind::ResourceLoader);
@@ -286,7 +286,7 @@ void register_core_singletons() {
GDREGISTER_CLASS(core_bind::special::ClassDB);
GDREGISTER_CLASS(core_bind::Marshalls);
GDREGISTER_CLASS(TranslationServer);
- GDREGISTER_VIRTUAL_CLASS(Input);
+ GDREGISTER_ABSTRACT_CLASS(Input);
GDREGISTER_CLASS(InputMap);
GDREGISTER_CLASS(Expression);
GDREGISTER_CLASS(core_bind::EngineDebugger);
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 5bc723787f..b9067e2edd 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -86,10 +86,10 @@ public:
}
p_mem->~T();
available_pool[allocs_available >> page_shift][allocs_available & page_mask] = p_mem;
+ allocs_available++;
if (thread_safe) {
spin_lock.unlock();
}
- allocs_available++;
}
void reset(bool p_allow_unfreed = false) {
diff --git a/core/templates/pooled_list.h b/core/templates/pooled_list.h
index 360fda81f8..f13156b292 100644
--- a/core/templates/pooled_list.h
+++ b/core/templates/pooled_list.h
@@ -28,16 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef POOLED_LIST_H
-#define POOLED_LIST_H
-
-#include "core/templates/local_vector.h"
+#pragma once
// Simple template to provide a pool with O(1) allocate and free.
// The freelist could alternatively be a linked list placed within the unused elements
// to use less memory, however a separate freelist is probably more cache friendly.
-//
-// NOTE: Take great care when using this with non POD types. The construction and destruction
+
+// NOTE : Take great care when using this with non POD types. The construction and destruction
// is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee
// a constructor is run, and free does not guarantee a destructor.
// You should generally handle clearing
@@ -45,33 +42,60 @@
// This is by design for fastest use in the BVH. If you want a more general pool
// that does call constructors / destructors on request / free, this should probably be
// a separate template.
-template <class T, bool force_trivial = false>
+
+// The zero_on_first_request feature is optional and is useful for e.g. pools of handles,
+// which may use a ref count which we want to be initialized to zero the first time a handle is created,
+// but left alone on subsequent allocations (as will typically be incremented).
+
+// Note that there is no function to compact the pool - this would
+// invalidate any existing pool IDs held externally.
+// Compaction can be done but would rely on a more complex method
+// of preferentially giving out lower IDs in the freelist first.
+
+#include "core/templates/local_vector.h"
+
+template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>
class PooledList {
- LocalVector<T, uint32_t, force_trivial> list;
- LocalVector<uint32_t, uint32_t, true> freelist;
+ LocalVector<T, U, force_trivial> list;
+ LocalVector<U, U, true> freelist;
// not all list members are necessarily used
- int _used_size;
+ U _used_size;
public:
PooledList() {
_used_size = 0;
}
- int estimate_memory_use() const {
- return (list.size() * sizeof(T)) + (freelist.size() * sizeof(uint32_t));
+ // Use with care, in most cases you should make sure to
+ // free all elements first (i.e. _used_size would be zero),
+ // although it could also be used without this as an optimization
+ // in some cases.
+ void clear() {
+ list.clear();
+ freelist.clear();
+ _used_size = 0;
+ }
+
+ uint64_t estimate_memory_use() const {
+ return ((uint64_t)list.size() * sizeof(T)) + ((uint64_t)freelist.size() * sizeof(U));
}
- const T &operator[](uint32_t p_index) const {
+ const T &operator[](U p_index) const {
return list[p_index];
}
- T &operator[](uint32_t p_index) {
+ T &operator[](U p_index) {
return list[p_index];
}
- int size() const { return _used_size; }
+ // To be explicit in a pool there is a distinction
+ // between the number of elements that are currently
+ // in use, and the number of elements that have been reserved.
+ // Using size() would be vague.
+ U used_size() const { return _used_size; }
+ U reserved_size() const { return list.size(); }
- T *request(uint32_t &r_id) {
+ T *request(U &r_id) {
_used_size++;
if (freelist.size()) {
@@ -79,19 +103,106 @@ public:
int new_size = freelist.size() - 1;
r_id = freelist[new_size];
freelist.resize(new_size);
+
return &list[r_id];
}
r_id = list.size();
list.resize(r_id + 1);
+
+ static_assert((!zero_on_first_request) || (__is_pod(T)), "zero_on_first_request requires trivial type");
+ if (zero_on_first_request && __is_pod(T)) {
+ list[r_id] = {};
+ }
+
return &list[r_id];
}
- void free(const uint32_t &p_id) {
+ void free(const U &p_id) {
// should not be on free list already
- CRASH_COND(p_id >= list.size());
+ ERR_FAIL_UNSIGNED_INDEX(p_id, list.size());
freelist.push_back(p_id);
+ ERR_FAIL_COND_MSG(!_used_size, "_used_size has become out of sync, have you double freed an item?");
_used_size--;
}
};
-#endif // POOLED_LIST_H
+// a pooled list which automatically keeps a list of the active members
+template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>
+class TrackedPooledList {
+public:
+ U pool_used_size() const { return _pool.used_size(); }
+ U pool_reserved_size() const { return _pool.reserved_size(); }
+ U active_size() const { return _active_list.size(); }
+
+ // use with care, see the earlier notes in the PooledList clear()
+ void clear() {
+ _pool.clear();
+ _active_list.clear();
+ _active_map.clear();
+ }
+
+ U get_active_id(U p_index) const {
+ return _active_list[p_index];
+ }
+
+ const T &get_active(U p_index) const {
+ return _pool[get_active_id(p_index)];
+ }
+
+ T &get_active(U p_index) {
+ return _pool[get_active_id(p_index)];
+ }
+
+ const T &operator[](U p_index) const {
+ return _pool[p_index];
+ }
+ T &operator[](U p_index) {
+ return _pool[p_index];
+ }
+
+ T *request(U &r_id) {
+ T *item = _pool.request(r_id);
+
+ // add to the active list
+ U active_list_id = _active_list.size();
+ _active_list.push_back(r_id);
+
+ // expand the active map (this should be in sync with the pool list
+ if (_pool.used_size() > _active_map.size()) {
+ _active_map.resize(_pool.used_size());
+ }
+
+ // store in the active map
+ _active_map[r_id] = active_list_id;
+
+ return item;
+ }
+
+ void free(const U &p_id) {
+ _pool.free(p_id);
+
+ // remove from the active list.
+ U list_id = _active_map[p_id];
+
+ // zero the _active map to detect bugs (only in debug?)
+ _active_map[p_id] = -1;
+
+ _active_list.remove_unordered(list_id);
+
+ // keep the replacement in sync with the correct list Id
+ if (list_id < _active_list.size()) {
+ // which pool id has been replaced in the active list
+ U replacement_id = _active_list[list_id];
+
+ // keep that replacements map up to date with the new position
+ _active_map[replacement_id] = list_id;
+ }
+ }
+
+ const LocalVector<U, U> &get_active_list() const { return _active_list; }
+
+private:
+ PooledList<T, U, force_trivial, zero_on_first_request> _pool;
+ LocalVector<U, U> _active_map;
+ LocalVector<U, U> _active_list;
+};
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index b6fdb4d902..f31191e8a3 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -182,7 +182,7 @@ struct VariantCasterAndValidate {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) ||
- !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) {
+ !VariantObjectClassChecker<T>::check(*p_args[p_arg_idx])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = p_arg_idx;
r_error.expected = argtype;
@@ -197,7 +197,7 @@ struct VariantCasterAndValidate<T &> {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) ||
- !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) {
+ !VariantObjectClassChecker<T>::check(*p_args[p_arg_idx])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = p_arg_idx;
r_error.expected = argtype;
@@ -212,7 +212,7 @@ struct VariantCasterAndValidate<const T &> {
static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype) ||
- !VariantObjectClassChecker<T>::check(p_args[p_arg_idx])) {
+ !VariantObjectClassChecker<T>::check(*p_args[p_arg_idx])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = p_arg_idx;
r_error.expected = argtype;
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 27792ce111..48ed48d120 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -37,7 +37,7 @@
#include "core/object/script_language.h"
void Callable::call_deferred(const Variant **p_arguments, int p_argcount) const {
- MessageQueue::get_singleton()->push_callable(*this, p_arguments, p_argcount);
+ MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount);
}
void Callable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const {
@@ -59,7 +59,7 @@ void Callable::call(const Variant **p_arguments, int p_argcount, Variant &r_retu
return;
}
#endif
- r_return_value = obj->call(method, p_arguments, p_argcount, r_call_error);
+ r_return_value = obj->callp(method, p_arguments, p_argcount, r_call_error);
}
}
@@ -379,7 +379,7 @@ Error Signal::emit(const Variant **p_arguments, int p_argcount) const {
return ERR_INVALID_DATA;
}
- return obj->emit_signal(name, p_arguments, p_argcount);
+ return obj->emit_signalp(name, p_arguments, p_argcount);
}
Error Signal::connect(const Callable &p_callable, uint32_t p_flags) {
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 3d11ed6303..b3e909b489 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -1972,7 +1972,7 @@ Variant::operator ::RID() const {
}
#endif
Callable::CallError ce;
- Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->get_rid, nullptr, 0, ce);
+ Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->get_rid, nullptr, 0, ce);
if (ce.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::RID) {
return ret;
}
@@ -3309,21 +3309,7 @@ bool Variant::is_shared() const {
return false;
}
-Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
- VARIANT_ARGPTRS;
- int argc = 0;
- for (int i = 0; i < VARIANT_ARG_MAX; i++) {
- if (argptr[i]->get_type() == Variant::NIL) {
- break;
- }
- argc++;
- }
-
- Callable::CallError error;
-
- Variant ret;
- call(p_method, argptr, argc, ret, error);
-
+void Variant::_variant_call_error(const String &p_method, Callable::CallError &error) {
switch (error.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
String err = "Invalid type for argument #" + itos(error.argument) + ", expected '" + Variant::get_type_name(Variant::Type(error.expected)) + "'.";
@@ -3341,8 +3327,6 @@ Variant Variant::call(const StringName &p_method, VARIANT_ARG_DECLARE) {
default: {
}
}
-
- return ret;
}
void Variant::construct_from_string(const String &p_string, Variant &r_value, ObjectConstruct p_obj_construct, void *p_construct_ud) {
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 836a67d942..ca18249f36 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -282,6 +282,14 @@ private:
static void _register_variant_utility_functions();
static void _unregister_variant_utility_functions();
+ void _variant_call_error(const String &p_method, Callable::CallError &error);
+
+ // Avoid accidental conversion. If you reached this point, it's because you most likely forgot to dereference
+ // a Variant pointer (so add * like this: *variant_pointer).
+
+ Variant(const Variant *) {}
+ Variant(const Variant **) {}
+
public:
_FORCE_INLINE_ Type get_type() const {
return type;
@@ -527,8 +535,23 @@ public:
static int get_builtin_method_count(Variant::Type p_type);
static uint32_t get_builtin_method_hash(Variant::Type p_type, const StringName &p_method);
- void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
- Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant());
+ void callp(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
+
+ template <typename... VarArgs>
+ Variant call(const StringName &p_method, VarArgs... p_args) {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+ Callable::CallError cerr;
+ Variant ret;
+ callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), ret, cerr);
+ if (cerr.error != Callable::CallError::CALL_OK) {
+ _variant_call_error(p_method, cerr);
+ }
+ return ret;
+ }
static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index a5e89eec80..bc29be77fc 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1003,7 +1003,7 @@ static void register_builtin_method(const Vector<String> &p_argnames, const Vect
builtin_method_names[T::get_base_type()].push_back(name);
}
-void Variant::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
+void Variant::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
if (type == Variant::OBJECT) {
//call object
Object *obj = _get_obj().obj;
@@ -1018,7 +1018,7 @@ void Variant::call(const StringName &p_method, const Variant **p_args, int p_arg
}
#endif
- r_ret = _get_obj().obj->call(p_method, p_args, p_argcount, r_error);
+ r_ret = _get_obj().obj->callp(p_method, p_args, p_argcount, r_error);
//else if (type==Variant::METHOD) {
} else {
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index fa8d26a72b..e604ff9567 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -1277,7 +1277,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
ref.push_back(r_iter);
Variant vref = ref;
const Variant *refp[] = { &vref };
- Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce);
+ Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_init, refp, 1, ce);
if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) {
valid = false;
@@ -1504,7 +1504,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
ref.push_back(r_iter);
Variant vref = ref;
const Variant *refp[] = { &vref };
- Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce);
+ Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_next, refp, 1, ce);
if (ref.size() != 1 || ce.error != Callable::CallError::CALL_OK) {
valid = false;
@@ -1686,7 +1686,7 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
Callable::CallError ce;
ce.error = Callable::CallError::CALL_OK;
const Variant *refp[] = { &r_iter };
- Variant ret = _get_obj().obj->call(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce);
+ Variant ret = _get_obj().obj->callp(CoreStringNames::get_singleton()->_iter_get, refp, 1, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_valid = false;
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index e83c71098d..05fb577e2c 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -219,10 +219,6 @@ struct VariantUtilityFunctions {
return Math::step_decimals(step);
}
- static inline int range_step_decimals(float step) {
- return Math::range_step_decimals(step);
- }
-
static inline double snapped(double value, double step) {
return Math::snapped(value, step);
}
@@ -1204,7 +1200,6 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(ease, sarray("x", "curve"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
- FUNCBINDR(range_step_decimals, sarray("x"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(snapped, sarray("x", "step"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);