Merge pull request #1466 from dsnopek/4.1-cherrypicks-10
Cherry-picks for the godot-cpp 4.1 branch - 10th batch4.1
commit
32becf6a13
|
@ -178,6 +178,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
|
||||||
generate_utility_functions(api, target_dir)
|
generate_utility_functions(api, target_dir)
|
||||||
|
|
||||||
|
|
||||||
|
CLASS_ALIASES = {
|
||||||
|
"ClassDB": "ClassDBSingleton",
|
||||||
|
}
|
||||||
|
|
||||||
builtin_classes = []
|
builtin_classes = []
|
||||||
|
|
||||||
# Key is class name, value is boolean where True means the class is refcounted.
|
# Key is class name, value is boolean where True means the class is refcounted.
|
||||||
|
@ -1079,9 +1083,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
|
||||||
# First create map of classes and singletons.
|
# First create map of classes and singletons.
|
||||||
for class_api in api["classes"]:
|
for class_api in api["classes"]:
|
||||||
# Generate code for the ClassDB singleton under a different name.
|
# Generate code for the ClassDB singleton under a different name.
|
||||||
if class_api["name"] == "ClassDB":
|
if class_api["name"] in CLASS_ALIASES:
|
||||||
class_api["name"] = "ClassDBSingleton"
|
class_api["alias_for"] = class_api["name"]
|
||||||
class_api["alias_for"] = "ClassDB"
|
class_api["name"] = CLASS_ALIASES[class_api["alias_for"]]
|
||||||
engine_classes[class_api["name"]] = class_api["is_refcounted"]
|
engine_classes[class_api["name"]] = class_api["is_refcounted"]
|
||||||
for native_struct in api["native_structures"]:
|
for native_struct in api["native_structures"]:
|
||||||
engine_classes[native_struct["name"]] = False
|
engine_classes[native_struct["name"]] = False
|
||||||
|
@ -1089,9 +1093,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
|
||||||
|
|
||||||
for singleton in api["singletons"]:
|
for singleton in api["singletons"]:
|
||||||
# Generate code for the ClassDB singleton under a different name.
|
# Generate code for the ClassDB singleton under a different name.
|
||||||
if singleton["name"] == "ClassDB":
|
if singleton["name"] in CLASS_ALIASES:
|
||||||
singleton["name"] = "ClassDBSingleton"
|
singleton["alias_for"] = singleton["name"]
|
||||||
singleton["alias_for"] = "ClassDB"
|
singleton["name"] = CLASS_ALIASES[singleton["name"]]
|
||||||
singletons.append(singleton["name"])
|
singletons.append(singleton["name"])
|
||||||
|
|
||||||
for class_api in api["classes"]:
|
for class_api in api["classes"]:
|
||||||
|
@ -1294,6 +1298,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
result.append("#include <type_traits>")
|
result.append("#include <type_traits>")
|
||||||
result.append("")
|
result.append("")
|
||||||
|
|
||||||
|
if class_name == "ClassDBSingleton":
|
||||||
|
result.append("#include <godot_cpp/core/binder_common.hpp>")
|
||||||
|
result.append("")
|
||||||
|
|
||||||
result.append("namespace godot {")
|
result.append("namespace godot {")
|
||||||
result.append("")
|
result.append("")
|
||||||
|
|
||||||
|
@ -1448,6 +1456,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
|
|
||||||
if class_name == "ClassDBSingleton":
|
if class_name == "ClassDBSingleton":
|
||||||
result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")
|
result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\")
|
||||||
|
|
||||||
|
if "enums" in class_api:
|
||||||
|
for enum_api in class_api["enums"]:
|
||||||
|
if enum_api["is_bitfield"]:
|
||||||
|
result.append(f'\tenum {enum_api["name"]} : uint64_t {{ \\')
|
||||||
|
else:
|
||||||
|
result.append(f'\tenum {enum_api["name"]} {{ \\')
|
||||||
|
|
||||||
|
for value in enum_api["values"]:
|
||||||
|
result.append(f'\t\t{value["name"]} = {value["value"]}, \\')
|
||||||
|
result.append("\t}; \\")
|
||||||
|
result.append("\t \\")
|
||||||
|
|
||||||
for method in class_api["methods"]:
|
for method in class_api["methods"]:
|
||||||
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
|
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
|
||||||
if vararg:
|
if vararg:
|
||||||
|
@ -1456,12 +1477,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
continue
|
continue
|
||||||
|
|
||||||
method_signature = "\tstatic "
|
method_signature = "\tstatic "
|
||||||
|
return_type = None
|
||||||
if "return_type" in method:
|
if "return_type" in method:
|
||||||
method_signature += f'{correct_type(method["return_type"])} '
|
return_type = correct_type(method["return_type"].replace("ClassDBSingleton", "ClassDB"), None, False)
|
||||||
elif "return_value" in method:
|
elif "return_value" in method:
|
||||||
method_signature += (
|
return_type = correct_type(
|
||||||
correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " "
|
method["return_value"]["type"].replace("ClassDBSingleton", "ClassDB"),
|
||||||
|
method["return_value"].get("meta", None),
|
||||||
|
False,
|
||||||
)
|
)
|
||||||
|
if return_type is not None:
|
||||||
|
method_signature += return_type + " "
|
||||||
else:
|
else:
|
||||||
method_signature += "void "
|
method_signature += "void "
|
||||||
|
|
||||||
|
@ -1480,8 +1506,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
result.append(method_signature)
|
result.append(method_signature)
|
||||||
|
|
||||||
method_body = "\t\t"
|
method_body = "\t\t"
|
||||||
if "return_type" in method or "return_value" in method:
|
if return_type is not None:
|
||||||
method_body += "return "
|
method_body += "return "
|
||||||
|
if "alias_for" in class_api and return_type.startswith(class_api["alias_for"] + "::"):
|
||||||
|
method_body += f"({return_type})"
|
||||||
method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
|
method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}('
|
||||||
method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
|
method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
|
||||||
method_body += "); \\"
|
method_body += "); \\"
|
||||||
|
@ -1491,6 +1519,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
result.append("\t;")
|
result.append("\t;")
|
||||||
result.append("")
|
result.append("")
|
||||||
|
|
||||||
|
result.append("#define CLASSDB_SINGLETON_VARIANT_CAST \\")
|
||||||
|
|
||||||
|
if "enums" in class_api:
|
||||||
|
for enum_api in class_api["enums"]:
|
||||||
|
if enum_api["is_bitfield"]:
|
||||||
|
result.append(f'\tVARIANT_BITFIELD_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
|
||||||
|
else:
|
||||||
|
result.append(f'\tVARIANT_ENUM_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\')
|
||||||
|
|
||||||
|
result.append("\t;")
|
||||||
|
result.append("")
|
||||||
|
|
||||||
result.append(f"#endif // ! {header_guard}")
|
result.append(f"#endif // ! {header_guard}")
|
||||||
|
|
||||||
return "\n".join(result)
|
return "\n".join(result)
|
||||||
|
@ -2285,7 +2325,7 @@ def correct_typed_array(type_name):
|
||||||
return type_name
|
return type_name
|
||||||
|
|
||||||
|
|
||||||
def correct_type(type_name, meta=None):
|
def correct_type(type_name, meta=None, use_alias=True):
|
||||||
type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
|
type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
|
||||||
if meta != None:
|
if meta != None:
|
||||||
if "int" in meta:
|
if "int" in meta:
|
||||||
|
@ -2301,11 +2341,15 @@ def correct_type(type_name, meta=None):
|
||||||
if is_enum(type_name):
|
if is_enum(type_name):
|
||||||
if is_bitfield(type_name):
|
if is_bitfield(type_name):
|
||||||
base_class = get_enum_class(type_name)
|
base_class = get_enum_class(type_name)
|
||||||
|
if use_alias and base_class in CLASS_ALIASES:
|
||||||
|
base_class = CLASS_ALIASES[base_class]
|
||||||
if base_class == "GlobalConstants":
|
if base_class == "GlobalConstants":
|
||||||
return f"BitField<{get_enum_name(type_name)}>"
|
return f"BitField<{get_enum_name(type_name)}>"
|
||||||
return f"BitField<{base_class}::{get_enum_name(type_name)}>"
|
return f"BitField<{base_class}::{get_enum_name(type_name)}>"
|
||||||
else:
|
else:
|
||||||
base_class = get_enum_class(type_name)
|
base_class = get_enum_class(type_name)
|
||||||
|
if use_alias and base_class in CLASS_ALIASES:
|
||||||
|
base_class = CLASS_ALIASES[base_class]
|
||||||
if base_class == "GlobalConstants":
|
if base_class == "GlobalConstants":
|
||||||
return f"{get_enum_name(type_name)}"
|
return f"{get_enum_name(type_name)}"
|
||||||
return f"{base_class}::{get_enum_name(type_name)}"
|
return f"{base_class}::{get_enum_name(type_name)}"
|
||||||
|
|
|
@ -76,14 +76,15 @@ protected:
|
||||||
uint32_t plist_size = 0;
|
uint32_t plist_size = 0;
|
||||||
|
|
||||||
void _postinitialize();
|
void _postinitialize();
|
||||||
|
virtual void _notificationv(int32_t p_what) {}
|
||||||
|
|
||||||
Wrapped(const StringName p_godot_class);
|
Wrapped(const StringName p_godot_class);
|
||||||
Wrapped(GodotObject *p_godot_object);
|
Wrapped(GodotObject *p_godot_object);
|
||||||
virtual ~Wrapped() {}
|
virtual ~Wrapped() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static StringName &get_class_static() {
|
static const StringName &get_class_static() {
|
||||||
static StringName string_name = StringName("Wrapped");
|
static const StringName string_name = StringName("Wrapped");
|
||||||
return string_name;
|
return string_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +172,7 @@ protected:
|
||||||
\
|
\
|
||||||
public: \
|
public: \
|
||||||
typedef m_class self_type; \
|
typedef m_class self_type; \
|
||||||
|
typedef m_inherits parent_type; \
|
||||||
\
|
\
|
||||||
static void initialize_class() { \
|
static void initialize_class() { \
|
||||||
static bool initialized = false; \
|
static bool initialized = false; \
|
||||||
|
@ -185,12 +187,12 @@ public:
|
||||||
initialized = true; \
|
initialized = true; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static ::godot::StringName &get_class_static() { \
|
static const ::godot::StringName &get_class_static() { \
|
||||||
static ::godot::StringName string_name = ::godot::StringName(#m_class); \
|
static const ::godot::StringName string_name = ::godot::StringName(#m_class); \
|
||||||
return string_name; \
|
return string_name; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static ::godot::StringName &get_parent_class_static() { \
|
static const ::godot::StringName &get_parent_class_static() { \
|
||||||
return m_inherits::get_class_static(); \
|
return m_inherits::get_class_static(); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
@ -327,6 +329,11 @@ public:
|
||||||
_gde_binding_reference_callback, \
|
_gde_binding_reference_callback, \
|
||||||
}; \
|
}; \
|
||||||
\
|
\
|
||||||
|
protected: \
|
||||||
|
virtual void _notificationv(int32_t p_what) override { \
|
||||||
|
m_class::notification_bind(this, p_what); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Don't use this for your classes, use GDCLASS() instead.
|
// Don't use this for your classes, use GDCLASS() instead.
|
||||||
|
@ -334,6 +341,7 @@ private:
|
||||||
private: \
|
private: \
|
||||||
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
|
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
|
||||||
void operator=(const m_class &p_rval) {} \
|
void operator=(const m_class &p_rval) {} \
|
||||||
|
friend class ::godot::ClassDB; \
|
||||||
\
|
\
|
||||||
protected: \
|
protected: \
|
||||||
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
|
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
|
||||||
|
@ -343,6 +351,8 @@ protected:
|
||||||
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
|
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
|
||||||
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
|
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
|
||||||
\
|
\
|
||||||
|
static void _bind_methods() {} \
|
||||||
|
\
|
||||||
static void (*_get_bind_methods())() { \
|
static void (*_get_bind_methods())() { \
|
||||||
return nullptr; \
|
return nullptr; \
|
||||||
} \
|
} \
|
||||||
|
@ -385,15 +395,16 @@ protected:
|
||||||
\
|
\
|
||||||
public: \
|
public: \
|
||||||
typedef m_class self_type; \
|
typedef m_class self_type; \
|
||||||
|
typedef m_inherits parent_type; \
|
||||||
\
|
\
|
||||||
static void initialize_class() {} \
|
static void initialize_class() {} \
|
||||||
\
|
\
|
||||||
static ::godot::StringName &get_class_static() { \
|
static const ::godot::StringName &get_class_static() { \
|
||||||
static ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
|
static const ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \
|
||||||
return string_name; \
|
return string_name; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static ::godot::StringName &get_parent_class_static() { \
|
static const ::godot::StringName &get_parent_class_static() { \
|
||||||
return m_inherits::get_class_static(); \
|
return m_inherits::get_class_static(); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
|
|
@ -174,6 +174,8 @@ public:
|
||||||
template <typename T, bool is_abstract>
|
template <typename T, bool is_abstract>
|
||||||
void ClassDB::_register_class(bool p_virtual) {
|
void ClassDB::_register_class(bool p_virtual) {
|
||||||
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
|
static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
|
||||||
|
static_assert(!FunctionsAreSame<T::self_type::_bind_methods, T::parent_type::_bind_methods>::value, "Class must declare 'static void _bind_methods'.");
|
||||||
|
static_assert(!std::is_abstract_v<T> || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS.");
|
||||||
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
|
instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks;
|
||||||
|
|
||||||
// Register this class within our plugin
|
// Register this class within our plugin
|
||||||
|
@ -290,4 +292,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
||||||
|
CLASSDB_SINGLETON_VARIANT_CAST;
|
||||||
|
|
||||||
#endif // GODOT_CLASS_DB_HPP
|
#endif // GODOT_CLASS_DB_HPP
|
||||||
|
|
|
@ -613,6 +613,14 @@ inline bool is_inf(double p_val) {
|
||||||
return std::isinf(p_val);
|
return std::isinf(p_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool is_finite(float p_val) {
|
||||||
|
return std::isfinite(p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_finite(double p_val) {
|
||||||
|
return std::isfinite(p_val);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool is_equal_approx(float a, float b) {
|
inline bool is_equal_approx(float a, float b) {
|
||||||
// Check for exact equality first, required to handle "infinity" values.
|
// Check for exact equality first, required to handle "infinity" values.
|
||||||
if (a == b) {
|
if (a == b) {
|
||||||
|
|
|
@ -412,6 +412,7 @@ public:
|
||||||
method = p_method;
|
method = p_method;
|
||||||
generate_argument_types(sizeof...(P));
|
generate_argument_types(sizeof...(P));
|
||||||
set_argument_count(sizeof...(P));
|
set_argument_count(sizeof...(P));
|
||||||
|
set_const(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -578,6 +579,7 @@ public:
|
||||||
generate_argument_types(sizeof...(P));
|
generate_argument_types(sizeof...(P));
|
||||||
set_argument_count(sizeof...(P));
|
set_argument_count(sizeof...(P));
|
||||||
set_return(true);
|
set_return(true);
|
||||||
|
set_const(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -170,11 +170,11 @@ template <typename T>
|
||||||
struct PtrToArg<T *> {
|
struct PtrToArg<T *> {
|
||||||
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
|
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
|
||||||
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
|
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
|
||||||
return reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
|
return likely(p_ptr) ? reinterpret_cast<T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
|
||||||
}
|
}
|
||||||
typedef Object *EncodeT;
|
typedef Object *EncodeT;
|
||||||
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
|
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
|
||||||
*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr;
|
*reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -182,11 +182,11 @@ template <typename T>
|
||||||
struct PtrToArg<const T *> {
|
struct PtrToArg<const T *> {
|
||||||
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
|
static_assert(std::is_base_of<Object, T>::value, "Cannot encode non-Object value as an Object");
|
||||||
_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
|
_FORCE_INLINE_ static const T *convert(const void *p_ptr) {
|
||||||
return reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr))));
|
return likely(p_ptr) ? reinterpret_cast<const T *>(godot::internal::get_object_instance_binding(*reinterpret_cast<GDExtensionObjectPtr *>(const_cast<void *>(p_ptr)))) : nullptr;
|
||||||
}
|
}
|
||||||
typedef const Object *EncodeT;
|
typedef const Object *EncodeT;
|
||||||
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
|
_FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) {
|
||||||
*reinterpret_cast<const void **>(p_ptr) = p_var ? p_var->_owner : nullptr;
|
*reinterpret_cast<const void **>(p_ptr) = likely(p_var) ? p_var->_owner : nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,63 @@ struct PropertyInfo {
|
||||||
|
|
||||||
PropertyInfo(GDExtensionVariantType p_type, const StringName &p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = "") :
|
PropertyInfo(GDExtensionVariantType p_type, const StringName &p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = "") :
|
||||||
PropertyInfo((Variant::Type)p_type, p_name, p_hint, p_hint_string, p_usage, p_class_name) {}
|
PropertyInfo((Variant::Type)p_type, p_name, p_hint, p_hint_string, p_usage, p_class_name) {}
|
||||||
|
|
||||||
|
PropertyInfo(const GDExtensionPropertyInfo *p_info) :
|
||||||
|
PropertyInfo(p_info->type, *reinterpret_cast<StringName *>(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast<String *>(p_info->hint_string), p_info->usage, *reinterpret_cast<StringName *>(p_info->class_name)) {}
|
||||||
|
|
||||||
|
operator Dictionary() const {
|
||||||
|
Dictionary dict;
|
||||||
|
dict["name"] = name;
|
||||||
|
dict["class_name"] = class_name;
|
||||||
|
dict["type"] = type;
|
||||||
|
dict["hint"] = hint;
|
||||||
|
dict["hint_string"] = hint_string;
|
||||||
|
dict["usage"] = usage;
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PropertyInfo from_dict(const Dictionary &p_dict) {
|
||||||
|
PropertyInfo pi;
|
||||||
|
if (p_dict.has("type")) {
|
||||||
|
pi.type = Variant::Type(int(p_dict["type"]));
|
||||||
|
}
|
||||||
|
if (p_dict.has("name")) {
|
||||||
|
pi.name = p_dict["name"];
|
||||||
|
}
|
||||||
|
if (p_dict.has("class_name")) {
|
||||||
|
pi.class_name = p_dict["class_name"];
|
||||||
|
}
|
||||||
|
if (p_dict.has("hint")) {
|
||||||
|
pi.hint = PropertyHint(int(p_dict["hint"]));
|
||||||
|
}
|
||||||
|
if (p_dict.has("hint_string")) {
|
||||||
|
pi.hint_string = p_dict["hint_string"];
|
||||||
|
}
|
||||||
|
if (p_dict.has("usage")) {
|
||||||
|
pi.usage = p_dict["usage"];
|
||||||
|
}
|
||||||
|
return pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _update(GDExtensionPropertyInfo *p_info) {
|
||||||
|
p_info->type = (GDExtensionVariantType)type;
|
||||||
|
*(reinterpret_cast<StringName *>(p_info->name)) = name;
|
||||||
|
p_info->hint = hint;
|
||||||
|
*(reinterpret_cast<String *>(p_info->hint_string)) = hint_string;
|
||||||
|
p_info->usage = usage;
|
||||||
|
*(reinterpret_cast<StringName *>(p_info->class_name)) = class_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
GDExtensionPropertyInfo _to_gdextension() const {
|
||||||
|
return {
|
||||||
|
(GDExtensionVariantType)type,
|
||||||
|
name._native_ptr(),
|
||||||
|
class_name._native_ptr(),
|
||||||
|
hint,
|
||||||
|
hint_string._native_ptr(),
|
||||||
|
usage,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
|
@ -58,6 +58,16 @@ struct TypesAreSame<A, A> {
|
||||||
static bool const value = true;
|
static bool const value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <auto A, auto B>
|
||||||
|
struct FunctionsAreSame {
|
||||||
|
static bool const value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <auto A>
|
||||||
|
struct FunctionsAreSame<A, A> {
|
||||||
|
static bool const value = true;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename B, typename D>
|
template <typename B, typename D>
|
||||||
struct TypeInherits {
|
struct TypeInherits {
|
||||||
static D *get_d();
|
static D *get_d();
|
||||||
|
|
|
@ -65,6 +65,7 @@ struct _NO_DISCARD_ AABB {
|
||||||
bool operator!=(const AABB &p_rval) const;
|
bool operator!=(const AABB &p_rval) const;
|
||||||
|
|
||||||
bool is_equal_approx(const AABB &p_aabb) const;
|
bool is_equal_approx(const AABB &p_aabb) const;
|
||||||
|
bool is_finite() const;
|
||||||
_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
|
_FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap
|
||||||
_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap
|
_FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap
|
||||||
_FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this
|
_FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this
|
||||||
|
|
|
@ -128,6 +128,7 @@ struct _NO_DISCARD_ Basis {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_equal_approx(const Basis &p_basis) const;
|
bool is_equal_approx(const Basis &p_basis) const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
bool operator==(const Basis &p_matrix) const;
|
bool operator==(const Basis &p_matrix) const;
|
||||||
bool operator!=(const Basis &p_matrix) const;
|
bool operator!=(const Basis &p_matrix) const;
|
||||||
|
|
|
@ -77,6 +77,7 @@ struct _NO_DISCARD_ Plane {
|
||||||
Plane operator-() const { return Plane(-normal, -d); }
|
Plane operator-() const { return Plane(-normal, -d); }
|
||||||
bool is_equal_approx(const Plane &p_plane) const;
|
bool is_equal_approx(const Plane &p_plane) const;
|
||||||
bool is_equal_approx_any_side(const Plane &p_plane) const;
|
bool is_equal_approx_any_side(const Plane &p_plane) const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
|
_FORCE_INLINE_ bool operator==(const Plane &p_plane) const;
|
||||||
_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;
|
_FORCE_INLINE_ bool operator!=(const Plane &p_plane) const;
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion {
|
||||||
}
|
}
|
||||||
_FORCE_INLINE_ real_t length_squared() const;
|
_FORCE_INLINE_ real_t length_squared() const;
|
||||||
bool is_equal_approx(const Quaternion &p_quaternion) const;
|
bool is_equal_approx(const Quaternion &p_quaternion) const;
|
||||||
|
bool is_finite() const;
|
||||||
real_t length() const;
|
real_t length() const;
|
||||||
void normalize();
|
void normalize();
|
||||||
Quaternion normalized() const;
|
Quaternion normalized() const;
|
||||||
|
|
|
@ -209,6 +209,7 @@ struct _NO_DISCARD_ Rect2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_equal_approx(const Rect2 &p_rect) const;
|
bool is_equal_approx(const Rect2 &p_rect) const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
|
bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; }
|
||||||
bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }
|
bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; }
|
||||||
|
|
|
@ -99,6 +99,7 @@ struct _NO_DISCARD_ Transform2D {
|
||||||
void orthonormalize();
|
void orthonormalize();
|
||||||
Transform2D orthonormalized() const;
|
Transform2D orthonormalized() const;
|
||||||
bool is_equal_approx(const Transform2D &p_transform) const;
|
bool is_equal_approx(const Transform2D &p_transform) const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
Transform2D looking_at(const Vector2 &p_target) const;
|
Transform2D looking_at(const Vector2 &p_target) const;
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@ struct _NO_DISCARD_ Transform3D {
|
||||||
void orthogonalize();
|
void orthogonalize();
|
||||||
Transform3D orthogonalized() const;
|
Transform3D orthogonalized() const;
|
||||||
bool is_equal_approx(const Transform3D &p_transform) const;
|
bool is_equal_approx(const Transform3D &p_transform) const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
bool operator==(const Transform3D &p_transform) const;
|
bool operator==(const Transform3D &p_transform) const;
|
||||||
bool operator!=(const Transform3D &p_transform) const;
|
bool operator!=(const Transform3D &p_transform) const;
|
||||||
|
|
|
@ -123,6 +123,7 @@ struct _NO_DISCARD_ Vector2 {
|
||||||
|
|
||||||
bool is_equal_approx(const Vector2 &p_v) const;
|
bool is_equal_approx(const Vector2 &p_v) const;
|
||||||
bool is_zero_approx() const;
|
bool is_zero_approx() const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
Vector2 operator+(const Vector2 &p_v) const;
|
Vector2 operator+(const Vector2 &p_v) const;
|
||||||
void operator+=(const Vector2 &p_v);
|
void operator+=(const Vector2 &p_v);
|
||||||
|
|
|
@ -146,6 +146,7 @@ struct _NO_DISCARD_ Vector3 {
|
||||||
|
|
||||||
bool is_equal_approx(const Vector3 &p_v) const;
|
bool is_equal_approx(const Vector3 &p_v) const;
|
||||||
bool is_zero_approx() const;
|
bool is_zero_approx() const;
|
||||||
|
bool is_finite() const;
|
||||||
|
|
||||||
/* Operators */
|
/* Operators */
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ struct _NO_DISCARD_ Vector4 {
|
||||||
_FORCE_INLINE_ real_t length_squared() const;
|
_FORCE_INLINE_ real_t length_squared() const;
|
||||||
bool is_equal_approx(const Vector4 &p_vec4) const;
|
bool is_equal_approx(const Vector4 &p_vec4) const;
|
||||||
bool is_zero_approx() const;
|
bool is_zero_approx() const;
|
||||||
|
bool is_finite() const;
|
||||||
real_t length() const;
|
real_t length() const;
|
||||||
void normalize();
|
void normalize();
|
||||||
Vector4 normalized() const;
|
Vector4 normalized() const;
|
||||||
|
|
|
@ -51,10 +51,7 @@ void Wrapped::_postinitialize() {
|
||||||
}
|
}
|
||||||
godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
|
godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks());
|
||||||
if (extension_class) {
|
if (extension_class) {
|
||||||
Object *obj = dynamic_cast<Object *>(this);
|
_notificationv(Object::NOTIFICATION_POSTINITIALIZE);
|
||||||
if (obj) {
|
|
||||||
obj->notification(Object::NOTIFICATION_POSTINITIALIZE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,66 @@ Object *get_object_instance_binding(GodotObject *p_engine_object) {
|
||||||
return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
|
return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypedArray<Dictionary> convert_property_list(const std::vector<PropertyInfo> &p_list) {
|
||||||
|
TypedArray<Dictionary> va;
|
||||||
|
for (const PropertyInfo &pi : p_list) {
|
||||||
|
va.push_back(Dictionary(pi));
|
||||||
|
}
|
||||||
|
return va;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
MethodInfo::operator Dictionary() const {
|
||||||
|
Dictionary dict;
|
||||||
|
dict["name"] = name;
|
||||||
|
dict["args"] = internal::convert_property_list(arguments);
|
||||||
|
Array da;
|
||||||
|
for (int i = 0; i < default_arguments.size(); i++) {
|
||||||
|
da.push_back(default_arguments[i]);
|
||||||
|
}
|
||||||
|
dict["default_args"] = da;
|
||||||
|
dict["flags"] = flags;
|
||||||
|
dict["id"] = id;
|
||||||
|
Dictionary r = return_val;
|
||||||
|
dict["return"] = r;
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
|
||||||
|
MethodInfo mi;
|
||||||
|
|
||||||
|
if (p_dict.has("name")) {
|
||||||
|
mi.name = p_dict["name"];
|
||||||
|
}
|
||||||
|
Array args;
|
||||||
|
if (p_dict.has("args")) {
|
||||||
|
args = p_dict["args"];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < args.size(); i++) {
|
||||||
|
Dictionary d = args[i];
|
||||||
|
mi.arguments.push_back(PropertyInfo::from_dict(d));
|
||||||
|
}
|
||||||
|
Array defargs;
|
||||||
|
if (p_dict.has("default_args")) {
|
||||||
|
defargs = p_dict["default_args"];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < defargs.size(); i++) {
|
||||||
|
mi.default_arguments.push_back(defargs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_dict.has("return")) {
|
||||||
|
mi.return_val = PropertyInfo::from_dict(p_dict["return"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_dict.has("flags")) {
|
||||||
|
mi.flags = p_dict["flags"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
MethodInfo::MethodInfo() :
|
MethodInfo::MethodInfo() :
|
||||||
flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}
|
flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const {
|
||||||
return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
|
return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AABB::is_finite() const {
|
||||||
|
return position.is_finite() && size.is_finite();
|
||||||
|
}
|
||||||
|
|
||||||
AABB AABB::intersection(const AABB &p_aabb) const {
|
AABB AABB::intersection(const AABB &p_aabb) const {
|
||||||
#ifdef MATH_CHECKS
|
#ifdef MATH_CHECKS
|
||||||
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
|
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) {
|
||||||
|
|
|
@ -692,6 +692,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const {
|
||||||
return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
|
return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Basis::is_finite() const {
|
||||||
|
return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite();
|
||||||
|
}
|
||||||
|
|
||||||
bool Basis::operator==(const Basis &p_matrix) const {
|
bool Basis::operator==(const Basis &p_matrix) const {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
for (int j = 0; j < 3; j++) {
|
for (int j = 0; j < 3; j++) {
|
||||||
|
|
|
@ -178,6 +178,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const {
|
||||||
return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
|
return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Plane::is_finite() const {
|
||||||
|
return normal.is_finite() && Math::is_finite(d);
|
||||||
|
}
|
||||||
|
|
||||||
Plane::operator String() const {
|
Plane::operator String() const {
|
||||||
return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
|
return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const {
|
||||||
return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
|
return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Quaternion::is_finite() const {
|
||||||
|
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
|
||||||
|
}
|
||||||
|
|
||||||
real_t Quaternion::length() const {
|
real_t Quaternion::length() const {
|
||||||
return Math::sqrt(length_squared());
|
return Math::sqrt(length_squared());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const {
|
||||||
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
|
return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Rect2::is_finite() const {
|
||||||
|
return position.is_finite() && size.is_finite();
|
||||||
|
}
|
||||||
|
|
||||||
bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const {
|
bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const {
|
||||||
#ifdef MATH_CHECKS
|
#ifdef MATH_CHECKS
|
||||||
if (unlikely(size.x < 0 || size.y < 0)) {
|
if (unlikely(size.x < 0 || size.y < 0)) {
|
||||||
|
|
|
@ -170,6 +170,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const {
|
||||||
return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
|
return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Transform2D::is_finite() const {
|
||||||
|
return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite();
|
||||||
|
}
|
||||||
|
|
||||||
Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
|
Transform2D Transform2D::looking_at(const Vector2 &p_target) const {
|
||||||
Transform2D return_trans = Transform2D(get_rotation(), get_origin());
|
Transform2D return_trans = Transform2D(get_rotation(), get_origin());
|
||||||
Vector2 target_position = affine_inverse().xform(p_target);
|
Vector2 target_position = affine_inverse().xform(p_target);
|
||||||
|
|
|
@ -175,6 +175,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const {
|
||||||
return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
|
return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Transform3D::is_finite() const {
|
||||||
|
return basis.is_finite() && origin.is_finite();
|
||||||
|
}
|
||||||
|
|
||||||
bool Transform3D::operator==(const Transform3D &p_transform) const {
|
bool Transform3D::operator==(const Transform3D &p_transform) const {
|
||||||
return (basis == p_transform.basis && origin == p_transform.origin);
|
return (basis == p_transform.basis && origin == p_transform.origin);
|
||||||
}
|
}
|
||||||
|
|
|
@ -188,6 +188,10 @@ bool Vector2::is_zero_approx() const {
|
||||||
return Math::is_zero_approx(x) && Math::is_zero_approx(y);
|
return Math::is_zero_approx(x) && Math::is_zero_approx(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Vector2::is_finite() const {
|
||||||
|
return Math::is_finite(x) && Math::is_finite(y);
|
||||||
|
}
|
||||||
|
|
||||||
Vector2::operator String() const {
|
Vector2::operator String() const {
|
||||||
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
|
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,10 @@ bool Vector3::is_zero_approx() const {
|
||||||
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
|
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Vector3::is_finite() const {
|
||||||
|
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z);
|
||||||
|
}
|
||||||
|
|
||||||
Vector3::operator String() const {
|
Vector3::operator String() const {
|
||||||
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
|
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,10 @@ bool Vector4::is_zero_approx() const {
|
||||||
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
|
return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Vector4::is_finite() const {
|
||||||
|
return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w);
|
||||||
|
}
|
||||||
|
|
||||||
real_t Vector4::length() const {
|
real_t Vector4::length() const {
|
||||||
return Math::sqrt(length_squared());
|
return Math::sqrt(length_squared());
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,10 @@ func _ready():
|
||||||
control.queue_free()
|
control.queue_free()
|
||||||
sprite.queue_free()
|
sprite.queue_free()
|
||||||
|
|
||||||
|
# Test that passing null for objects works as expected too.
|
||||||
|
var example_null : Example = null
|
||||||
|
assert_equal(example.test_object_cast_to_node(example_null), false)
|
||||||
|
|
||||||
# Test conversions to and from Variant.
|
# Test conversions to and from Variant.
|
||||||
assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1))
|
assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1))
|
||||||
assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1))
|
assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1))
|
||||||
|
|
Loading…
Reference in New Issue