diff options
60 files changed, 1644 insertions, 1026 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index e5ab2fde43..2448123ecb 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -165,7 +165,7 @@ jobs: - name: Check for GDExtension compatibility if: ${{ matrix.api-compat }} run: | - ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" || true # don't fail the CI for now + ./misc/scripts/validate_extension_api.sh "${{ matrix.bin }}" # Download and run the test project - name: Test Godot project diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 545825011a..4f6bcb58b3 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "variant.h" +#include "variant_utility.h" #include "core/core_string_names.h" #include "core/io/marshalls.h" @@ -40,755 +40,772 @@ #include "core/variant/binder_common.h" #include "core/variant/variant_parser.h" -struct VariantUtilityFunctions { - // Math - - static inline double sin(double arg) { - return Math::sin(arg); - } +// Math +double VariantUtilityFunctions::sin(double arg) { + return Math::sin(arg); +} - static inline double cos(double arg) { - return Math::cos(arg); - } +double VariantUtilityFunctions::cos(double arg) { + return Math::cos(arg); +} - static inline double tan(double arg) { - return Math::tan(arg); - } +double VariantUtilityFunctions::tan(double arg) { + return Math::tan(arg); +} - static inline double sinh(double arg) { - return Math::sinh(arg); - } +double VariantUtilityFunctions::sinh(double arg) { + return Math::sinh(arg); +} - static inline double cosh(double arg) { - return Math::cosh(arg); - } +double VariantUtilityFunctions::cosh(double arg) { + return Math::cosh(arg); +} - static inline double tanh(double arg) { - return Math::tanh(arg); - } +double VariantUtilityFunctions::tanh(double arg) { + return Math::tanh(arg); +} - static inline double asin(double arg) { - return Math::asin(arg); - } +double VariantUtilityFunctions::asin(double arg) { + return Math::asin(arg); +} - static inline double acos(double arg) { - return Math::acos(arg); - } +double VariantUtilityFunctions::acos(double arg) { + return Math::acos(arg); +} - static inline double atan(double arg) { - return Math::atan(arg); - } +double VariantUtilityFunctions::atan(double arg) { + return Math::atan(arg); +} - static inline double atan2(double y, double x) { - return Math::atan2(y, x); - } +double VariantUtilityFunctions::atan2(double y, double x) { + return Math::atan2(y, x); +} - static inline double sqrt(double x) { - return Math::sqrt(x); - } +double VariantUtilityFunctions::sqrt(double x) { + return Math::sqrt(x); +} - static inline double fmod(double b, double r) { - return Math::fmod(b, r); - } +double VariantUtilityFunctions::fmod(double b, double r) { + return Math::fmod(b, r); +} - static inline double fposmod(double b, double r) { - return Math::fposmod(b, r); - } +double VariantUtilityFunctions::fposmod(double b, double r) { + return Math::fposmod(b, r); +} - static inline int64_t posmod(int64_t b, int64_t r) { - return Math::posmod(b, r); - } +int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) { + return Math::posmod(b, r); +} - static inline Variant floor(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::floor(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).floor(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).floor(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).floor(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::floor(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).floor(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).floor(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).floor(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double floorf(double x) { - return Math::floor(x); - } +double VariantUtilityFunctions::floorf(double x) { + return Math::floor(x); +} - static inline int64_t floori(double x) { - return int64_t(Math::floor(x)); - } +int64_t VariantUtilityFunctions::floori(double x) { + return int64_t(Math::floor(x)); +} - static inline Variant ceil(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::ceil(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).ceil(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).ceil(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).ceil(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::ceil(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).ceil(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).ceil(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).ceil(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double ceilf(double x) { - return Math::ceil(x); - } +double VariantUtilityFunctions::ceilf(double x) { + return Math::ceil(x); +} - static inline int64_t ceili(double x) { - return int64_t(Math::ceil(x)); - } +int64_t VariantUtilityFunctions::ceili(double x) { + return int64_t(Math::ceil(x)); +} - static inline Variant round(Variant x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return VariantInternalAccessor<int64_t>::get(&x); - } break; - case Variant::FLOAT: { - return Math::round(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).round(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).round(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).round(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return VariantInternalAccessor<int64_t>::get(&x); + } break; + case Variant::FLOAT: { + return Math::round(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).round(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).round(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).round(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double roundf(double x) { - return Math::round(x); - } +double VariantUtilityFunctions::roundf(double x) { + return Math::round(x); +} - static inline int64_t roundi(double x) { - return int64_t(Math::round(x)); - } +int64_t VariantUtilityFunctions::roundi(double x) { + return int64_t(Math::round(x)); +} - static inline Variant abs(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return ABS(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return Math::absd(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).abs(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).abs(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).abs(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).abs(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).abs(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).abs(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return ABS(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return Math::absd(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).abs(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).abs(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).abs(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).abs(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).abs(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).abs(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double absf(double x) { - return Math::absd(x); - } +double VariantUtilityFunctions::absf(double x) { + return Math::absd(x); +} - static inline int64_t absi(int64_t x) { - return ABS(x); - } +int64_t VariantUtilityFunctions::absi(int64_t x) { + return ABS(x); +} - static inline Variant sign(const Variant &x, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - switch (x.get_type()) { - case Variant::INT: { - return SIGN(VariantInternalAccessor<int64_t>::get(&x)); - } break; - case Variant::FLOAT: { - return SIGN(VariantInternalAccessor<double>::get(&x)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).sign(); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).sign(); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).sign(); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).sign(); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).sign(); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).sign(); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } +Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + switch (x.get_type()) { + case Variant::INT: { + return SIGN(VariantInternalAccessor<int64_t>::get(&x)); + } break; + case Variant::FLOAT: { + return SIGN(VariantInternalAccessor<double>::get(&x)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).sign(); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).sign(); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).sign(); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).sign(); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).sign(); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).sign(); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; + return Variant(); } } +} - static inline double signf(double x) { - return SIGN(x); - } +double VariantUtilityFunctions::signf(double x) { + return SIGN(x); +} - static inline int64_t signi(int64_t x) { - return SIGN(x); - } +int64_t VariantUtilityFunctions::signi(int64_t x) { + return SIGN(x); +} - static inline double pow(double x, double y) { - return Math::pow(x, y); - } +double VariantUtilityFunctions::pow(double x, double y) { + return Math::pow(x, y); +} - static inline double log(double x) { - return Math::log(x); - } +double VariantUtilityFunctions::log(double x) { + return Math::log(x); +} - static inline double exp(double x) { - return Math::exp(x); - } +double VariantUtilityFunctions::exp(double x) { + return Math::exp(x); +} - static inline bool is_nan(double x) { - return Math::is_nan(x); - } +bool VariantUtilityFunctions::is_nan(double x) { + return Math::is_nan(x); +} - static inline bool is_inf(double x) { - return Math::is_inf(x); - } +bool VariantUtilityFunctions::is_inf(double x) { + return Math::is_inf(x); +} - static inline bool is_equal_approx(double x, double y) { - return Math::is_equal_approx(x, y); - } +bool VariantUtilityFunctions::is_equal_approx(double x, double y) { + return Math::is_equal_approx(x, y); +} - static inline bool is_zero_approx(double x) { - return Math::is_zero_approx(x); - } +bool VariantUtilityFunctions::is_zero_approx(double x) { + return Math::is_zero_approx(x); +} - static inline bool is_finite(double x) { - return Math::is_finite(x); - } +bool VariantUtilityFunctions::is_finite(double x) { + return Math::is_finite(x); +} - static inline double ease(float x, float curve) { - return Math::ease(x, curve); - } +double VariantUtilityFunctions::ease(float x, float curve) { + return Math::ease(x, curve); +} - static inline int step_decimals(float step) { - return Math::step_decimals(step); - } +int VariantUtilityFunctions::step_decimals(float step) { + return Math::step_decimals(step); +} - static inline Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; +Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + return Variant(); + } + + switch (step.get_type()) { + case Variant::INT: { + return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); + } break; + case Variant::FLOAT: { + return snappedf(x, VariantInternalAccessor<double>::get(&step)); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); + } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); + } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); + } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (step.get_type()) { - case Variant::INT: { - return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); - } break; - case Variant::FLOAT: { - return snappedf(x, VariantInternalAccessor<double>::get(&step)); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&x).snapped(VariantInternalAccessor<Vector2>::get(&step)); - } break; - case Variant::VECTOR2I: { - return VariantInternalAccessor<Vector2i>::get(&x).snapped(VariantInternalAccessor<Vector2i>::get(&step)); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&x).snapped(VariantInternalAccessor<Vector3>::get(&step)); - } break; - case Variant::VECTOR3I: { - return VariantInternalAccessor<Vector3i>::get(&x).snapped(VariantInternalAccessor<Vector3i>::get(&step)); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&x).snapped(VariantInternalAccessor<Vector4>::get(&step)); - } break; - case Variant::VECTOR4I: { - return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double snappedf(double x, double step) { - return Math::snapped(x, step); - } +double VariantUtilityFunctions::snappedf(double x, double step) { + return Math::snapped(x, step); +} - static inline int64_t snappedi(double x, int64_t step) { - return Math::snapped(x, step); - } +int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) { + return Math::snapped(x, step); +} - static inline Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (from.get_type() != to.get_type()) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = from.get_type(); - r_error.argument = 1; +Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { + r_error.error = Callable::CallError::CALL_OK; + if (from.get_type() != to.get_type()) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = from.get_type(); + r_error.argument = 1; + return Variant(); + } + + switch (from.get_type()) { + case Variant::INT: { + return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); + } break; + case Variant::FLOAT: { + return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); + } break; + case Variant::VECTOR2: { + return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); + } break; + case Variant::VECTOR3: { + return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); + } break; + case Variant::VECTOR4: { + return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); + } break; + case Variant::QUATERNION: { + return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); + } break; + case Variant::BASIS: { + return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); + } break; + case Variant::COLOR: { + return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); + } break; + default: { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; return Variant(); } - - switch (from.get_type()) { - case Variant::INT: { - return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); - } break; - case Variant::FLOAT: { - return lerpf(VariantInternalAccessor<double>::get(&from), to, weight); - } break; - case Variant::VECTOR2: { - return VariantInternalAccessor<Vector2>::get(&from).lerp(VariantInternalAccessor<Vector2>::get(&to), weight); - } break; - case Variant::VECTOR3: { - return VariantInternalAccessor<Vector3>::get(&from).lerp(VariantInternalAccessor<Vector3>::get(&to), weight); - } break; - case Variant::VECTOR4: { - return VariantInternalAccessor<Vector4>::get(&from).lerp(VariantInternalAccessor<Vector4>::get(&to), weight); - } break; - case Variant::QUATERNION: { - return VariantInternalAccessor<Quaternion>::get(&from).slerp(VariantInternalAccessor<Quaternion>::get(&to), weight); - } break; - case Variant::BASIS: { - return VariantInternalAccessor<Basis>::get(&from).slerp(VariantInternalAccessor<Basis>::get(&to), weight); - } break; - case Variant::COLOR: { - return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); - } break; - default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } - } } +} - static inline double lerpf(double from, double to, double weight) { - return Math::lerp(from, to, weight); - } +double VariantUtilityFunctions::lerpf(double from, double to, double weight) { + return Math::lerp(from, to, weight); +} - static inline double cubic_interpolate(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate(from, to, pre, post, weight); +} - static inline double cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { - return Math::cubic_interpolate_angle(from, to, pre, post, weight); - } +double VariantUtilityFunctions::cubic_interpolate_angle(double from, double to, double pre, double post, double weight) { + return Math::cubic_interpolate_angle(from, to, pre, post, weight); +} - static inline double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, - double to_t, double pre_t, double post_t) { - return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); - } +double VariantUtilityFunctions::cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t) { + return Math::cubic_interpolate_angle_in_time(from, to, pre, post, weight, to_t, pre_t, post_t); +} - static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { - return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); - } +double VariantUtilityFunctions::bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) { + return Math::bezier_derivative(p_start, p_control_1, p_control_2, p_end, p_t); +} - static inline double lerp_angle(double from, double to, double weight) { - return Math::lerp_angle(from, to, weight); - } +double VariantUtilityFunctions::lerp_angle(double from, double to, double weight) { + return Math::lerp_angle(from, to, weight); +} - static inline double inverse_lerp(double from, double to, double weight) { - return Math::inverse_lerp(from, to, weight); - } +double VariantUtilityFunctions::inverse_lerp(double from, double to, double weight) { + return Math::inverse_lerp(from, to, weight); +} - static inline double remap(double value, double istart, double istop, double ostart, double ostop) { - return Math::remap(value, istart, istop, ostart, ostop); - } +double VariantUtilityFunctions::remap(double value, double istart, double istop, double ostart, double ostop) { + return Math::remap(value, istart, istop, ostart, ostop); +} - static inline double smoothstep(double from, double to, double val) { - return Math::smoothstep(from, to, val); - } +double VariantUtilityFunctions::smoothstep(double from, double to, double val) { + return Math::smoothstep(from, to, val); +} - static inline double move_toward(double from, double to, double delta) { - return Math::move_toward(from, to, delta); - } +double VariantUtilityFunctions::move_toward(double from, double to, double delta) { + return Math::move_toward(from, to, delta); +} - static inline double deg_to_rad(double angle_deg) { - return Math::deg_to_rad(angle_deg); - } +double VariantUtilityFunctions::deg_to_rad(double angle_deg) { + return Math::deg_to_rad(angle_deg); +} - static inline double rad_to_deg(double angle_rad) { - return Math::rad_to_deg(angle_rad); - } +double VariantUtilityFunctions::rad_to_deg(double angle_rad) { + return Math::rad_to_deg(angle_rad); +} - static inline double linear_to_db(double linear) { - return Math::linear_to_db(linear); - } +double VariantUtilityFunctions::linear_to_db(double linear) { + return Math::linear_to_db(linear); +} + +double VariantUtilityFunctions::db_to_linear(double db) { + return Math::db_to_linear(db); +} - static inline double db_to_linear(double db) { - return Math::db_to_linear(db); +Variant VariantUtilityFunctions::wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { + Variant::Type x_type = p_x.get_type(); + if (x_type != Variant::INT && x_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = x_type; + return Variant(); } - static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) { - Variant::Type x_type = p_x.get_type(); - if (x_type != Variant::INT && x_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = x_type; - return Variant(); - } + Variant::Type min_type = p_min.get_type(); + if (min_type != Variant::INT && min_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = x_type; + return Variant(); + } - Variant::Type min_type = p_min.get_type(); - if (min_type != Variant::INT && min_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = x_type; - return Variant(); - } + Variant::Type max_type = p_max.get_type(); + if (max_type != Variant::INT && max_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 2; + r_error.expected = x_type; + return Variant(); + } - Variant::Type max_type = p_max.get_type(); - if (max_type != Variant::INT && max_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 2; - r_error.expected = x_type; - return Variant(); - } + Variant value; - Variant value; - - switch (x_type) { - case Variant::INT: { - if (x_type != min_type || x_type != max_type) { - value = wrapf((double)p_x, (double)p_min, (double)p_max); - } else { - value = wrapi((int)p_x, (int)p_min, (int)p_max); - } - } break; - case Variant::FLOAT: { + switch (x_type) { + case Variant::INT: { + if (x_type != min_type || x_type != max_type) { value = wrapf((double)p_x, (double)p_min, (double)p_max); - } break; - default: - break; - } - - r_error.error = Callable::CallError::CALL_OK; - return value; + } else { + value = wrapi((int)p_x, (int)p_min, (int)p_max); + } + } break; + case Variant::FLOAT: { + value = wrapf((double)p_x, (double)p_min, (double)p_max); + } break; + default: + break; } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { - return Math::wrapi(value, min, max); - } + r_error.error = Callable::CallError::CALL_OK; + return value; +} - static inline double wrapf(double value, double min, double max) { - return Math::wrapf(value, min, max); - } +int64_t VariantUtilityFunctions::wrapi(int64_t value, int64_t min, int64_t max) { + return Math::wrapi(value, min, max); +} - static inline double pingpong(double value, double length) { - return Math::pingpong(value, length); +double VariantUtilityFunctions::wrapf(double value, double min, double max) { + return Math::wrapf(value, min, max); +} + +double VariantUtilityFunctions::pingpong(double value, double length) { + return Math::pingpong(value, length); +} + +Variant VariantUtilityFunctions::max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); } + Variant base = *p_args[0]; + Variant ret; - static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (i == 0) { + continue; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double maxf(double x, double y) { - return MAX(x, y); - } - - static inline int64_t maxi(int64_t x, int64_t y) { - return MAX(x, y); - } - - static inline Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.expected = 2; + bool valid; + Variant::evaluate(Variant::OP_LESS, base, *p_args[i], ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } - Variant base = *p_args[0]; - Variant ret; - - for (int i = 0; i < p_argcount; i++) { - Variant::Type arg_type = p_args[i]->get_type(); - if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = Variant::FLOAT; - r_error.argument = i; - return Variant(); - } - if (i == 0) { - continue; - } - bool valid; - Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); - if (!valid) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = base.get_type(); - r_error.argument = i; - return Variant(); - } - if (ret.booleanize()) { - base = *p_args[i]; - } + if (ret.booleanize()) { + base = *p_args[i]; } - r_error.error = Callable::CallError::CALL_OK; - return base; - } - - static inline double minf(double x, double y) { - return MIN(x, y); } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - static inline int64_t mini(int64_t x, int64_t y) { - return MIN(x, y); - } +double VariantUtilityFunctions::maxf(double x, double y) { + return MAX(x, y); +} - static inline Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { - Variant value = x; +int64_t VariantUtilityFunctions::maxi(int64_t x, int64_t y) { + return MAX(x, y); +} - Variant ret; +Variant VariantUtilityFunctions::min(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.expected = 2; + return Variant(); + } + Variant base = *p_args[0]; + Variant ret; - bool valid; - Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); - if (!valid) { + for (int i = 0; i < p_argcount; i++) { + Variant::Type arg_type = p_args[i]->get_type(); + if (arg_type != Variant::INT && arg_type != Variant::FLOAT) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 1; + r_error.expected = Variant::FLOAT; + r_error.argument = i; return Variant(); } - if (ret.booleanize()) { - value = min; + if (i == 0) { + continue; } - Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + bool valid; + Variant::evaluate(Variant::OP_GREATER, base, *p_args[i], ret, valid); if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.expected = value.get_type(); - r_error.argument = 2; + r_error.expected = base.get_type(); + r_error.argument = i; return Variant(); } if (ret.booleanize()) { - value = max; + base = *p_args[i]; } + } + r_error.error = Callable::CallError::CALL_OK; + return base; +} - r_error.error = Callable::CallError::CALL_OK; +double VariantUtilityFunctions::minf(double x, double y) { + return MIN(x, y); +} - return value; - } +int64_t VariantUtilityFunctions::mini(int64_t x, int64_t y) { + return MIN(x, y); +} - static inline double clampf(double x, double min, double max) { - return CLAMP(x, min, max); - } +Variant VariantUtilityFunctions::clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error) { + Variant value = x; - static inline int64_t clampi(int64_t x, int64_t min, int64_t max) { - return CLAMP(x, min, max); - } + Variant ret; - static inline int64_t nearest_po2(int64_t x) { - return nearest_power_of_2_templated(uint64_t(x)); + bool valid; + Variant::evaluate(Variant::OP_LESS, value, min, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 1; + return Variant(); + } + if (ret.booleanize()) { + value = min; + } + Variant::evaluate(Variant::OP_GREATER, value, max, ret, valid); + if (!valid) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.expected = value.get_type(); + r_error.argument = 2; + return Variant(); + } + if (ret.booleanize()) { + value = max; } - // Random + r_error.error = Callable::CallError::CALL_OK; - static inline void randomize() { - Math::randomize(); - } + return value; +} - static inline int64_t randi() { - return Math::rand(); - } +double VariantUtilityFunctions::clampf(double x, double min, double max) { + return CLAMP(x, min, max); +} - static inline double randf() { - return Math::randf(); - } +int64_t VariantUtilityFunctions::clampi(int64_t x, int64_t min, int64_t max) { + return CLAMP(x, min, max); +} - static inline double randfn(double mean, double deviation) { - return Math::randfn(mean, deviation); - } +int64_t VariantUtilityFunctions::nearest_po2(int64_t x) { + return nearest_power_of_2_templated(uint64_t(x)); +} - static inline int64_t randi_range(int64_t from, int64_t to) { - return Math::random((int32_t)from, (int32_t)to); - } +// Random - static inline double randf_range(double from, double to) { - return Math::random(from, to); - } +void VariantUtilityFunctions::randomize() { + Math::randomize(); +} - static inline void seed(int64_t s) { - return Math::seed(s); - } +int64_t VariantUtilityFunctions::randi() { + return Math::rand(); +} - static inline PackedInt64Array rand_from_seed(int64_t seed) { - uint64_t s = seed; - PackedInt64Array arr; - arr.resize(2); - arr.write[0] = Math::rand_from_seed(&s); - arr.write[1] = s; - return arr; - } +double VariantUtilityFunctions::randf() { + return Math::randf(); +} - // Utility +double VariantUtilityFunctions::randfn(double mean, double deviation) { + return Math::randfn(mean, deviation); +} - static inline Variant weakref(const Variant &obj, Callable::CallError &r_error) { - if (obj.get_type() == Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_OK; - if (obj.is_ref_counted()) { - Ref<WeakRef> wref = memnew(WeakRef); - Ref<RefCounted> r = obj; - if (r.is_valid()) { - wref->set_ref(r); - } - return wref; - } else { - Ref<WeakRef> wref = memnew(WeakRef); - Object *o = obj.get_validated_object(); - if (o) { - wref->set_obj(o); - } - return wref; - } - } else if (obj.get_type() == Variant::NIL) { - r_error.error = Callable::CallError::CALL_OK; +int64_t VariantUtilityFunctions::randi_range(int64_t from, int64_t to) { + return Math::random((int32_t)from, (int32_t)to); +} + +double VariantUtilityFunctions::randf_range(double from, double to) { + return Math::random(from, to); +} + +void VariantUtilityFunctions::seed(int64_t s) { + return Math::seed(s); +} + +PackedInt64Array VariantUtilityFunctions::rand_from_seed(int64_t seed) { + uint64_t s = seed; + PackedInt64Array arr; + arr.resize(2); + arr.write[0] = Math::rand_from_seed(&s); + arr.write[1] = s; + return arr; +} + +// Utility + +Variant VariantUtilityFunctions::weakref(const Variant &obj, Callable::CallError &r_error) { + if (obj.get_type() == Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_OK; + if (obj.is_ref_counted()) { Ref<WeakRef> wref = memnew(WeakRef); + Ref<RefCounted> r = obj; + if (r.is_valid()) { + wref->set_ref(r); + } return wref; } else { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return Variant(); + Ref<WeakRef> wref = memnew(WeakRef); + Object *o = obj.get_validated_object(); + if (o) { + wref->set_obj(o); + } + return wref; } + } else if (obj.get_type() == Variant::NIL) { + r_error.error = Callable::CallError::CALL_OK; + Ref<WeakRef> wref = memnew(WeakRef); + return wref; + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return Variant(); } +} - static inline int64_t _typeof(const Variant &obj) { - return obj.get_type(); +int64_t VariantUtilityFunctions::_typeof(const Variant &obj) { + return obj.get_type(); +} + +String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; + return String(); } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; - return String(); + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + r_error.error = Callable::CallError::CALL_OK; - r_error.error = Callable::CallError::CALL_OK; + return s; +} - return s; +String VariantUtilityFunctions::error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); } - static inline String error_string(Error error) { - if (error < 0 || error >= ERR_MAX) { - return String("(invalid error code)"); - } + return String(error_names[error]); +} - return String(error_names[error]); +void VariantUtilityFunctions::print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + if (i == 0) { + s = os; + } else { + s += os; + } } - static inline void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + print_line_rich(s); + r_error.error = Callable::CallError::CALL_OK; +} + +#undef print_verbose + +void VariantUtilityFunctions::print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (OS::get_singleton()->is_stdout_verbose()) { String s; for (int i = 0; i < p_arg_count; i++) { String os = p_args[i]->operator String(); @@ -800,247 +817,227 @@ struct VariantUtilityFunctions { } } - print_line_rich(s); - r_error.error = Callable::CallError::CALL_OK; + // No need to use `print_verbose()` as this call already only happens + // when verbose mode is enabled. This avoids performing string argument concatenation + // when not needed. + print_line(s); } -#undef print_verbose + r_error.error = Callable::CallError::CALL_OK; +} - static inline void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (OS::get_singleton()->is_stdout_verbose()) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); - - if (i == 0) { - s = os; - } else { - s += os; - } - } +void VariantUtilityFunctions::printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - // No need to use `print_verbose()` as this call already only happens - // when verbose mode is enabled. This avoids performing string argument concatenation - // when not needed. - print_line(s); + if (i == 0) { + s = os; + } else { + s += os; } - - r_error.error = Callable::CallError::CALL_OK; } - static inline void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_error(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += "\t"; } - - print_error(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += "\t"; - } - s += p_args[i]->operator String(); - } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; - } + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - static inline void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - if (i) { - s += " "; - } - s += p_args[i]->operator String(); +void VariantUtilityFunctions::prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + if (i) { + s += " "; } - - print_line(s); - r_error.error = Callable::CallError::CALL_OK; + s += p_args[i]->operator String(); } - static inline void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + print_line(s); + r_error.error = Callable::CallError::CALL_OK; +} - if (i == 0) { - s = os; - } else { - s += os; - } +void VariantUtilityFunctions::printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); + + if (i == 0) { + s = os; + } else { + s += os; } + } - OS::get_singleton()->print("%s", s.utf8().get_data()); - r_error.error = Callable::CallError::CALL_OK; + OS::get_singleton()->print("%s", s.utf8().get_data()); + r_error.error = Callable::CallError::CALL_OK; +} + +void VariantUtilityFunctions::push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + ERR_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - ERR_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; +void VariantUtilityFunctions::push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { + if (p_arg_count < 1) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 1; } + String s; + for (int i = 0; i < p_arg_count; i++) { + String os = p_args[i]->operator String(); - static inline void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { - if (p_arg_count < 1) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 1; + if (i == 0) { + s = os; + } else { + s += os; } - String s; - for (int i = 0; i < p_arg_count; i++) { - String os = p_args[i]->operator String(); + } - if (i == 0) { - s = os; - } else { - s += os; - } - } + WARN_PRINT(s); + r_error.error = Callable::CallError::CALL_OK; +} - WARN_PRINT(s); - r_error.error = Callable::CallError::CALL_OK; - } +String VariantUtilityFunctions::var_to_str(const Variant &p_var) { + String vars; + VariantWriter::write_to_string(p_var, vars); + return vars; +} - static inline String var_to_str(const Variant &p_var) { - String vars; - VariantWriter::write_to_string(p_var, vars); - return vars; - } +Variant VariantUtilityFunctions::str_to_var(const String &p_var) { + VariantParser::StreamString ss; + ss.s = p_var; - static inline Variant str_to_var(const String &p_var) { - VariantParser::StreamString ss; - ss.s = p_var; + String errs; + int line; + Variant ret; + (void)VariantParser::parse(&ss, ret, errs, line); - String errs; - int line; - Variant ret; - (void)VariantParser::parse(&ss, ret, errs, line); + return ret; +} - return ret; +PackedByteArray VariantUtilityFunctions::var_to_bytes(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, false); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, false); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, false); if (err != OK) { return PackedByteArray(); } + } - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, false); - if (err != OK) { - return PackedByteArray(); - } - } + return barr; +} - return barr; +PackedByteArray VariantUtilityFunctions::var_to_bytes_with_objects(const Variant &p_var) { + int len; + Error err = encode_variant(p_var, nullptr, len, true); + if (err != OK) { + return PackedByteArray(); } - static inline PackedByteArray var_to_bytes_with_objects(const Variant &p_var) { - int len; - Error err = encode_variant(p_var, nullptr, len, true); + PackedByteArray barr; + barr.resize(len); + { + uint8_t *w = barr.ptrw(); + err = encode_variant(p_var, w, len, true); if (err != OK) { return PackedByteArray(); } - - PackedByteArray barr; - barr.resize(len); - { - uint8_t *w = barr.ptrw(); - err = encode_variant(p_var, w, len, true); - if (err != OK) { - return PackedByteArray(); - } - } - - return barr; } - static inline Variant bytes_to_var(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); - if (err != OK) { - return Variant(); - } + return barr; +} + +Variant VariantUtilityFunctions::bytes_to_var(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, false); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline Variant bytes_to_var_with_objects(const PackedByteArray &p_arr) { - Variant ret; - { - const uint8_t *r = p_arr.ptr(); - Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); - if (err != OK) { - return Variant(); - } +Variant VariantUtilityFunctions::bytes_to_var_with_objects(const PackedByteArray &p_arr) { + Variant ret; + { + const uint8_t *r = p_arr.ptr(); + Error err = decode_variant(ret, r, p_arr.size(), nullptr, true); + if (err != OK) { + return Variant(); } - return ret; } + return ret; +} - static inline int64_t hash(const Variant &p_arr) { - return p_arr.hash(); - } +int64_t VariantUtilityFunctions::hash(const Variant &p_arr) { + return p_arr.hash(); +} - static inline Object *instance_from_id(int64_t p_id) { - ObjectID id = ObjectID((uint64_t)p_id); - Object *ret = ObjectDB::get_instance(id); - return ret; - } +Object *VariantUtilityFunctions::instance_from_id(int64_t p_id) { + ObjectID id = ObjectID((uint64_t)p_id); + Object *ret = ObjectDB::get_instance(id); + return ret; +} - static inline bool is_instance_id_valid(int64_t p_id) { - return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; - } +bool VariantUtilityFunctions::is_instance_id_valid(int64_t p_id) { + return ObjectDB::get_instance(ObjectID((uint64_t)p_id)) != nullptr; +} - static inline bool is_instance_valid(const Variant &p_instance) { - if (p_instance.get_type() != Variant::OBJECT) { - return false; - } - return p_instance.get_validated_object() != nullptr; +bool VariantUtilityFunctions::is_instance_valid(const Variant &p_instance) { + if (p_instance.get_type() != Variant::OBJECT) { + return false; } + return p_instance.get_validated_object() != nullptr; +} - static inline uint64_t rid_allocate_id() { - return RID_AllocBase::_gen_id(); - } +uint64_t VariantUtilityFunctions::rid_allocate_id() { + return RID_AllocBase::_gen_id(); +} - static inline RID rid_from_int64(uint64_t p_base) { - return RID::from_uint64(p_base); - } +RID VariantUtilityFunctions::rid_from_int64(uint64_t p_base) { + return RID::from_uint64(p_base); +} - static inline bool is_same(const Variant &p_a, const Variant &p_b) { - return p_a.identity_compare(p_b); - } -}; +bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); +} #ifdef DEBUG_METHODS_ENABLED #define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...) diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h new file mode 100644 index 0000000000..78f66987cb --- /dev/null +++ b/core/variant/variant_utility.h @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* variant_utility.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef VARIANT_UTILITY_H +#define VARIANT_UTILITY_H + +#include "variant.h" + +struct VariantUtilityFunctions { + // Math + static double sin(double arg); + static double cos(double arg); + static double tan(double arg); + static double sinh(double arg); + static double cosh(double arg); + static double tanh(double arg); + static double asin(double arg); + static double acos(double arg); + static double atan(double arg); + static double atan2(double y, double x); + static double sqrt(double x); + static double fmod(double b, double r); + static double fposmod(double b, double r); + static int64_t posmod(int64_t b, int64_t r); + static Variant floor(Variant x, Callable::CallError &r_error); + static double floorf(double x); + static int64_t floori(double x); + static Variant ceil(Variant x, Callable::CallError &r_error); + static double ceilf(double x); + static int64_t ceili(double x); + static Variant round(Variant x, Callable::CallError &r_error); + static double roundf(double x); + static int64_t roundi(double x); + static Variant abs(const Variant &x, Callable::CallError &r_error); + static double absf(double x); + static int64_t absi(int64_t x); + static Variant sign(const Variant &x, Callable::CallError &r_error); + static double signf(double x); + static int64_t signi(int64_t x); + static double pow(double x, double y); + static double log(double x); + static double exp(double x); + static bool is_nan(double x); + static bool is_inf(double x); + static bool is_equal_approx(double x, double y); + static bool is_zero_approx(double x); + static bool is_finite(double x); + static double ease(float x, float curve); + static int step_decimals(float step); + static Variant snapped(const Variant &x, const Variant &step, Callable::CallError &r_error); + static double snappedf(double x, double step); + static int64_t snappedi(double x, int64_t step); + static Variant lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error); + static double lerpf(double from, double to, double weight); + static double cubic_interpolate(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_angle(double from, double to, double pre, double post, double weight); + static double cubic_interpolate_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double cubic_interpolate_angle_in_time(double from, double to, double pre, double post, double weight, + double to_t, double pre_t, double post_t); + static double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double bezier_derivative(double p_start, double p_control_1, double p_control_2, double p_end, double p_t); + static double lerp_angle(double from, double to, double weight); + static double inverse_lerp(double from, double to, double weight); + static double remap(double value, double istart, double istop, double ostart, double ostop); + static double smoothstep(double from, double to, double val); + static double move_toward(double from, double to, double delta); + static double deg_to_rad(double angle_deg); + static double rad_to_deg(double angle_rad); + static double linear_to_db(double linear); + static double db_to_linear(double db); + static Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error); + static int64_t wrapi(int64_t value, int64_t min, int64_t max); + static double wrapf(double value, double min, double max); + static double pingpong(double value, double length); + static Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double maxf(double x, double y); + static int64_t maxi(int64_t x, int64_t y); + static Variant min(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + static double minf(double x, double y); + static int64_t mini(int64_t x, int64_t y); + static Variant clamp(const Variant &x, const Variant &min, const Variant &max, Callable::CallError &r_error); + static double clampf(double x, double min, double max); + static int64_t clampi(int64_t x, int64_t min, int64_t max); + static int64_t nearest_po2(int64_t x); + // Random + static void randomize(); + static int64_t randi(); + static double randf(); + static double randfn(double mean, double deviation); + static int64_t randi_range(int64_t from, int64_t to); + static double randf_range(double from, double to); + static void seed(int64_t s); + static PackedInt64Array rand_from_seed(int64_t seed); + // Utility + static Variant weakref(const Variant &obj, Callable::CallError &r_error); + static int64_t _typeof(const Variant &obj); + static Variant type_convert(const Variant &p_variant, const Variant::Type p_type); + static String str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String error_string(Error error); + static void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void print_rich(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); +#undef print_verbose + static void print_verbose(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printerr(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printt(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void prints(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void printraw(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_error(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static void push_warning(const Variant **p_args, int p_arg_count, Callable::CallError &r_error); + static String var_to_str(const Variant &p_var); + static Variant str_to_var(const String &p_var); + static PackedByteArray var_to_bytes(const Variant &p_var); + static PackedByteArray var_to_bytes_with_objects(const Variant &p_var); + static Variant bytes_to_var(const PackedByteArray &p_arr); + static Variant bytes_to_var_with_objects(const PackedByteArray &p_arr); + static int64_t hash(const Variant &p_arr); + static Object *instance_from_id(int64_t p_id); + static bool is_instance_id_valid(int64_t p_id); + static bool is_instance_valid(const Variant &p_instance); + static uint64_t rid_allocate_id(); + static RID rid_from_int64(uint64_t p_base); + static bool is_same(const Variant &p_a, const Variant &p_b); +}; + +#endif // VARIANT_UTILITY_H diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index b18ee95a43..c5998ad8a1 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -17,7 +17,7 @@ </method> <method name="_toggled" qualifiers="virtual"> <return type="void" /> - <param index="0" name="button_pressed" type="bool" /> + <param index="0" name="toggled_on" type="bool" /> <description> Called when the button is toggled (only if [member toggle_mode] is active). </description> @@ -98,9 +98,9 @@ </description> </signal> <signal name="toggled"> - <param index="0" name="button_pressed" type="bool" /> + <param index="0" name="toggled_on" type="bool" /> <description> - Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param button_pressed] argument. + Emitted when the button was just toggled between pressed and normal states (only if [member toggle_mode] is active). The new state is contained in the [param toggled_on] argument. </description> </signal> </signals> diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index fdabb110c6..4f17e3cf2d 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -83,7 +83,7 @@ <return type="Rect2" /> <param index="0" name="to" type="Vector2" /> <description> - Returns a copy of this rectangle expanded to include the given [param to] point, if necessary. + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] var rect = Rect2(0, 0, 5, 2) diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index 79d9e3f6df..c021a1be26 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -82,7 +82,7 @@ <return type="Rect2i" /> <param index="0" name="to" type="Vector2i" /> <description> - Returns a copy of this rectangle expanded to include the given [param to] point, if necessary. + Returns a copy of this rectangle expanded to align the edges with the given [param to] point, if necessary. [codeblocks] [gdscript] var rect = Rect2i(0, 0, 5, 2) diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 5790fc347a..82498b9ba4 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -10,6 +10,12 @@ <tutorials> </tutorials> <methods> + <method name="_get_contents_minimum_size" qualifiers="virtual const"> + <return type="Vector2" /> + <description> + Virtual method to be implemented by the user. Overrides the value returned by [method get_contents_minimum_size]. + </description> + </method> <method name="add_theme_color_override"> <return type="void" /> <param index="0" name="name" type="StringName" /> @@ -92,6 +98,7 @@ <return type="Vector2" /> <description> Returns the combined minimum size from the child [Control] nodes of the window. Use [method child_controls_changed] to update it when children nodes have changed. + The value returned by this method can be overridden with [method _get_contents_minimum_size]. </description> </method> <method name="get_flag" qualifiers="const"> diff --git a/doc/classes/bool.xml b/doc/classes/bool.xml index 9e7d1fc965..647bd85d3d 100644 --- a/doc/classes/bool.xml +++ b/doc/classes/bool.xml @@ -4,42 +4,46 @@ A built-in boolean type. </brief_description> <description> - A [bool] is always one of two values: [code]true[/code] or [code]false[/code], similar to a switch that is either on or off. Booleans are used in programming for logic in condition statements. - Booleans can be directly used in [code]if[/code] and [code]elif[/code] statements. You don't need to add [code]== true[/code] or [code]== false[/code]: + The [bool] is a built-in [Variant] type that may only store one of two values: [code]true[/code] or [code]false[/code]. You can imagine it as a switch that can be either turned on or off, or as a binary digit that can either be 1 or 0. + Booleans can be directly used in [code]if[/code], and other conditional statements: [codeblocks] [gdscript] + var can_shoot = true if can_shoot: launch_bullet() [/gdscript] [csharp] + bool canShoot = true; if (canShoot) { - launchBullet(); + LaunchBullet(); } [/csharp] [/codeblocks] - Many common methods and operations return [bool]s, for example, [code]shooting_cooldown <= 0.0[/code] may evaluate to [code]true[/code] or [code]false[/code] depending on the number's value. - [bool]s are usually used with the logical operators [code]and[/code], [code]or[/code], and [code]not[/code] to create complex conditions: + All comparison operators return booleans ([code]==[/code], [code]>[/code], [code]<=[/code], etc.). As such, it is not necessary to compare booleans themselves. You do not need to add [code]== true[/code] or [code]== false[/code]. + Booleans can be combined with the logical operators [code]and[/code], [code]or[/code], [code]not[/code] to create complex conditions: [codeblocks] [gdscript] - if bullets > 0 and not is_reloading: + if bullets > 0 and not is_reloading(): launch_bullet() - if bullets == 0 or is_reloading: + if bullets == 0 or is_reloading(): play_clack_sound() [/gdscript] [csharp] - if (bullets > 0 && !isReloading) + if (bullets > 0 && !IsReloading()) { - launchBullet(); + LaunchBullet(); } - if (bullets == 0 || isReloading) + if (bullets == 0 || IsReloading()) { - playClackSound(); + PlayClackSound(); } [/csharp] [/codeblocks] + [b]Note:[/b] In modern programming languages, logical operators are evaluated in order. All remaining conditions are skipped if their result would have no effect on the final value. This concept is known as [url=https://en.wikipedia.org/wiki/Short-circuit_evaluation]short-circuit evaluation[/url] and can be useful to avoid evaluating expensive conditions in some performance-critical cases. + [b]Note:[/b] By convention, built-in methods and properties that return booleans are usually defined as yes-no questions, single adjectives, or similar ([method String.is_empty], [method Node.can_process], [member Camera2D.enabled], etc.). </description> <tutorials> </tutorials> @@ -47,7 +51,7 @@ <constructor name="bool"> <return type="bool" /> <description> - Constructs a default-initialized [bool] set to [code]false[/code]. + Constructs a [bool] set to [code]false[/code]. </description> </constructor> <constructor name="bool"> @@ -61,14 +65,14 @@ <return type="bool" /> <param index="0" name="from" type="float" /> <description> - Cast a [float] value to a boolean value. This method will return [code]false[/code] if [code]0.0[/code] is passed in, and [code]true[/code] for all other values. + Cast a [float] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0.0[/code] (including [code]-0.0[/code]), and [code]true[/code] for all other values (including [constant @GDScript.INF] and [constant @GDScript.NAN]). </description> </constructor> <constructor name="bool"> <return type="bool" /> <param index="0" name="from" type="int" /> <description> - Cast an [int] value to a boolean value. This method will return [code]false[/code] if [code]0[/code] is passed in, and [code]true[/code] for all other values. + Cast an [int] value to a boolean value. Returns [code]false[/code] if [param from] is equal to [code]0[/code], and [code]true[/code] for all other values. </description> </constructor> </constructors> @@ -77,7 +81,7 @@ <return type="bool" /> <param index="0" name="right" type="bool" /> <description> - Returns [code]true[/code] if two bools are different, i.e. one is [code]true[/code] and the other is [code]false[/code]. + Returns [code]true[/code] if the two booleans are not equal. That is, one is [code]true[/code] and the other is [code]false[/code]. This operation can be seen as a logical XOR. </description> </operator> <operator name="operator <"> @@ -91,7 +95,7 @@ <return type="bool" /> <param index="0" name="right" type="bool" /> <description> - Returns [code]true[/code] if two bools are equal, i.e. both are [code]true[/code] or both are [code]false[/code]. + Returns [code]true[/code] if the two booleans are equal. That is, both are [code]true[/code] or both are [code]false[/code]. This operation can be seen as a logical EQ or XNOR. </description> </operator> <operator name="operator >"> diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 2c40f0e120..afa6aaf395 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -569,8 +569,11 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread error->set_custom_color(1, color); String error_title; - if (oe.callstack.size() > 0) { - // If available, use the script's stack in the error title. + if (!oe.source_func.is_empty() && source_is_project_file) { + // If source function is inside the project file. + error_title += oe.source_func + ": "; + } else if (oe.callstack.size() > 0) { + // Otherwise, if available, use the script's stack in the error title. error_title = _format_frame_text(&oe.callstack[0]) + ": "; } else if (!oe.source_func.is_empty()) { // Otherwise try to use the C++ source function. @@ -645,7 +648,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread if (i == 0) { stack_trace->set_text(0, "<" + TTR("Stack Trace") + ">"); stack_trace->set_text_alignment(0, HORIZONTAL_ALIGNMENT_LEFT); - error->set_metadata(0, meta); + if (!source_is_project_file) { + // Only override metadata if the source is not inside the project. + error->set_metadata(0, meta); + } tooltip += TTR("Stack Trace:") + "\n"; } diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 38a78babfb..c6747c4481 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -35,7 +35,7 @@ #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/message_queue.h" -#include "core/variant/variant_utility.cpp" +#include "core/variant/variant_utility.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_scale.h" diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp index c226c1a2d6..e144d1d10d 100644 --- a/editor/gui/editor_run_bar.cpp +++ b/editor/gui/editor_run_bar.cpp @@ -348,6 +348,10 @@ bool EditorRunBar::is_movie_maker_enabled() const { return write_movie_button->is_pressed(); } +HBoxContainer *EditorRunBar::get_buttons_container() { + return main_hbox; +} + void EditorRunBar::_bind_methods() { ADD_SIGNAL(MethodInfo("play_pressed")); ADD_SIGNAL(MethodInfo("stop_pressed")); @@ -359,7 +363,7 @@ EditorRunBar::EditorRunBar() { main_panel = memnew(PanelContainer); add_child(main_panel); - HBoxContainer *main_hbox = memnew(HBoxContainer); + main_hbox = memnew(HBoxContainer); main_panel->add_child(main_hbox); play_button = memnew(Button); diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h index b7e7db2bd6..1cb999612a 100644 --- a/editor/gui/editor_run_bar.h +++ b/editor/gui/editor_run_bar.h @@ -39,6 +39,7 @@ class Button; class EditorRunNative; class EditorQuickOpen; class PanelContainer; +class HBoxContainer; class EditorRunBar : public MarginContainer { GDCLASS(EditorRunBar, MarginContainer); @@ -53,6 +54,7 @@ class EditorRunBar : public MarginContainer { }; PanelContainer *main_panel = nullptr; + HBoxContainer *main_hbox = nullptr; Button *play_button = nullptr; Button *pause_button = nullptr; @@ -109,6 +111,8 @@ public: Button *get_pause_button() { return pause_button; } + HBoxContainer *get_buttons_container(); + EditorRunBar(); }; diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index fbe167814d..f8b9313e0a 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -964,26 +964,55 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) { String new_name = p_name.validate_node_name(); if (new_name != p_name) { - error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters()); - error->popup_centered(); - - if (new_name.is_empty()) { - item->set_text(0, p_node->get_name()); - return; + String text = TTR("Invalid node name, the following characters are not allowed:") + "\n" + String::get_invalid_node_name_characters(); + if (error->is_visible()) { + if (!error->get_meta("invalid_character", false)) { + error->set_text(error->get_text() + "\n\n" + text); + error->set_meta("invalid_character", true); + } + } else { + error->set_text(text); + error->set_meta("invalid_character", true); + error->set_meta("same_unique_name", false); + error->popup_centered(); } + } - item->set_text(0, new_name); + // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. + new_name = new_name.strip_edges(); + if (new_name.is_empty()) { + // If name is empty, fallback to class name. + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + new_name = Node::adjust_name_casing(p_node->get_class()); + } else { + new_name = p_node->get_class(); + } } if (new_name == p_node->get_name()) { if (item->get_text(0).is_empty()) { item->set_text(0, new_name); } + return; + } + // We previously made sure name is not the same as current name so that it won't complain about already used unique name when not changing name. + if (p_node->is_unique_name_in_owner() && get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name)) { + String text = TTR("Another node already uses this unique name in the scene."); + if (error->is_visible()) { + if (!error->get_meta("same_unique_name", false)) { + error->set_text(error->get_text() + "\n\n" + text); + error->set_meta("same_unique_name", true); + } + } else { + error->set_text(text); + error->set_meta("same_unique_name", true); + error->set_meta("invalid_character", false); + error->popup_centered(); + } + item->set_text(0, p_node->get_name()); return; } - // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. - new_name = new_name.strip_edges(); if (!is_scene_tree_dock) { p_node->set_name(new_name); @@ -999,7 +1028,7 @@ void SceneTreeEditor::_rename_node(Node *p_node, const String &p_name) { undo_redo->add_undo_method(item, "set_metadata", 0, p_node->get_path()); undo_redo->add_undo_method(item, "set_text", 0, p_node->get_name()); - p_node->set_name(p_name); + p_node->set_name(new_name); undo_redo->add_do_method(p_node, "set_name", new_name); undo_redo->add_do_method(item, "set_metadata", 0, p_node->get_path()); undo_redo->add_do_method(item, "set_text", 0, new_name); @@ -1017,28 +1046,6 @@ void SceneTreeEditor::_renamed() { ERR_FAIL_COND(!n); String new_name = which->get_text(0); - if (new_name.strip_edges().is_empty()) { - // If name is empty, fallback to class name. - if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { - new_name = Node::adjust_name_casing(n->get_class()); - } else { - new_name = n->get_class(); - } - } - - if (n->is_unique_name_in_owner()) { - Node *existing = get_tree()->get_edited_scene_root()->get_node_or_null("%" + new_name); - if (existing == n) { - which->set_text(0, n->get_name()); - return; - } - if (existing != nullptr) { - error->set_text(TTR("Another node already uses this unique name in the scene.")); - error->popup_centered(); - which->set_text(0, n->get_name()); - return; - } - } _rename_node(n, new_name); } diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 79f46f7bf7..b967cb7ccd 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -134,6 +134,11 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p return Variant(); } +void CollisionShape3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) { + initial_transform = p_gizmo->get_node_3d()->get_global_transform(); + initial_value = get_handle_value(p_gizmo, p_id, p_secondary); +} + void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_node_3d()); @@ -142,7 +147,7 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i return; } - Transform3D gt = cs->get_global_transform(); + Transform3D gt = initial_transform; Transform3D gi = gt.affine_inverse(); Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -184,22 +189,37 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i if (Object::cast_to<BoxShape3D>(*s)) { Vector3 axis; - axis[p_id] = 1.0; + axis[p_id / 2] = 1.0; Ref<BoxShape3D> bs = s; Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = ra[p_id] * 2; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } + int sign = p_id % 2 * -2 + 1; + Vector3 initial_size = initial_value; - if (d < 0.001) { - d = 0.001; + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * sign, sg[0], sg[1], ra, rb); + if (ra[p_id / 2] == 0) { + // Point before half of the shape. Needs to be calculated in opposite direction. + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096 * -sign, sg[0], sg[1], ra, rb); } + float d = ra[p_id / 2] * sign; + Vector3 he = bs->get_size(); - he[p_id] = d; - bs->set_size(he); + he[p_id / 2] = d * 2; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + he[p_id / 2] = Math::snapped(he[p_id / 2], Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + he[p_id / 2] = MAX(he[p_id / 2], 0.001); + bs->set_size(he); + cs->set_global_position(initial_transform.get_origin()); + } else { + he[p_id / 2] = MAX(he[p_id / 2], -initial_size[p_id / 2] + 0.002); + bs->set_size((initial_size + (he - initial_size) * 0.5).abs()); + Vector3 pos = initial_transform.affine_inverse().xform(initial_transform.get_origin()); + pos += (bs->get_size() - initial_size) * 0.5 * sign; + cs->set_global_position(initial_transform.xform(pos)); + } } if (Object::cast_to<CapsuleShape3D>(*s)) { @@ -273,6 +293,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (Object::cast_to<BoxShape3D>(*s)) { Ref<BoxShape3D> ss = s; if (p_cancel) { + cs->set_global_position(initial_transform.get_origin()); ss->set_size(p_restore); return; } @@ -280,7 +301,9 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(ss.ptr(), "set_size", ss->get_size()); + ur->add_do_method(cs, "set_position", cs->get_global_position()); ur->add_undo_method(ss.ptr(), "set_size", p_restore); + ur->add_undo_method(cs, "set_global_position", initial_transform.get_origin()); ur->commit_action(); } @@ -429,6 +452,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 ax; ax[i] = bs->get_size()[i] / 2; handles.push_back(ax); + handles.push_back(-ax); } p_gizmo->add_lines(lines, material); diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h index 520295a522..6b7740de2f 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h @@ -36,6 +36,9 @@ class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); + Transform3D initial_transform; + Variant initial_value; + public: bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; @@ -44,6 +47,7 @@ public: String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; + void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) override; void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) override; void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override; diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 0da6fd3c41..2a91d9f108 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -115,6 +115,15 @@ Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const { return gizmo_plugin->get_handle_value(this, p_id, p_secondary); } +void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) { + if (GDVIRTUAL_CALL(_begin_handle_action, p_id, p_secondary)) { + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->begin_handle_action(this, p_id, p_secondary); +} + void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { if (GDVIRTUAL_CALL(_set_handle, p_id, p_secondary, p_camera, p_point)) { return; @@ -1095,6 +1104,10 @@ Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_giz return ret; } +void EditorNode3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) { + GDVIRTUAL_CALL(_begin_handle_action, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary); +} + void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_camera, p_point); } diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index ae2214de98..d7c368d5d0 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -83,6 +83,7 @@ protected: GDVIRTUAL2RC(String, _get_handle_name, int, bool) GDVIRTUAL2RC(bool, _is_handle_highlighted, int, bool) GDVIRTUAL2RC(Variant, _get_handle_value, int, bool) + GDVIRTUAL2(_begin_handle_action, int, bool) GDVIRTUAL4(_set_handle, int, bool, const Camera3D *, Vector2) GDVIRTUAL4(_commit_handle, int, bool, Variant, bool) @@ -104,6 +105,7 @@ public: virtual bool is_handle_highlighted(int p_id, bool p_secondary) const; virtual String get_handle_name(int p_id, bool p_secondary) const; virtual Variant get_handle_value(int p_id, bool p_secondary) const; + virtual void begin_handle_action(int p_id, bool p_secondary); virtual void set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point); virtual void commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false); @@ -170,6 +172,7 @@ protected: GDVIRTUAL3RC(bool, _is_handle_highlighted, Ref<EditorNode3DGizmo>, int, bool) GDVIRTUAL3RC(Variant, _get_handle_value, Ref<EditorNode3DGizmo>, int, bool) + GDVIRTUAL3(_begin_handle_action, Ref<EditorNode3DGizmo>, int, bool) GDVIRTUAL5(_set_handle, Ref<EditorNode3DGizmo>, int, bool, const Camera3D *, Vector2) GDVIRTUAL5(_commit_handle, Ref<EditorNode3DGizmo>, int, bool, Variant, bool) @@ -196,6 +199,7 @@ public: virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const; + virtual void begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary); virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point); virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 68d3661d10..78805324c9 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1775,6 +1775,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { seg->handles_intersect_ray(camera, _edit.mouse_pos, b->is_shift_pressed(), gizmo_handle, gizmo_secondary); if (gizmo_handle != -1) { _edit.gizmo = seg; + seg->begin_handle_action(gizmo_handle, gizmo_secondary); _edit.gizmo_handle = gizmo_handle; _edit.gizmo_handle_secondary = gizmo_secondary; _edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle, gizmo_secondary); @@ -2152,7 +2153,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } if (_edit.mode == TRANSFORM_NONE) { - if (_edit.gizmo.is_valid()) { + if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) { // Restore. _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true); _edit.gizmo = Ref<EditorNode3DGizmo>(); diff --git a/main/main.cpp b/main/main.cpp index 7e2741648d..2d582f1a96 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -857,6 +857,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); } } + // If gpu is specified, both editor and debug instances started from editor will inherit. + if (I->get() == "--gpu-index") { + if (I->next()) { + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(I->next()->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->get()); + forwardable_cli_arguments[CLI_SCOPE_PROJECT].push_back(I->next()->get()); + } + } #endif if (adding_user_args) { diff --git a/misc/extension_api_validation/4.0-stable.expected b/misc/extension_api_validation/4.0-stable.expected index 380c1d5193..9dc852d5b9 100644 --- a/misc/extension_api_validation/4.0-stable.expected +++ b/misc/extension_api_validation/4.0-stable.expected @@ -4,8 +4,21 @@ This file contains the expected output of --validate-extension-api when run agai Only lines that start with "Validate extension JSON:" matter, everything else is considered a comment and ignored. They should instead be used to justify these changes and describe how users should work around these changes. +Add new entries at the end of the file. + ======================================================================================================================== +Misc +---- +Validate extension JSON: API was removed: classes/FramebufferCacheRD +Validate extension JSON: API was removed: classes/UniformSetCacheRD + +FIXME: These aren't written when dumping the interface with a headless build +(since there's no RD backend in use). We need to fix this inconsistency somehow. + + +## Changes between 4.0-stable and 4.1-stable + GH-78517 -------- Validate extension JSON: Error: Field 'classes/DisplayServer/methods/global_menu_add_check_item/arguments/2': default_value changed value in new API, from "" to "Callable()". @@ -36,10 +49,12 @@ Validate extension JSON: Error: Field 'classes/RenderingServer/methods/instances The previous argument was a serialization bug, there's no actual API change. + GH-78237 -------- Validate extension JSON: Error: Field 'classes/WebRTCPeerConnectionExtension/methods/_create_data_channel/return_value': type changed value in new API, from "Object" to "WebRTCDataChannel". + GH-77757 -------- Validate extension JSON: Error: Field 'classes/Viewport/methods/gui_get_focus_owner': is_const changed value in new API, from false to true. @@ -136,13 +151,6 @@ Navigation avoidance was reworked entirely. Migration: TODO -GH-????? --------- -Validate extension JSON: API was removed: classes/FramebufferCacheRD -Validate extension JSON: API was removed: classes/UniformSetCacheRD - -Unsure where these come from; when dumping the interface, these do actually still exist - GH-76176 -------- Validate extension JSON: Error: Hash changed for 'classes/EditorInterface/methods/get_base_control', from 31757941 to A5E188F5. This means that the function has changed and no compatibility function was provided. @@ -260,7 +268,6 @@ Validate extension JSON: Error: Field 'classes/SyntaxHighlighter/methods/get_tex Function was made `const`. No adjustments should be necessary. - GH-75250 & GH-76401 ------------------- Validate extension JSON: Error: Hash changed for 'classes/RichTextLabel/methods/push_paragraph', from 3DD1D1C2 to BFDC71FE. This means that the function has changed and no compatibility function was provided. @@ -359,6 +366,9 @@ Validate extension JSON: Error: Hash changed for 'classes/UndoRedo/methods/creat Added a optional parameters with default values. No adjustments should be necessary. + +## Changes between 4.1-stable and 4.2-stable + GH-79911 -------- Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/BarrierMask/values/BARRIER_MASK_RASTER': value changed value in new API, from 1.0 to 9. @@ -379,3 +389,47 @@ Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/barrier/a Validate extension JSON: Error: Hash changed for 'classes/RenderingDevice/methods/barrier', from 0FE50041 to DD9E8DAB. This means that the function has changed and no compatibility function was provided. Raster barrier was split into vertex and fragment barriers for use in mobile renderer. + + +GH-79308 +-------- +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_scroll_ofs +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/get_zoom_hbox +Validate extension JSON: API was removed: classes/GraphEdit/methods/is_using_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_scroll_ofs +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_snap +Validate extension JSON: API was removed: classes/GraphEdit/methods/set_use_snap +Validate extension JSON: API was removed: classes/GraphEdit/properties/snap_distance +Validate extension JSON: API was removed: classes/GraphEdit/properties/use_snap +Validate extension JSON: API was removed: classes/GraphNode/methods/is_comment +Validate extension JSON: API was removed: classes/GraphNode/methods/set_comment +Validate extension JSON: API was removed: classes/GraphNode/properties/comment +Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': getter changed value in new API, from "get_scroll_ofs" to &"get_scroll_offset". +Validate extension JSON: Error: Field 'classes/GraphEdit/properties/scroll_offset': setter changed value in new API, from "set_scroll_ofs" to &"set_scroll_offset". + +Intentional compatibility breakage during refactoring of API marked as experimental. + +FIXME: Still a WIP, review this list once the work is completed, especially if compatibility +code is added. + + +GH-73196 +-------- +Validate extension JSON: Error: Field 'classes/CodeEdit/methods/get_text_for_symbol_lookup': is_const changed value in new API, from false to true. + +Function was made `const`. No adjustments should be necessary. + + +GH-78328 +-------- +Validate extension JSON: Error: Field 'classes/TileMap/methods/get_used_rect': is_const changed value in new API, from false to true. + +Function was made `const`. No adjustments should be necessary. + + +GH-79606 +-------- +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/shader_create_from_bytecode/arguments': size changed value in new API, from 1 to 2. + +Added optional argument. Compatibility method registered. diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh index 74a8e1a8a3..40d94d4276 100755 --- a/misc/scripts/clang_format.sh +++ b/misc/scripts/clang_format.sh @@ -21,7 +21,7 @@ fi # Fix copyright headers, but not all files get them. for f in $files; do - if [[ "$f" == *"inc" ]]; then + if [[ "$f" == *"inc" && "$f" != *"compat.inc" ]]; then continue elif [[ "$f" == *"glsl" ]]; then continue diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 0bf9f72a2c..42b08f8a68 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2695,6 +2695,11 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str Error err; Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE); + if (err && scr.is_valid()) { + // If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error. + ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_path, error_names[err])); + } + if (r_error) { // Don't fail loading because of parsing error. *r_error = scr.is_valid() ? OK : err; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2a52db4158..3366fa2eec 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -245,6 +245,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // MEMBERS. case GDScriptParser::IdentifierNode::MEMBER_VARIABLE: + case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: + case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: { // Try class members. if (_is_class_member_property(codegen, identifier)) { @@ -271,45 +273,44 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } } - } break; - case GDScriptParser::IdentifierNode::MEMBER_FUNCTION: - case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: { - // Try methods and signals (can be Callable and Signal). - // Search upwards through parent classes: - const GDScriptParser::ClassNode *base_class = codegen.class_node; - while (base_class != nullptr) { - if (base_class->has_member(identifier)) { - const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier); - if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { - // Get like it was a property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - - gen->write_get_named(temp, identifier, self); - return temp; + // Try methods and signals (can be Callable and Signal). + { + // Search upwards through parent classes: + const GDScriptParser::ClassNode *base_class = codegen.class_node; + while (base_class != nullptr) { + if (base_class->has_member(identifier)) { + const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier); + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) { + // Get like it was a property. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. + GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); + + gen->write_get_named(temp, identifier, self); + return temp; + } } + base_class = base_class->base_type.class_type; } - base_class = base_class->base_type.class_type; - } - // Try in native base. - GDScript *scr = codegen.script; - GDScriptNativeClass *nc = nullptr; - while (scr) { - if (scr->native.is_valid()) { - nc = scr->native.ptr(); + // Try in native base. + GDScript *scr = codegen.script; + GDScriptNativeClass *nc = nullptr; + while (scr) { + if (scr->native.is_valid()) { + nc = scr->native.ptr(); + } + scr = scr->_base; } - scr = scr->_base; - } - if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { - // Get like it was a property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. - GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); + if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) { + // Get like it was a property. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here. + GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF); - gen->write_get_named(temp, identifier, self); - return temp; + gen->write_get_named(temp, identifier, self); + return temp; + } } } break; case GDScriptParser::IdentifierNode::MEMBER_CONSTANT: @@ -319,6 +320,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code while (owner) { GDScript *scr = owner; GDScriptNativeClass *nc = nullptr; + while (scr) { if (scr->constants.has(identifier)) { return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here. diff --git a/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd new file mode 100644 index 0000000000..f17fb9823d --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.gd @@ -0,0 +1,14 @@ +# GH-80157 + +extends Node + +func f(): + pass + +signal s() + +func test(): + print(f) + print(s) + print(get_child) + print(ready) diff --git a/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out new file mode 100644 index 0000000000..e5e9ff7043 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/first_class_callable_and_signal.out @@ -0,0 +1,5 @@ +GDTEST_OK +Node::f +Node::[signal]s +Node::get_child +Node::[signal]ready diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 5bc6081803..9b760a997a 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -1,11 +1,16 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="GLTFDocument" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + Class for importing and exporting glTF files in and out of Godot. </brief_description> <description> - Append a glTF2 3d format from a file, buffer or scene and then write to the filesystem, buffer or scene. + GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene. + All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. + GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported. </description> <tutorials> + <link title="glTF 'What the duck?' guide">https://www.khronos.org/files/gltf20-reference-guide.pdf</link> + <link title="Khronos glTF specification">https://registry.khronos.org/glTF/</link> </tutorials> <methods> <method name="append_from_buffer"> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 927ffb6aae..bae980fb56 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -17,7 +17,7 @@ <param index="1" name="gltf_node" type="GLTFNode" /> <param index="2" name="scene_node" type="Node" /> <description> - Part of the export process. This method is run after [method _export_preflight] and before [method _export_node]. + Part of the export process. This method is run after [method _export_preflight] and before [method _export_preserialize]. Runs when converting the data from a Godot scene node. This method can be used to process the Godot scene node data into a format that can be used by [method _export_node]. </description> </method> @@ -28,7 +28,7 @@ <param index="2" name="json" type="Dictionary" /> <param index="3" name="node" type="Node" /> <description> - Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_post]. + Part of the export process. This method is run after [method _export_preserialize] and before [method _export_post]. This method can be used to modify the final JSON of each node. </description> </method> @@ -49,6 +49,14 @@ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned. </description> </method> + <method name="_export_preserialize" qualifiers="virtual"> + <return type="int" enum="Error" /> + <param index="0" name="state" type="GLTFState" /> + <description> + Part of the export process. This method is run after [method _convert_scene_node] and before [method _export_node]. + This method can be used to alter the state before performing serialization. It runs every time when generating a buffer with [method GLTFDocument.generate_buffer] or writing to the file system with [method GLTFDocument.write_to_filesystem]. + </description> + </method> <method name="_generate_scene_node" qualifiers="virtual"> <return type="Node3D" /> <param index="0" name="state" type="GLTFState" /> @@ -59,6 +67,12 @@ Runs when generating a Godot scene node from a GLTFNode. The returned node will be added to the scene tree. Multiple nodes can be generated in this step if they are added as a child of the returned node. </description> </method> + <method name="_get_image_file_extension" qualifiers="virtual"> + <return type="String" /> + <description> + Returns the file extension to use for saving image data into, for example, [code]".png"[/code]. If defined, when this extension is used to handle images, and the images are saved to a separate file, the image bytes will be copied to a file with this extension. If this is set, there should be a [ResourceImporter] class able to import the file. If not defined or empty, Godot will save the image into a PNG file. + </description> + </method> <method name="_get_supported_extensions" qualifiers="virtual"> <return type="PackedStringArray" /> <description> diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 2804a8b0a2..11718ba78a 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -36,6 +36,7 @@ void GLTFDocumentExtension::_bind_methods() { GDVIRTUAL_BIND(_get_supported_extensions); GDVIRTUAL_BIND(_parse_node_extensions, "state", "gltf_node", "extensions"); GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image"); + GDVIRTUAL_BIND(_get_image_file_extension); GDVIRTUAL_BIND(_parse_texture_json, "state", "texture_json", "ret_gltf_texture"); GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent"); GDVIRTUAL_BIND(_import_post_parse, "state"); @@ -44,6 +45,7 @@ void GLTFDocumentExtension::_bind_methods() { // Export process. GDVIRTUAL_BIND(_export_preflight, "state", "root"); GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node"); + GDVIRTUAL_BIND(_export_preserialize, "state"); GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node"); GDVIRTUAL_BIND(_export_post, "state"); } @@ -78,6 +80,12 @@ Error GLTFDocumentExtension::parse_image_data(Ref<GLTFState> p_state, const Pack return err; } +String GLTFDocumentExtension::get_image_file_extension() { + String ret; + GDVIRTUAL_CALL(_get_image_file_extension, ret); + return ret; +} + Error GLTFDocumentExtension::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(r_gltf_texture, ERR_INVALID_PARAMETER); @@ -134,6 +142,13 @@ void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFN GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node); } +Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) { + ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + Error err = OK; + GDVIRTUAL_CALL(_export_preserialize, p_state, err); + return err; +} + Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index d922588a29..0a631bb6c5 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -45,6 +45,7 @@ public: virtual Vector<String> get_supported_extensions(); virtual Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions); virtual Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image); + virtual String get_image_file_extension(); virtual Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture); virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent); virtual Error import_post_parse(Ref<GLTFState> p_state); @@ -53,6 +54,7 @@ public: // Export process. virtual Error export_preflight(Ref<GLTFState> p_state, Node *p_root); virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node); + virtual Error export_preserialize(Ref<GLTFState> p_state); virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node); virtual Error export_post(Ref<GLTFState> p_state); @@ -61,6 +63,7 @@ public: GDVIRTUAL0R(Vector<String>, _get_supported_extensions); GDVIRTUAL3R(Error, _parse_node_extensions, Ref<GLTFState>, Ref<GLTFNode>, Dictionary); GDVIRTUAL4R(Error, _parse_image_data, Ref<GLTFState>, PackedByteArray, String, Ref<Image>); + GDVIRTUAL0R(String, _get_image_file_extension); GDVIRTUAL3R(Error, _parse_texture_json, Ref<GLTFState>, Dictionary, Ref<GLTFTexture>); GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>); @@ -69,6 +72,7 @@ public: // Export process. GDVIRTUAL2R(Error, _export_preflight, Ref<GLTFState>, Node *); GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *); + GDVIRTUAL1R(Error, _export_preserialize, Ref<GLTFState>); GDVIRTUAL4R(Error, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *); GDVIRTUAL1R(Error, _export_post, Ref<GLTFState>); }; diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp index ded4970968..73c869be3b 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.cpp @@ -30,8 +30,6 @@ #include "gltf_document_extension_texture_webp.h" -#include "scene/3d/area_3d.h" - // Import process. Error GLTFDocumentExtensionTextureWebP::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { if (!p_extensions.has("EXT_texture_webp")) { @@ -53,6 +51,10 @@ Error GLTFDocumentExtensionTextureWebP::parse_image_data(Ref<GLTFState> p_state, return OK; } +String GLTFDocumentExtensionTextureWebP::get_image_file_extension() { + return ".webp"; +} + Error GLTFDocumentExtensionTextureWebP::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { if (!p_texture_json.has("extensions")) { return OK; diff --git a/modules/gltf/extensions/gltf_document_extension_texture_webp.h b/modules/gltf/extensions/gltf_document_extension_texture_webp.h index 9abf09a41f..d2654aae8c 100644 --- a/modules/gltf/extensions/gltf_document_extension_texture_webp.h +++ b/modules/gltf/extensions/gltf_document_extension_texture_webp.h @@ -41,6 +41,7 @@ public: Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override; Vector<String> get_supported_extensions() override; Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override; + String get_image_file_extension() override; Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override; }; diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 4381a0e00d..d23b22049b 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -115,6 +115,12 @@ Error GLTFDocument::_serialize(Ref<GLTFState> p_state, const String &p_path) { p_state->buffers.push_back(Vector<uint8_t>()); } + for (Ref<GLTFDocumentExtension> ext : document_extensions) { + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preserialize(p_state); + ERR_CONTINUE(err != OK); + } + /* STEP CONVERT MESH INSTANCES */ _convert_mesh_instances(p_state); @@ -2998,7 +3004,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) { Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_path) { Array images; for (int i = 0; i < p_state->images.size(); i++) { - Dictionary d; + Dictionary image_dict; ERR_CONTINUE(p_state->images[i].is_null()); @@ -3031,8 +3037,8 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa p_state->buffer_views.push_back(bv); bvi = p_state->buffer_views.size() - 1; - d["bufferView"] = bvi; - d["mimeType"] = "image/png"; + image_dict["bufferView"] = bvi; + image_dict["mimeType"] = "image/png"; } else { ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER); String img_name = p_state->images[i]->get_name(); @@ -3041,17 +3047,17 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa } img_name = _gen_unique_name(p_state, img_name); img_name = img_name.pad_zeros(3) + ".png"; - String texture_dir = "textures"; - String path = p_path.get_base_dir(); - String new_texture_dir = path + "/" + texture_dir; - Ref<DirAccess> da = DirAccess::open(path); - if (!da->dir_exists(new_texture_dir)) { - da->make_dir(new_texture_dir); + String relative_texture_dir = "textures"; + String parent_path = p_path.get_base_dir(); + String full_texture_dir = parent_path + "/" + relative_texture_dir; + Ref<DirAccess> da = DirAccess::open(parent_path); + if (!da->dir_exists(full_texture_dir)) { + da->make_dir(full_texture_dir); } - image->save_png(new_texture_dir.path_join(img_name)); - d["uri"] = texture_dir.path_join(img_name).uri_encode(); + image->save_png(full_texture_dir.path_join(img_name)); + image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode(); } - images.push_back(d); + images.push_back(image_dict); } print_verbose("Total images: " + itos(p_state->images.size())); @@ -3064,7 +3070,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state, const String &p_pa return OK; } -Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index) { +Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension) { Ref<Image> r_image; r_image.instantiate(); // Check if any GLTFDocumentExtensions want to import this data as an image. @@ -3073,6 +3079,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image); ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing."); if (!r_image->is_empty()) { + r_file_extension = ext->get_image_file_extension(); return r_image; } } @@ -3080,8 +3087,10 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c // First we honor the mime types if they were defined. if (p_mime_type == "image/png") { // Load buffer as PNG. r_image->load_png_from_buffer(p_bytes); + r_file_extension = ".png"; } else if (p_mime_type == "image/jpeg") { // Loader buffer as JPEG. r_image->load_jpg_from_buffer(p_bytes); + r_file_extension = ".jpg"; } // If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly. // This covers URIs with base64-encoded data with application/* type but @@ -3102,7 +3111,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c return r_image; } -void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image) { +void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image) { GLTFState::GLTFHandleBinary handling = GLTFState::GLTFHandleBinary(p_state->handle_binary_image); if (p_image->is_empty() || handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref<Texture2D>()); @@ -3119,11 +3128,11 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); } else { - Error err = OK; bool must_import = true; Vector<uint8_t> img_data = p_image->get_data(); Dictionary generator_parameters; - String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name() + ".png"; + String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name(); + file_path += p_file_extension.is_empty() ? ".png" : p_file_extension; if (FileAccess::exists(file_path + ".import")) { Ref<ConfigFile> config; config.instantiate(); @@ -3144,8 +3153,18 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const String } } if (must_import) { - err = p_image->save_png(file_path); - ERR_FAIL_COND(err != OK); + Error err = OK; + if (p_file_extension.is_empty()) { + // If a file extension was not specified, save the image data to a PNG file. + err = p_image->save_png(file_path); + ERR_FAIL_COND(err != OK); + } else { + // If a file extension was specified, save the original bytes to a file with that extension. + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::WRITE, &err); + ERR_FAIL_COND(err != OK); + file->store_buffer(p_bytes); + file->close(); + } // ResourceLoader::import will crash if not is_editor_hint(), so this case is protected above and will fall through to uncompressed. HashMap<StringName, Variant> custom_options; custom_options[SNAME("mipmaps/generate")] = true; @@ -3295,9 +3314,10 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p continue; } // Parse the image data from bytes into an Image resource and save if needed. - Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i); + String file_extension; + Ref<Image> img = _parse_image_bytes_into_image(p_state, data, mime_type, i, file_extension); img->set_name(image_name); - _parse_image_save_image(p_state, mime_type, i, img); + _parse_image_save_image(p_state, data, file_extension, i, img); } print_verbose("glTF: Total images: " + itos(p_state->images.size())); @@ -3312,16 +3332,16 @@ Error GLTFDocument::_serialize_textures(Ref<GLTFState> p_state) { Array textures; for (int32_t i = 0; i < p_state->textures.size(); i++) { - Dictionary d; - Ref<GLTFTexture> t = p_state->textures[i]; - ERR_CONTINUE(t->get_src_image() == -1); - d["source"] = t->get_src_image(); + Dictionary texture_dict; + Ref<GLTFTexture> gltf_texture = p_state->textures[i]; + ERR_CONTINUE(gltf_texture->get_src_image() == -1); + texture_dict["source"] = gltf_texture->get_src_image(); - GLTFTextureSamplerIndex sampler_index = t->get_sampler(); + GLTFTextureSamplerIndex sampler_index = gltf_texture->get_sampler(); if (sampler_index != -1) { - d["sampler"] = sampler_index; + texture_dict["sampler"] = sampler_index; } - textures.push_back(d); + textures.push_back(texture_dict); } p_state->json["textures"] = textures; @@ -3335,28 +3355,28 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) { const Array &textures = p_state->json["textures"]; for (GLTFTextureIndex i = 0; i < textures.size(); i++) { - const Dictionary &dict = textures[i]; - Ref<GLTFTexture> texture; - texture.instantiate(); + const Dictionary &texture_dict = textures[i]; + Ref<GLTFTexture> gltf_texture; + gltf_texture.instantiate(); // Check if any GLTFDocumentExtensions want to handle this texture JSON. for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - Error err = ext->parse_texture_json(p_state, dict, texture); - ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(dict)) + " in file " + p_state->filename + ". Continuing."); - if (texture->get_src_image() != -1) { + Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture); + ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing."); + if (gltf_texture->get_src_image() != -1) { break; } } - if (texture->get_src_image() == -1) { + if (gltf_texture->get_src_image() == -1) { // No extensions handled it, so use the base GLTF source. // This may be the fallback, or the only option anyway. - ERR_FAIL_COND_V(!dict.has("source"), ERR_PARSE_ERROR); - texture->set_src_image(dict["source"]); + ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR); + gltf_texture->set_src_image(texture_dict["source"]); } - if (texture->get_sampler() == -1 && dict.has("sampler")) { - texture->set_sampler(dict["sampler"]); + if (gltf_texture->get_sampler() == -1 && texture_dict.has("sampler")) { + gltf_texture->set_sampler(texture_dict["sampler"]); } - p_state->textures.push_back(texture); + p_state->textures.push_back(gltf_texture); } return OK; @@ -7280,44 +7300,44 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo return root; } -Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags) { - ERR_FAIL_COND_V(r_state.is_null(), FAILED); - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; - if (!r_state->buffers.size()) { - r_state->buffers.push_back(Vector<uint8_t>()); +Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags) { + ERR_FAIL_COND_V(p_state.is_null(), FAILED); + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + if (!p_state->buffers.size()) { + p_state->buffers.push_back(Vector<uint8_t>()); } // Perform export preflight for document extensions. Only extensions that // return OK will be used for the rest of the export steps. document_extensions.clear(); for (Ref<GLTFDocumentExtension> ext : all_document_extensions) { ERR_CONTINUE(ext.is_null()); - Error err = ext->export_preflight(r_state, p_node); + Error err = ext->export_preflight(p_state, p_node); if (err == OK) { document_extensions.push_back(ext); } } // Add the root node(s) and their descendants to the state. - _convert_scene_node(r_state, p_node, -1, -1); + _convert_scene_node(p_state, p_node, -1, -1); return OK; } -Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags) { - ERR_FAIL_COND_V(r_state.is_null(), FAILED); +Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags) { + ERR_FAIL_COND_V(p_state.is_null(), FAILED); // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire Error err = FAILED; - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; Ref<FileAccessMemory> file_access; file_access.instantiate(); file_access->open_custom(p_bytes.ptr(), p_bytes.size()); - r_state->base_path = p_base_path.get_base_dir(); - err = _parse(r_state, r_state->base_path, file_access); + p_state->base_path = p_base_path.get_base_dir(); + err = _parse(p_state, p_state->base_path, file_access); ERR_FAIL_COND_V(err != OK, err); for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - err = ext->import_post_parse(r_state); + err = ext->import_post_parse(p_state); ERR_FAIL_COND_V(err != OK, err); } return OK; @@ -7436,14 +7456,14 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se return OK; } -Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, String p_base_path) { +Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags, String p_base_path) { // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire - if (r_state == Ref<GLTFState>()) { - r_state.instantiate(); + if (p_state == Ref<GLTFState>()) { + p_state.instantiate(); } - r_state->filename = p_path.get_file().get_basename(); - r_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; - r_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; + p_state->filename = p_path.get_file().get_basename(); + p_state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; + p_state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); @@ -7452,12 +7472,12 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint if (base_path.is_empty()) { base_path = p_path.get_base_dir(); } - r_state->base_path = base_path; - err = _parse(r_state, base_path, file); + p_state->base_path = base_path; + err = _parse(p_state, base_path, file); ERR_FAIL_COND_V(err != OK, err); for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); - err = ext->import_post_parse(r_state); + err = ext->import_post_parse(p_state); ERR_FAIL_COND_V(err != OK, err); } return OK; diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index dfde53c9fb..f8bd156feb 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -151,8 +151,8 @@ private: Error _serialize_texture_samplers(Ref<GLTFState> p_state); Error _serialize_images(Ref<GLTFState> p_state, const String &p_path); Error _serialize_lights(Ref<GLTFState> p_state); - Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index); - void _parse_image_save_image(Ref<GLTFState> p_state, const String &p_mime_type, int p_index, Ref<Image> p_image); + Ref<Image> _parse_image_bytes_into_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension); + void _parse_image_save_image(Ref<GLTFState> p_state, const Vector<uint8_t> &p_bytes, const String &p_file_extension, int p_index, Ref<Image> p_image); Error _parse_images(Ref<GLTFState> p_state, const String &p_base_path); Error _parse_textures(Ref<GLTFState> p_state); Error _parse_texture_samplers(Ref<GLTFState> p_state); @@ -293,9 +293,9 @@ private: static float get_max_component(const Color &p_color); public: - Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, String p_base_path = String()); - Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0); - Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0); + Error append_from_file(String p_path, Ref<GLTFState> p_state, uint32_t p_flags = 0, String p_base_path = String()); + Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> p_state, uint32_t p_flags = 0); + Error append_from_scene(Node *p_node, Ref<GLTFState> p_state, uint32_t p_flags = 0); public: Node *generate_scene(Ref<GLTFState> p_state, float p_bake_fps = 30.0f, bool p_trimming = false, bool p_remove_immutable_tracks = true); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1ed495943f..f592533a5a 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1197,8 +1197,6 @@ void CSharpLanguage::_editor_init_callback() { // Add plugin to EditorNode and enable it EditorNode::add_editor_plugin(godotsharp_editor); - ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B); - ED_SHORTCUT_OVERRIDE("mono/build_solution", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::B); godotsharp_editor->enable_plugin(); get_singleton()->godotsharp_editor = godotsharp_editor; diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 1bb1b3227e..cc11132a55 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -114,7 +114,7 @@ namespace GodotTools.Build var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill }; AddChild(toolBarHBox); - _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") }; + _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("BuildCSharp", "EditorIcons") }; toolBarHBox.AddChild(_buildMenuBtn); var buildMenu = _buildMenuBtn.GetPopup(); @@ -184,7 +184,7 @@ namespace GodotTools.Build if (what == NotificationThemeChanged) { if (_buildMenuBtn != null) - _buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons"); + _buildMenuBtn.Icon = GetThemeIcon("BuildCSharp", "EditorIcons"); if (_errorsBtn != null) _errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons"); if (_warningsBtn != null) diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 622a155d37..cdf0a344d4 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -497,18 +497,20 @@ namespace GodotTools AddToolSubmenuItem("C#", _menuPopup); - var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution"); - _toolBarBuildButton = new Button { - Text = "Build", - TooltipText = "Build Solution".TTR(), + Flat = true, + Icon = editorBaseControl.GetThemeIcon("BuildCSharp", "EditorIcons"), FocusMode = Control.FocusModeEnum.None, - Shortcut = buildSolutionShortcut, - ShortcutInTooltip = true + Shortcut = EditorDefShortcut("mono/build_solution", "Build Project".TTR(), (Key)KeyModifierMask.MaskAlt | Key.B), + ShortcutInTooltip = true, }; + EditorShortcutOverride("mono/build_solution", "macos", (Key)KeyModifierMask.MaskMeta | (Key)KeyModifierMask.MaskCtrl | Key.B); + _toolBarBuildButton.Pressed += BuildProjectPressed; - AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton); + Internal.EditorPlugin_AddControlToEditorRunBar(_toolBarBuildButton); + // Move Build button so it appears to the left of the Play button. + _toolBarBuildButton.GetParent().MoveChild(_toolBarBuildButton, 0); if (File.Exists(GodotSharpDirs.ProjectCsProjPath)) { diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index 45ae7eb86b..a6718e8fd5 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -29,11 +29,26 @@ namespace GodotTools.Internals return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static Variant EditorShortcut(string setting) + public static Shortcut EditorDefShortcut(string setting, string name, Key keycode = Key.None, bool physical = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result); - return Variant.CreateTakingOwnershipOfDisposableValue(result); + using godot_string nameIn = Marshaling.ConvertStringToNative(name); + Internal.godot_icall_Globals_EditorDefShortcut(settingIn, nameIn, keycode, physical.ToGodotBool(), out godot_variant result); + return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result); + } + + public static Shortcut EditorGetShortcut(string setting) + { + using godot_string settingIn = Marshaling.ConvertStringToNative(setting); + Internal.godot_icall_Globals_EditorGetShortcut(settingIn, out godot_variant result); + return (Shortcut)Variant.CreateTakingOwnershipOfDisposableValue(result); + } + + public static void EditorShortcutOverride(string setting, string feature, Key keycode = Key.None, bool physical = false) + { + using godot_string settingIn = Marshaling.ConvertStringToNative(setting); + using godot_string featureIn = Marshaling.ConvertStringToNative(feature); + Internal.godot_icall_Globals_EditorShortcutOverride(settingIn, featureIn, keycode, physical.ToGodotBool()); } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index 3ea11750b7..90c443ebb8 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -54,6 +54,9 @@ namespace GodotTools.Internals public static void EditorRunStop() => godot_icall_Internal_EditorRunStop(); + public static void EditorPlugin_AddControlToEditorRunBar(Control control) => + godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(control.NativeInstance); + public static void ScriptEditorDebugger_ReloadScripts() => godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); @@ -137,6 +140,8 @@ namespace GodotTools.Internals private static partial void godot_icall_Internal_EditorRunStop(); + private static partial void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(IntPtr p_control); + private static partial void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts(); private static partial void godot_icall_Internal_CodeCompletionRequest(int kind, in godot_string scriptFile, @@ -151,7 +156,13 @@ namespace GodotTools.Internals bool restartIfChanged, out godot_variant result); public static partial void - godot_icall_Globals_EditorShortcut(in godot_string setting, out godot_variant result); + godot_icall_Globals_EditorDefShortcut(in godot_string setting, in godot_string name, Key keycode, godot_bool physical, out godot_variant result); + + public static partial void + godot_icall_Globals_EditorGetShortcut(in godot_string setting, out godot_variant result); + + public static partial void + godot_icall_Globals_EditorShortcutOverride(in godot_string setting, in godot_string feature, Key keycode, godot_bool physical); public static partial void godot_icall_Globals_TTR(in godot_string text, out godot_string dest); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index ba6b91b704..fc99f3ceda 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -168,6 +168,10 @@ void godot_icall_Internal_EditorRunStop() { EditorRunBar::get_singleton()->stop_playing(); } +void godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar(Control *p_control) { + EditorRunBar::get_singleton()->get_buttons_container()->add_child(p_control); +} + void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() { EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); if (ed) { @@ -199,12 +203,25 @@ void godot_icall_Globals_EditorDef(const godot_string *p_setting, const godot_va memnew_placement(r_result, Variant(result)); } -void godot_icall_Globals_EditorShortcut(const godot_string *p_setting, godot_variant *r_result) { +void godot_icall_Globals_EditorDefShortcut(const godot_string *p_setting, const godot_string *p_name, Key p_keycode, bool p_physical, godot_variant *r_result) { + String setting = *reinterpret_cast<const String *>(p_setting); + String name = *reinterpret_cast<const String *>(p_name); + Ref<Shortcut> result = ED_SHORTCUT(setting, name, p_keycode, p_physical); + memnew_placement(r_result, Variant(result)); +} + +void godot_icall_Globals_EditorGetShortcut(const godot_string *p_setting, Ref<Shortcut> *r_result) { String setting = *reinterpret_cast<const String *>(p_setting); Ref<Shortcut> result = ED_GET_SHORTCUT(setting); memnew_placement(r_result, Variant(result)); } +void godot_icall_Globals_EditorShortcutOverride(const godot_string *p_setting, const godot_string *p_feature, Key p_keycode, bool p_physical) { + String setting = *reinterpret_cast<const String *>(p_setting); + String feature = *reinterpret_cast<const String *>(p_feature); + ED_SHORTCUT_OVERRIDE(setting, feature, p_keycode, p_physical); +} + void godot_icall_Globals_TTR(const godot_string *p_text, godot_string *r_dest) { String text = *reinterpret_cast<const String *>(p_text); memnew_placement(r_dest, String(TTR(text))); @@ -251,12 +268,15 @@ static const void *unmanaged_callbacks[]{ (void *)godot_icall_Internal_EditorNodeShowScriptScreen, (void *)godot_icall_Internal_EditorRunPlay, (void *)godot_icall_Internal_EditorRunStop, + (void *)godot_icall_Internal_EditorPlugin_AddControlToEditorRunBar, (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts, (void *)godot_icall_Internal_CodeCompletionRequest, (void *)godot_icall_Globals_EditorScale, (void *)godot_icall_Globals_GlobalDef, (void *)godot_icall_Globals_EditorDef, - (void *)godot_icall_Globals_EditorShortcut, + (void *)godot_icall_Globals_EditorDefShortcut, + (void *)godot_icall_Globals_EditorGetShortcut, + (void *)godot_icall_Globals_EditorShortcutOverride, (void *)godot_icall_Globals_TTR, (void *)godot_icall_Utils_OS_GetPlatformName, (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs index c4161d2ded..57b292793a 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DebuggingUtils.cs @@ -14,14 +14,46 @@ namespace Godot { private static void AppendTypeName(this StringBuilder sb, Type type) { - if (type.IsPrimitive) - sb.Append(type.Name); - else if (type == typeof(void)) + // Use the C# type keyword for built-in types. + // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types + if (type == typeof(void)) sb.Append("void"); + else if (type == typeof(bool)) + sb.Append("bool"); + else if (type == typeof(byte)) + sb.Append("byte"); + else if (type == typeof(sbyte)) + sb.Append("sbyte"); + else if (type == typeof(char)) + sb.Append("char"); + else if (type == typeof(decimal)) + sb.Append("decimal"); + else if (type == typeof(double)) + sb.Append("double"); + else if (type == typeof(float)) + sb.Append("float"); + else if (type == typeof(int)) + sb.Append("int"); + else if (type == typeof(uint)) + sb.Append("uint"); + else if (type == typeof(nint)) + sb.Append("nint"); + else if (type == typeof(nuint)) + sb.Append("nuint"); + else if (type == typeof(long)) + sb.Append("long"); + else if (type == typeof(ulong)) + sb.Append("ulong"); + else if (type == typeof(short)) + sb.Append("short"); + else if (type == typeof(ushort)) + sb.Append("ushort"); + else if (type == typeof(object)) + sb.Append("object"); + else if (type == typeof(string)) + sb.Append("string"); else sb.Append(type); - - sb.Append(' '); } internal static void InstallTraceListener() @@ -70,13 +102,26 @@ namespace Godot } } + internal static unsafe StackFrame? GetCurrentStackFrame(int skipFrames = 0) + { + // We skip 2 frames: + // The first skipped frame is the current method. + // The second skipped frame is a method in NativeInterop.NativeFuncs. + var stackTrace = new StackTrace(skipFrames: 2 + skipFrames, fNeedFileInfo: true); + return stackTrace.GetFrame(0); + } + [UnmanagedCallersOnly] internal static unsafe void GetCurrentStackInfo(void* destVector) { try { var vector = (godot_stack_info_vector*)destVector; - var stackTrace = new StackTrace(skipFrames: 1, fNeedFileInfo: true); + + // We skip 2 frames: + // The first skipped frame is the current method. + // The second skipped frame is a method in NativeInterop.NativeFuncs. + var stackTrace = new StackTrace(skipFrames: 2, fNeedFileInfo: true); int frameCount = stackTrace.FrameCount; if (frameCount == 0) @@ -87,6 +132,14 @@ namespace Godot int i = 0; foreach (StackFrame frame in stackTrace.GetFrames()) { + var method = frame.GetMethod(); + + if (method is MethodInfo methodInfo && methodInfo.IsDefined(typeof(StackTraceHiddenAttribute))) + { + // Skip methods marked hidden from the stack trace. + continue; + } + string? fileName = frame.GetFileName(); int fileLineNumber = frame.GetFileLineNumber(); @@ -102,6 +155,9 @@ namespace Godot i++; } + + // Resize the vector again in case we skipped some frames. + vector->Resize(i); } catch (Exception e) { @@ -122,7 +178,10 @@ namespace Godot var sb = new StringBuilder(); if (methodBase is MethodInfo methodInfo) + { sb.AppendTypeName(methodInfo.ReturnType); + sb.Append(' '); + } sb.Append(methodBase.DeclaringType?.FullName ?? "<unknown>"); sb.Append('.'); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs index 9425b7424c..33ebb8171e 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Text; using Godot.NativeInterop; @@ -334,6 +335,21 @@ namespace Godot NativeFuncs.godotsharp_printt(godotStr); } + [StackTraceHidden] + private static void ErrPrintError(string message, godot_error_handler_type type = godot_error_handler_type.ERR_HANDLER_ERROR) + { + // Skip 1 frame to avoid current method. + var stackFrame = DebuggingUtils.GetCurrentStackFrame(skipFrames: 1); + string callerFilePath = ProjectSettings.LocalizePath(stackFrame.GetFileName()); + DebuggingUtils.GetStackFrameMethodDecl(stackFrame, out string callerName); + int callerLineNumber = stackFrame.GetFileLineNumber(); + + using godot_string messageStr = Marshaling.ConvertStringToNative(message); + using godot_string callerNameStr = Marshaling.ConvertStringToNative(callerName); + using godot_string callerFilePathStr = Marshaling.ConvertStringToNative(callerFilePath); + NativeFuncs.godotsharp_err_print_error(callerNameStr, callerFilePathStr, callerLineNumber, messageStr, p_type: type); + } + /// <summary> /// Pushes an error message to Godot's built-in debugger and to the OS terminal. /// @@ -347,8 +363,7 @@ namespace Godot /// <param name="message">Error message.</param> public static void PushError(string message) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pusherror(godotStr); + ErrPrintError(message); } /// <summary> @@ -364,7 +379,7 @@ namespace Godot /// <param name="what">Arguments that form the error message.</param> public static void PushError(params object[] what) { - PushError(AppendPrintParams(what)); + ErrPrintError(AppendPrintParams(what)); } /// <summary> @@ -378,8 +393,7 @@ namespace Godot /// <param name="message">Warning message.</param> public static void PushWarning(string message) { - using var godotStr = Marshaling.ConvertStringToNative(message); - NativeFuncs.godotsharp_pushwarning(godotStr); + ErrPrintError(message, type: godot_error_handler_type.ERR_HANDLER_WARNING); } /// <summary> @@ -393,7 +407,7 @@ namespace Godot /// <param name="what">Arguments that form the warning message.</param> public static void PushWarning(params object[] what) { - PushWarning(AppendPrintParams(what)); + ErrPrintError(AppendPrintParams(what), type: godot_error_handler_type.ERR_HANDLER_WARNING); } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs index ba2c232580..a656c5de90 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs @@ -95,7 +95,7 @@ namespace Godot.NativeInterop } NativeFuncs.godotsharp_internal_script_debugger_send_error(nFunc, nFile, line, - nErrorMsg, nExcMsg, p_warning: godot_bool.False, stackInfoVector); + nErrorMsg, nExcMsg, godot_error_handler_type.ERR_HANDLER_ERROR, stackInfoVector); } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 1dddc82e85..d5d9404ed1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -1134,4 +1134,13 @@ namespace Godot.NativeInterop get => _ptr != null ? *((int*)_ptr - 1) : 0; } } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum godot_error_handler_type + { + ERR_HANDLER_ERROR = 0, + ERR_HANDLER_WARNING, + ERR_HANDLER_SCRIPT, + ERR_HANDLER_SHADER, + } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 3ec3d1e530..d42ee15657 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -58,7 +58,7 @@ namespace Godot.NativeInterop internal static partial void godotsharp_internal_script_debugger_send_error(in godot_string p_func, in godot_string p_file, int p_line, in godot_string p_err, in godot_string p_descr, - godot_bool p_warning, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector); + godot_error_handler_type p_type, in DebuggingUtils.godot_stack_info_vector p_stack_info_vector); internal static partial godot_bool godotsharp_internal_script_debugger_is_active(); @@ -540,9 +540,7 @@ namespace Godot.NativeInterop internal static partial void godotsharp_var_to_str(in godot_variant p_var, out godot_string r_ret); - internal static partial void godotsharp_pusherror(in godot_string p_str); - - internal static partial void godotsharp_pushwarning(in godot_string p_str); + internal static partial void godotsharp_err_print_error(in godot_string p_function, in godot_string p_file, int p_line, in godot_string p_error, in godot_string p_message = default, godot_bool p_editor_notify = godot_bool.False, godot_error_handler_type p_type = godot_error_handler_type.ERR_HANDLER_ERROR); // Object diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index ee4de4e9f5..24a9d4030a 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -92,10 +92,10 @@ void godotsharp_stack_info_vector_destroy( void godotsharp_internal_script_debugger_send_error(const String *p_func, const String *p_file, int32_t p_line, const String *p_err, const String *p_descr, - bool p_warning, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) { + ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> *p_stack_info_vector) { const String file = ProjectSettings::get_singleton()->localize_path(p_file->simplify_path()); EngineDebugger::get_script_debugger()->send_error(*p_func, file, p_line, *p_err, *p_descr, - true, p_warning ? ERR_HANDLER_WARNING : ERR_HANDLER_ERROR, *p_stack_info_vector); + true, p_type, *p_stack_info_vector); } bool godotsharp_internal_script_debugger_is_active() { @@ -1320,12 +1320,14 @@ void godotsharp_printraw(const godot_string *p_what) { OS::get_singleton()->print("%s", reinterpret_cast<const String *>(p_what)->utf8().get_data()); } -void godotsharp_pusherror(const godot_string *p_str) { - ERR_PRINT(*reinterpret_cast<const String *>(p_str)); -} - -void godotsharp_pushwarning(const godot_string *p_str) { - WARN_PRINT(*reinterpret_cast<const String *>(p_str)); +void godotsharp_err_print_error(const godot_string *p_function, const godot_string *p_file, int32_t p_line, const godot_string *p_error, const godot_string *p_message, bool p_editor_notify, ErrorHandlerType p_type) { + _err_print_error( + reinterpret_cast<const String *>(p_function)->utf8().get_data(), + reinterpret_cast<const String *>(p_file)->utf8().get_data(), + p_line, + reinterpret_cast<const String *>(p_error)->utf8().get_data(), + reinterpret_cast<const String *>(p_message)->utf8().get_data(), + p_editor_notify, p_type); } void godotsharp_var_to_str(const godot_variant *p_var, godot_string *r_ret) { @@ -1611,8 +1613,7 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_str_to_var, (void *)godotsharp_var_to_bytes, (void *)godotsharp_var_to_str, - (void *)godotsharp_pusherror, - (void *)godotsharp_pushwarning, + (void *)godotsharp_err_print_error, (void *)godotsharp_object_to_string, }; diff --git a/modules/mono/icons/BuildCSharp.svg b/modules/mono/icons/BuildCSharp.svg new file mode 100644 index 0000000000..9d0102c35d --- /dev/null +++ b/modules/mono/icons/BuildCSharp.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M 9.6060193,0.78346667 C 8.6741914,0.96303367 7.6708299,1.5334576 6.9028943,1.9768256 l -2.1523438,1.244141 0.082031,0.138672 -0.3105469,-0.00781 -2.5839844,1.492188 1.9101563,3.308593 2.5820312,-1.490234 0.1425781,-0.255859 4.1875002,7.2539054 0.5,0.867187 c 0.415803,0.720194 1.331398,0.964165 2.050782,0.548829 0.719286,-0.415279 0.963839,-1.33001 0.548828,-2.048829 l -2,-3.4648424 -2.8808602,-4.990235 3.7070322,-2.101562 -0.265626,-0.439453 C 11.697382,0.83561667 10.650124,0.58226267 9.6060193,0.78346667 Z" fill="#e0e0e0"/></svg> diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 5e52f25cf4..247968e251 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -376,6 +376,12 @@ void GDMono::initialize() { godot_plugins_initialize_fn godot_plugins_initialize = nullptr; + // Check that the .NET assemblies directory exists before trying to use it. + if (!DirAccess::exists(GodotSharpDirs::get_api_assemblies_dir())) { + OS::get_singleton()->alert(vformat(RTR("Unable to find the .NET assemblies directory.\nMake sure the '%s' directory exists and contains the .NET assemblies."), GodotSharpDirs::get_api_assemblies_dir()), RTR(".NET assemblies not found")); + ERR_FAIL_MSG(".NET: Assemblies not found"); + } + if (!load_hostfxr(hostfxr_dll_handle)) { #if !defined(TOOLS_ENABLED) godot_plugins_initialize = try_load_native_aot_library(hostfxr_dll_handle); diff --git a/scene/2d/tile_map.compat.inc b/scene/2d/tile_map.compat.inc index 49e2bf6f0b..c7786ecced 100644 --- a/scene/2d/tile_map.compat.inc +++ b/scene/2d/tile_map.compat.inc @@ -1,5 +1,5 @@ /**************************************************************************/ -/* object.compat.inc */ +/* tile_map.compat.inc */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -30,10 +30,6 @@ #ifndef DISABLE_DEPRECATED -#include "core/object/object.h" - -#include "core/object/class_db.h" - Rect2i TileMap::_get_used_rect_bind_compat_78328() { return get_used_rect(); } diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 3ac3ca7363..f57afb66b3 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -468,12 +468,12 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); GDVIRTUAL_BIND(_pressed); - GDVIRTUAL_BIND(_toggled, "button_pressed"); + GDVIRTUAL_BIND(_toggled, "toggled_on"); ADD_SIGNAL(MethodInfo("pressed")); ADD_SIGNAL(MethodInfo("button_up")); ADD_SIGNAL(MethodInfo("button_down")); - ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed"))); + ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "toggled_on"))); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); diff --git a/scene/gui/code_edit.compat.inc b/scene/gui/code_edit.compat.inc new file mode 100644 index 0000000000..9107d6523f --- /dev/null +++ b/scene/gui/code_edit.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* code_edit.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +String CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196() { + return get_text_for_symbol_lookup(); +} + +void CodeEdit::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_text_for_symbol_lookup"), &CodeEdit::_get_text_for_symbol_lookup_bind_compat_73196); +} + +#endif diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index eee59606e3..68241337c9 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "code_edit.h" +#include "code_edit.compat.inc" #include "core/os/keyboard.h" #include "core/string/string_builder.h" diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index a3c968da60..addbb6e468 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -287,6 +287,11 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + String _get_text_for_symbol_lookup_bind_compat_73196(); + static void _bind_compatibility_methods(); +#endif + virtual void _update_theme_item_cache() override; /* Text manipulation */ diff --git a/scene/main/window.cpp b/scene/main/window.cpp index bd51f8eeaf..d0658c489d 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -1748,6 +1748,10 @@ Rect2i Window::fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) co Size2 Window::get_contents_minimum_size() const { ERR_READ_THREAD_GUARD_V(Size2()); + Vector2 ms; + if (GDVIRTUAL_CALL(_get_contents_minimum_size, ms)) { + return ms; + } return _get_contents_minimum_size(); } @@ -2760,6 +2764,8 @@ void Window::_bind_methods() { BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN); BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS); BIND_ENUM_CONSTANT(WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS); + + GDVIRTUAL_BIND(_get_contents_minimum_size); } Window::Window() { diff --git a/scene/main/window.h b/scene/main/window.h index 24142b8a91..18ddd89662 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -205,7 +205,6 @@ protected: virtual void _update_theme_item_cache(); virtual void _post_popup() {} - virtual Size2 _get_contents_minimum_size() const; static void _bind_methods(); void _notification(int p_what); @@ -217,6 +216,8 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + GDVIRTUAL0RC(Vector2, _get_contents_minimum_size) + public: enum { NOTIFICATION_VISIBILITY_CHANGED = 30, @@ -409,6 +410,8 @@ public: Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override; + virtual Size2 _get_contents_minimum_size() const; + Window(); ~Window(); }; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 87835a9522..f0971f1a34 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -199,6 +199,7 @@ SceneStringNames::SceneStringNames() { _window_input = StaticCString::create("_window_input"); window_input = StaticCString::create("window_input"); _window_unhandled_input = StaticCString::create("_window_unhandled_input"); + _get_contents_minimum_size = StaticCString::create("_get_contents_minimum_size"); theme_changed = StaticCString::create("theme_changed"); parameters_base_path = "parameters/"; diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index ad1135e24c..f31cf7b881 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -210,6 +210,7 @@ public: StringName _window_input; StringName _window_unhandled_input; StringName window_input; + StringName _get_contents_minimum_size; StringName theme_changed; StringName shader_overrides_group; diff --git a/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc new file mode 100644 index 0000000000..dc7817e66b --- /dev/null +++ b/servers/rendering/rendering_device.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* rendering_device.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +RID RenderingDevice::_shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary) { + return shader_create_from_bytecode(p_shader_binary, RID()); +} + +void RenderingDevice::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("shader_create_from_bytecode", "binary_data"), &RenderingDevice::_shader_create_from_bytecode_bind_compat_79606); +} + +#endif diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 1b0a3e9d0f..4e1ff07be4 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "rendering_device.h" +#include "rendering_device.compat.inc" #include "rendering_device_binds.h" diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 58da3df577..30d9b1c7b7 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -141,6 +141,11 @@ private: protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + RID _shader_create_from_bytecode_bind_compat_79606(const Vector<uint8_t> &p_shader_binary); + static void _bind_compatibility_methods(); +#endif + Capabilities device_capabilities; public: diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp index ffb89fc385..2dacabe588 100644 --- a/servers/xr/xr_positional_tracker.cpp +++ b/servers/xr/xr_positional_tracker.cpp @@ -153,7 +153,13 @@ void XRPositionalTracker::invalidate_pose(const StringName &p_action_name) { void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transform3D &p_transform, const Vector3 &p_linear_velocity, const Vector3 &p_angular_velocity, const XRPose::TrackingConfidence p_tracking_confidence) { Ref<XRPose> new_pose; - new_pose.instantiate(); + if (poses.has(p_action_name)) { + new_pose = poses[p_action_name]; + } else { + new_pose.instantiate(); + poses[p_action_name] = new_pose; + } + new_pose->set_name(p_action_name); new_pose->set_has_tracking_data(true); new_pose->set_transform(p_transform); @@ -161,7 +167,6 @@ void XRPositionalTracker::set_pose(const StringName &p_action_name, const Transf new_pose->set_angular_velocity(p_angular_velocity); new_pose->set_tracking_confidence(p_tracking_confidence); - poses[p_action_name] = new_pose; emit_signal(SNAME("pose_changed"), new_pose); // TODO discuss whether we also want to create and emit an InputEventXRPose event |
