summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--README.md2
-rw-r--r--binding_generator.py66
-rw-r--r--include/godot_cpp/classes/wrapped.hpp5
-rw-r--r--include/godot_cpp/core/class_db.hpp4
-rw-r--r--include/godot_cpp/core/math.hpp8
-rw-r--r--include/godot_cpp/core/method_ptrcall.hpp8
-rw-r--r--include/godot_cpp/core/property_info.hpp38
-rw-r--r--include/godot_cpp/core/type_info.hpp10
-rw-r--r--include/godot_cpp/variant/aabb.hpp1
-rw-r--r--include/godot_cpp/variant/basis.hpp1
-rw-r--r--include/godot_cpp/variant/plane.hpp1
-rw-r--r--include/godot_cpp/variant/quaternion.hpp1
-rw-r--r--include/godot_cpp/variant/rect2.hpp1
-rw-r--r--include/godot_cpp/variant/transform2d.hpp1
-rw-r--r--include/godot_cpp/variant/transform3d.hpp1
-rw-r--r--include/godot_cpp/variant/vector2.hpp1
-rw-r--r--include/godot_cpp/variant/vector3.hpp1
-rw-r--r--include/godot_cpp/variant/vector4.hpp1
-rw-r--r--src/core/object.cpp58
-rw-r--r--src/variant/aabb.cpp4
-rw-r--r--src/variant/basis.cpp4
-rw-r--r--src/variant/plane.cpp4
-rw-r--r--src/variant/projection.cpp2
-rw-r--r--src/variant/quaternion.cpp4
-rw-r--r--src/variant/rect2.cpp4
-rw-r--r--src/variant/transform2d.cpp4
-rw-r--r--src/variant/transform3d.cpp4
-rw-r--r--src/variant/vector2.cpp4
-rw-r--r--src/variant/vector3.cpp4
-rw-r--r--src/variant/vector4.cpp4
-rw-r--r--test/project/main.gd4
-rw-r--r--test/src/register_types.cpp18
-rw-r--r--tools/godotcpp.py73
34 files changed, 322 insertions, 34 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ada01e9..8925852 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -117,7 +117,7 @@ jobs:
- name: Web dependencies
if: ${{ matrix.platform == 'web' }}
- uses: mymindstorm/setup-emsdk@v13
+ uses: mymindstorm/setup-emsdk@v14
with:
version: ${{env.EM_VERSION}}
actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
@@ -134,22 +134,22 @@ jobs:
- name: Generate godot-cpp sources only
run: |
- scons platform=${{ matrix.platform }} build_library=no ${{ matrix.flags }}
+ scons platform=${{ matrix.platform }} verbose=yes build_library=no ${{ matrix.flags }}
scons -c
- name: Build godot-cpp (debug)
run: |
- scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }}
+ scons platform=${{ matrix.platform }} verbose=yes target=template_debug ${{ matrix.flags }}
- name: Build test without rebuilding godot-cpp (debug)
run: |
cd test
- scons platform=${{ matrix.platform }} target=template_debug ${{ matrix.flags }} build_library=no
+ scons platform=${{ matrix.platform }} verbose=yes target=template_debug ${{ matrix.flags }} build_library=no
- name: Build test and godot-cpp (release)
run: |
cd test
- scons platform=${{ matrix.platform }} target=template_release ${{ matrix.flags }}
+ scons platform=${{ matrix.platform }} verbose=yes target=template_release ${{ matrix.flags }}
- name: Download latest Godot artifacts
uses: dsnopek/action-download-artifact@1322f74e2dac9feed2ee76a32d9ae1ca3b4cf4e9
diff --git a/README.md b/README.md
index f4f3be0..ca9a738 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- ClassDB::register_class<Example>();
+ GDREGISTER_CLASS(Example);
}
```
diff --git a/binding_generator.py b/binding_generator.py
index e4e827e..972cbf9 100644
--- a/binding_generator.py
+++ b/binding_generator.py
@@ -309,6 +309,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision=
generate_utility_functions(api, target_dir)
+CLASS_ALIASES = {
+ "ClassDB": "ClassDBSingleton",
+}
+
builtin_classes = []
# Key is class name, value is boolean where True means the class is refcounted.
@@ -1256,9 +1260,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
# First create map of classes and singletons.
for class_api in api["classes"]:
# Generate code for the ClassDB singleton under a different name.
- if class_api["name"] == "ClassDB":
- class_api["name"] = "ClassDBSingleton"
- class_api["alias_for"] = "ClassDB"
+ if class_api["name"] in CLASS_ALIASES:
+ class_api["alias_for"] = class_api["name"]
+ class_api["name"] = CLASS_ALIASES[class_api["alias_for"]]
engine_classes[class_api["name"]] = class_api["is_refcounted"]
for native_struct in api["native_structures"]:
if native_struct["name"] == "ObjectID":
@@ -1268,9 +1272,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node):
for singleton in api["singletons"]:
# Generate code for the ClassDB singleton under a different name.
- if singleton["name"] == "ClassDB":
- singleton["name"] = "ClassDBSingleton"
- singleton["alias_for"] = "ClassDB"
+ if singleton["name"] in CLASS_ALIASES:
+ singleton["alias_for"] = singleton["name"]
+ singleton["name"] = CLASS_ALIASES[singleton["name"]]
singletons.append(singleton["name"])
for class_api in api["classes"]:
@@ -1475,6 +1479,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("#include <type_traits>")
result.append("")
+ if class_name == "ClassDBSingleton":
+ result.append("#include <godot_cpp/core/binder_common.hpp>")
+ result.append("")
+
result.append("namespace godot {")
result.append("")
@@ -1633,6 +1641,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
if class_name == "ClassDBSingleton":
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"]:
# ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them.
if vararg:
@@ -1641,12 +1662,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
continue
method_signature = "\tstatic "
+ return_type = None
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:
- method_signature += (
- correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " "
+ return_type = correct_type(
+ 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:
method_signature += "void "
@@ -1665,8 +1691,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append(method_signature)
method_body = "\t\t"
- if "return_type" in method or "return_value" in method:
+ if return_type is not None:
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 += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments))
method_body += "); \\"
@@ -1676,6 +1704,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("\t;")
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}")
return "\n".join(result)
@@ -2513,7 +2553,7 @@ def correct_typed_array(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"}
if meta != None:
if "int" in meta:
@@ -2529,11 +2569,15 @@ def correct_type(type_name, meta=None):
if is_enum(type_name):
if is_bitfield(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":
return f"BitField<{get_enum_name(type_name)}>"
return f"BitField<{base_class}::{get_enum_name(type_name)}>"
else:
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":
return f"{get_enum_name(type_name)}"
return f"{base_class}::{get_enum_name(type_name)}"
diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp
index 97f9a6e..ce8c968 100644
--- a/include/godot_cpp/classes/wrapped.hpp
+++ b/include/godot_cpp/classes/wrapped.hpp
@@ -214,6 +214,7 @@ protected:
\
public: \
typedef m_class self_type; \
+ typedef m_inherits parent_type; \
\
static void initialize_class() { \
static bool initialized = false; \
@@ -381,6 +382,7 @@ private:
private: \
inline static ::godot::internal::EngineClassRegistration<m_class> _gde_engine_class_registration_helper; \
void operator=(const m_class &p_rval) {} \
+ friend class ::godot::ClassDB; \
\
protected: \
virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \
@@ -390,6 +392,8 @@ protected:
m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \
m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \
\
+ static void _bind_methods() {} \
+ \
static void (*_get_bind_methods())() { \
return nullptr; \
} \
@@ -432,6 +436,7 @@ protected:
\
public: \
typedef m_class self_type; \
+ typedef m_inherits parent_type; \
\
static void initialize_class() {} \
\
diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp
index e240193..31ebddc 100644
--- a/include/godot_cpp/core/class_db.hpp
+++ b/include/godot_cpp/core/class_db.hpp
@@ -203,6 +203,8 @@ public:
template <typename T, bool is_abstract>
void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
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;
// Register this class within our plugin
@@ -337,4 +339,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p
} // namespace godot
+CLASSDB_SINGLETON_VARIANT_CAST;
+
#endif // GODOT_CLASS_DB_HPP
diff --git a/include/godot_cpp/core/math.hpp b/include/godot_cpp/core/math.hpp
index 2cbbe27..1949360 100644
--- a/include/godot_cpp/core/math.hpp
+++ b/include/godot_cpp/core/math.hpp
@@ -613,6 +613,14 @@ inline bool is_inf(double 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) {
// Check for exact equality first, required to handle "infinity" values.
if (a == b) {
diff --git a/include/godot_cpp/core/method_ptrcall.hpp b/include/godot_cpp/core/method_ptrcall.hpp
index ca3327e..b12a7e6 100644
--- a/include/godot_cpp/core/method_ptrcall.hpp
+++ b/include/godot_cpp/core/method_ptrcall.hpp
@@ -170,11 +170,11 @@ template <typename T>
struct PtrToArg<T *> {
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) {
- 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;
_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 *> {
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) {
- 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;
_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;
}
};
diff --git a/include/godot_cpp/core/property_info.hpp b/include/godot_cpp/core/property_info.hpp
index f610f3f..dd71d48 100644
--- a/include/godot_cpp/core/property_info.hpp
+++ b/include/godot_cpp/core/property_info.hpp
@@ -47,9 +47,9 @@ struct PropertyInfo {
Variant::Type type = Variant::NIL;
StringName name;
StringName class_name;
- uint32_t hint = 0;
+ uint32_t hint = PROPERTY_HINT_NONE;
String hint_string;
- uint32_t usage = 7;
+ uint32_t usage = PROPERTY_USAGE_DEFAULT;
PropertyInfo() = default;
@@ -72,6 +72,40 @@ struct PropertyInfo {
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;
diff --git a/include/godot_cpp/core/type_info.hpp b/include/godot_cpp/core/type_info.hpp
index 5896651..096e0ec 100644
--- a/include/godot_cpp/core/type_info.hpp
+++ b/include/godot_cpp/core/type_info.hpp
@@ -58,6 +58,16 @@ struct TypesAreSame<A, A> {
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>
struct TypeInherits {
static D *get_d();
diff --git a/include/godot_cpp/variant/aabb.hpp b/include/godot_cpp/variant/aabb.hpp
index 8f2bce7..b827112 100644
--- a/include/godot_cpp/variant/aabb.hpp
+++ b/include/godot_cpp/variant/aabb.hpp
@@ -65,6 +65,7 @@ struct _NO_DISCARD_ AABB {
bool operator!=(const AABB &p_rval) 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_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
diff --git a/include/godot_cpp/variant/basis.hpp b/include/godot_cpp/variant/basis.hpp
index a365b02..e740a64 100644
--- a/include/godot_cpp/variant/basis.hpp
+++ b/include/godot_cpp/variant/basis.hpp
@@ -128,6 +128,7 @@ struct _NO_DISCARD_ Basis {
}
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;
diff --git a/include/godot_cpp/variant/plane.hpp b/include/godot_cpp/variant/plane.hpp
index 727f4f5..829f801 100644
--- a/include/godot_cpp/variant/plane.hpp
+++ b/include/godot_cpp/variant/plane.hpp
@@ -77,6 +77,7 @@ struct _NO_DISCARD_ Plane {
Plane operator-() const { return Plane(-normal, -d); }
bool is_equal_approx(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;
diff --git a/include/godot_cpp/variant/quaternion.hpp b/include/godot_cpp/variant/quaternion.hpp
index 3816b66..5de91b2 100644
--- a/include/godot_cpp/variant/quaternion.hpp
+++ b/include/godot_cpp/variant/quaternion.hpp
@@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion {
}
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Quaternion &p_quaternion) const;
+ bool is_finite() const;
real_t length() const;
void normalize();
Quaternion normalized() const;
diff --git a/include/godot_cpp/variant/rect2.hpp b/include/godot_cpp/variant/rect2.hpp
index 201e02b..e643035 100644
--- a/include/godot_cpp/variant/rect2.hpp
+++ b/include/godot_cpp/variant/rect2.hpp
@@ -205,6 +205,7 @@ struct _NO_DISCARD_ Rect2 {
}
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; }
diff --git a/include/godot_cpp/variant/transform2d.hpp b/include/godot_cpp/variant/transform2d.hpp
index 5a48398..d73323f 100644
--- a/include/godot_cpp/variant/transform2d.hpp
+++ b/include/godot_cpp/variant/transform2d.hpp
@@ -99,6 +99,7 @@ struct _NO_DISCARD_ Transform2D {
void orthonormalize();
Transform2D orthonormalized() const;
bool is_equal_approx(const Transform2D &p_transform) const;
+ bool is_finite() const;
Transform2D looking_at(const Vector2 &p_target) const;
diff --git a/include/godot_cpp/variant/transform3d.hpp b/include/godot_cpp/variant/transform3d.hpp
index 3a54c0b..6fa5999 100644
--- a/include/godot_cpp/variant/transform3d.hpp
+++ b/include/godot_cpp/variant/transform3d.hpp
@@ -78,6 +78,7 @@ struct _NO_DISCARD_ Transform3D {
void orthogonalize();
Transform3D orthogonalized() 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;
diff --git a/include/godot_cpp/variant/vector2.hpp b/include/godot_cpp/variant/vector2.hpp
index 51dd8cc..8f08985 100644
--- a/include/godot_cpp/variant/vector2.hpp
+++ b/include/godot_cpp/variant/vector2.hpp
@@ -131,6 +131,7 @@ struct _NO_DISCARD_ Vector2 {
bool is_equal_approx(const Vector2 &p_v) const;
bool is_zero_approx() const;
+ bool is_finite() const;
Vector2 operator+(const Vector2 &p_v) const;
void operator+=(const Vector2 &p_v);
diff --git a/include/godot_cpp/variant/vector3.hpp b/include/godot_cpp/variant/vector3.hpp
index 0d3ad62..f256c38 100644
--- a/include/godot_cpp/variant/vector3.hpp
+++ b/include/godot_cpp/variant/vector3.hpp
@@ -157,6 +157,7 @@ struct _NO_DISCARD_ Vector3 {
bool is_equal_approx(const Vector3 &p_v) const;
bool is_zero_approx() const;
+ bool is_finite() const;
/* Operators */
diff --git a/include/godot_cpp/variant/vector4.hpp b/include/godot_cpp/variant/vector4.hpp
index a2e8fae..866e522 100644
--- a/include/godot_cpp/variant/vector4.hpp
+++ b/include/godot_cpp/variant/vector4.hpp
@@ -89,6 +89,7 @@ struct _NO_DISCARD_ Vector4 {
_FORCE_INLINE_ real_t length_squared() const;
bool is_equal_approx(const Vector4 &p_vec4) const;
bool is_zero_approx() const;
+ bool is_finite() const;
real_t length() const;
void normalize();
Vector4 normalized() const;
diff --git a/src/core/object.cpp b/src/core/object.cpp
index dc3c879..d2e10ff 100644
--- a/src/core/object.cpp
+++ b/src/core/object.cpp
@@ -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));
}
+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
+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() :
flags(GDEXTENSION_METHOD_FLAG_NORMAL) {}
diff --git a/src/variant/aabb.cpp b/src/variant/aabb.cpp
index 92e751b..ded17d2 100644
--- a/src/variant/aabb.cpp
+++ b/src/variant/aabb.cpp
@@ -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);
}
+bool AABB::is_finite() const {
+ return position.is_finite() && size.is_finite();
+}
+
AABB AABB::intersection(const AABB &p_aabb) const {
#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)) {
diff --git a/src/variant/basis.cpp b/src/variant/basis.cpp
index 8f78d9f..8d4176e 100644
--- a/src/variant/basis.cpp
+++ b/src/variant/basis.cpp
@@ -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]);
}
+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 {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
diff --git a/src/variant/plane.cpp b/src/variant/plane.cpp
index 53dd439..caea516 100644
--- a/src/variant/plane.cpp
+++ b/src/variant/plane.cpp
@@ -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);
}
+bool Plane::is_finite() const {
+ return normal.is_finite() && Math::is_finite(d);
+}
+
Plane::operator String() const {
return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]";
}
diff --git a/src/variant/projection.cpp b/src/variant/projection.cpp
index c28e651..ddedc93 100644
--- a/src/variant/projection.cpp
+++ b/src/variant/projection.cpp
@@ -136,7 +136,7 @@ Projection Projection::create_for_hmd(int p_eye, real_t p_aspect, real_t p_intra
Projection Projection::create_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) {
Projection proj;
- proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_zfar, p_zfar);
+ proj.set_orthogonal(p_left, p_right, p_bottom, p_top, p_znear, p_zfar);
return proj;
}
diff --git a/src/variant/quaternion.cpp b/src/variant/quaternion.cpp
index 9d4d838..c010850 100644
--- a/src/variant/quaternion.cpp
+++ b/src/variant/quaternion.cpp
@@ -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);
}
+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 {
return Math::sqrt(length_squared());
}
diff --git a/src/variant/rect2.cpp b/src/variant/rect2.cpp
index a70fee6..62730a9 100644
--- a/src/variant/rect2.cpp
+++ b/src/variant/rect2.cpp
@@ -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);
}
+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 {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0)) {
diff --git a/src/variant/transform2d.cpp b/src/variant/transform2d.cpp
index 530a99e..3b2c0b0 100644
--- a/src/variant/transform2d.cpp
+++ b/src/variant/transform2d.cpp
@@ -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]);
}
+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 return_trans = Transform2D(get_rotation(), get_origin());
Vector2 target_position = affine_inverse().xform(p_target);
diff --git a/src/variant/transform3d.cpp b/src/variant/transform3d.cpp
index 1a4189e..d71e919 100644
--- a/src/variant/transform3d.cpp
+++ b/src/variant/transform3d.cpp
@@ -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);
}
+bool Transform3D::is_finite() const {
+ return basis.is_finite() && origin.is_finite();
+}
+
bool Transform3D::operator==(const Transform3D &p_transform) const {
return (basis == p_transform.basis && origin == p_transform.origin);
}
diff --git a/src/variant/vector2.cpp b/src/variant/vector2.cpp
index 409ba50..12201f1 100644
--- a/src/variant/vector2.cpp
+++ b/src/variant/vector2.cpp
@@ -200,6 +200,10 @@ bool Vector2::is_zero_approx() const {
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 {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")";
}
diff --git a/src/variant/vector3.cpp b/src/variant/vector3.cpp
index e660185..d2ad6a9 100644
--- a/src/variant/vector3.cpp
+++ b/src/variant/vector3.cpp
@@ -160,6 +160,10 @@ bool Vector3::is_zero_approx() const {
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 {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")";
}
diff --git a/src/variant/vector4.cpp b/src/variant/vector4.cpp
index 641c696..2f1bb59 100644
--- a/src/variant/vector4.cpp
+++ b/src/variant/vector4.cpp
@@ -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);
}
+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 {
return Math::sqrt(length_squared());
}
diff --git a/test/project/main.gd b/test/project/main.gd
index 5cf483e..0cfba19 100644
--- a/test/project/main.gd
+++ b/test/project/main.gd
@@ -191,6 +191,10 @@ func _ready():
control.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.
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))
diff --git a/test/src/register_types.cpp b/test/src/register_types.cpp
index cbede66..7cfe689 100644
--- a/test/src/register_types.cpp
+++ b/test/src/register_types.cpp
@@ -21,15 +21,15 @@ void initialize_example_module(ModuleInitializationLevel p_level) {
return;
}
- ClassDB::register_class<ExampleRef>();
- ClassDB::register_class<ExampleMin>();
- ClassDB::register_class<Example>();
- ClassDB::register_class<ExampleVirtual>(true);
- ClassDB::register_abstract_class<ExampleAbstractBase>();
- ClassDB::register_class<ExampleConcrete>();
- ClassDB::register_class<ExampleBase>();
- ClassDB::register_class<ExampleChild>();
- ClassDB::register_runtime_class<ExampleRuntime>();
+ GDREGISTER_CLASS(ExampleRef);
+ GDREGISTER_CLASS(ExampleMin);
+ GDREGISTER_CLASS(Example);
+ GDREGISTER_VIRTUAL_CLASS(ExampleVirtual);
+ GDREGISTER_ABSTRACT_CLASS(ExampleAbstractBase);
+ GDREGISTER_CLASS(ExampleConcrete);
+ GDREGISTER_CLASS(ExampleBase);
+ GDREGISTER_CLASS(ExampleChild);
+ GDREGISTER_RUNTIME_CLASS(ExampleRuntime);
}
void uninitialize_example_module(ModuleInitializationLevel p_level) {
diff --git a/tools/godotcpp.py b/tools/godotcpp.py
index 5ba2742..13a57e9 100644
--- a/tools/godotcpp.py
+++ b/tools/godotcpp.py
@@ -3,6 +3,7 @@ import os, sys, platform
from SCons.Variables import EnumVariable, PathVariable, BoolVariable
from SCons.Variables.BoolVariable import _text2bool
from SCons.Tool import Tool
+from SCons.Action import Action
from SCons.Builder import Builder
from SCons.Errors import UserError
from SCons.Script import ARGUMENTS
@@ -66,6 +67,67 @@ def get_custom_platforms(env):
return platforms
+def no_verbose(env):
+ colors = {}
+
+ # Colors are disabled in non-TTY environments such as pipes. This means
+ # that if output is redirected to a file, it will not contain color codes
+ if sys.stdout.isatty():
+ colors["blue"] = "\033[0;94m"
+ colors["bold_blue"] = "\033[1;94m"
+ colors["reset"] = "\033[0m"
+ else:
+ colors["blue"] = ""
+ colors["bold_blue"] = ""
+ colors["reset"] = ""
+
+ # There is a space before "..." to ensure that source file names can be
+ # Ctrl + clicked in the VS Code terminal.
+ compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ java_compile_source_message = "{}Compiling {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ compile_shared_source_message = "{}Compiling shared {}$SOURCE{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ link_program_message = "{}Linking Program {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ link_library_message = "{}Linking Static Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ ranlib_library_message = "{}Ranlib Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ link_shared_library_message = "{}Linking Shared Library {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ java_library_message = "{}Creating Java Archive {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ compiled_resource_message = "{}Creating Compiled Resource {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+ generated_file_message = "{}Generating {}$TARGET{} ...{}".format(
+ colors["blue"], colors["bold_blue"], colors["blue"], colors["reset"]
+ )
+
+ env.Append(CXXCOMSTR=[compile_source_message])
+ env.Append(CCCOMSTR=[compile_source_message])
+ env.Append(SHCCCOMSTR=[compile_shared_source_message])
+ env.Append(SHCXXCOMSTR=[compile_shared_source_message])
+ env.Append(ARCOMSTR=[link_library_message])
+ env.Append(RANLIBCOMSTR=[ranlib_library_message])
+ env.Append(SHLINKCOMSTR=[link_shared_library_message])
+ env.Append(LINKCOMSTR=[link_program_message])
+ env.Append(JARCOMSTR=[java_library_message])
+ env.Append(JAVACCOMSTR=[java_compile_source_message])
+ env.Append(RCCOMSTR=[compiled_resource_message])
+ env.Append(GENCOMSTR=[generated_file_message])
+
+
platforms = ["linux", "macos", "windows", "android", "ios", "web"]
# CPU architecture options.
@@ -254,6 +316,7 @@ def options(opts, env):
)
opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", True))
opts.Add(BoolVariable("dev_build", "Developer build with dev-only debugging code (DEV_ENABLED)", False))
+ opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
# Add platform options (custom tools can override platforms)
for pl in sorted(set(platforms + custom_platforms)):
@@ -381,8 +444,16 @@ def generate(env):
env.Tool("compilation_db")
env.Alias("compiledb", env.CompilationDatabase(normalize_path(env["compiledb_file"], env)))
+ # Formatting
+ if not env["verbose"]:
+ no_verbose(env)
+
# Builders
- env.Append(BUILDERS={"GodotCPPBindings": Builder(action=scons_generate_bindings, emitter=scons_emit_files)})
+ env.Append(
+ BUILDERS={
+ "GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files)
+ }
+ )
env.AddMethod(_godot_cpp, "GodotCPP")