diff options
174 files changed, 3105 insertions, 1359 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 00e74a7cce..ba9c6e4c60 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1137,8 +1137,8 @@ Array ProjectSettings::get_global_class_list() { Ref<ConfigFile> cf; cf.instantiate(); - if (cf->load(get_project_data_path().path_join("global_script_class_cache.cfg")) == OK) { - script_classes = cf->get_value("", "list"); + if (cf->load(get_global_class_list_path()) == OK) { + script_classes = cf->get_value("", "list", Array()); } else { #ifndef TOOLS_ENABLED // Script classes can't be recreated in exported project, so print an error. @@ -1148,11 +1148,15 @@ Array ProjectSettings::get_global_class_list() { return script_classes; } +String ProjectSettings::get_global_class_list_path() const { + return get_project_data_path().path_join("global_script_class_cache.cfg"); +} + void ProjectSettings::store_global_class_list(const Array &p_classes) { Ref<ConfigFile> cf; cf.instantiate(); cf->set_value("", "list", p_classes); - cf->save(get_project_data_path().path_join("global_script_class_cache.cfg")); + cf->save(get_global_class_list_path()); } bool ProjectSettings::has_custom_feature(const String &p_feature) const { diff --git a/core/config/project_settings.h b/core/config/project_settings.h index d1704a7c31..70f697741f 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -143,6 +143,7 @@ public: Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const; Array get_global_class_list(); void store_global_class_list(const Array &p_classes); + String get_global_class_list_path() const; bool has_setting(String p_var) const; String localize_path(const String &p_path) const; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9de901754a..c752bdd057 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -717,7 +717,7 @@ TypedArray<PackedVector2Array> Geometry2D::decompose_polygon_in_convex(const Vec TypedArray<PackedVector2Array> Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -739,7 +739,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polygons(const Vector<Vector2> & TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -750,7 +750,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vecto TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) { Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -761,7 +761,7 @@ TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2 TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -772,7 +772,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vect TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) { Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -783,7 +783,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) { Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type)); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); @@ -794,7 +794,7 @@ TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2> TypedArray<PackedVector2Array> Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type)); - Array ret; + TypedArray<PackedVector2Array> ret; for (int i = 0; i < polys.size(); ++i) { ret.push_back(polys[i]); diff --git a/core/doc_data.cpp b/core/doc_data.cpp index f90dbe1423..5e09e560d5 100644 --- a/core/doc_data.cpp +++ b/core/doc_data.cpp @@ -30,6 +30,14 @@ #include "doc_data.h" +String DocData::get_default_value_string(const Variant &p_value) { + if (p_value.get_type() == Variant::ARRAY) { + return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " "); + } else { + return p_value.get_construct_string().replace("\n", " "); + } +} + void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) { if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) { p_method.return_type = p_retinfo.hint_string; @@ -105,7 +113,7 @@ void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_propert p_property.getter = p_memberinfo.getter; if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) { - p_property.default_value = p_memberinfo.default_value.get_construct_string().replace("\n", ""); + p_property.default_value = get_default_value_string(p_memberinfo.default_value); } p_property.overridden = false; @@ -148,7 +156,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met int default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size()); if (default_arg_index >= 0) { Variant default_arg = p_methodinfo.default_arguments[default_arg_index]; - argument.default_value = default_arg.get_construct_string().replace("\n", ""); + argument.default_value = get_default_value_string(default_arg); } p_method.arguments.push_back(argument); } diff --git a/core/doc_data.h b/core/doc_data.h index 1cf4e4f206..c503a4e0d6 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -516,6 +516,8 @@ public: } }; + static String get_default_value_string(const Variant &p_value); + static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo); static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo); static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 63ed60a710..3bea013fab 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -856,6 +856,12 @@ static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionC return (GDExtensionVariantPtr)&self->operator[](p_index); } +void gdextension_array_ref(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from) { + Array *self = (Array *)p_self; + const Array *from = (const Array *)p_from; + self->_ref(*from); +} + void gdextension_array_set_typed(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script) { Array *self = reinterpret_cast<Array *>(p_self); const StringName *class_name = reinterpret_cast<const StringName *>(p_class_name); @@ -1136,6 +1142,7 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) { gde_interface.array_operator_index = gdextension_array_operator_index; gde_interface.array_operator_index_const = gdextension_array_operator_index_const; + gde_interface.array_ref = gdextension_array_ref; gde_interface.array_set_typed = gdextension_array_set_typed; /* Dictionary functions */ diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 7f8f7374e9..876a09beff 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -551,6 +551,7 @@ typedef struct { GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr + void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr void (*array_set_typed)(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr /* Dictionary functions */ diff --git a/core/input/input.cpp b/core/input/input.cpp index 3cf83fd64b..aa89facdd7 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -891,6 +891,31 @@ void Input::parse_input_event(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); +#ifdef DEBUG_ENABLED + uint64_t curr_frame = Engine::get_singleton()->get_process_frames(); + if (curr_frame != last_parsed_frame) { + frame_parsed_events.clear(); + last_parsed_frame = curr_frame; + frame_parsed_events.insert(p_event); + } else if (frame_parsed_events.has(p_event)) { + // It would be technically safe to send the same event in cases such as: + // - After an explicit flush. + // - In platforms using buffering when agile flushing is enabled, after one of the mid-frame flushes. + // - If platform doesn't use buffering and event accumulation is disabled. + // - If platform doesn't use buffering and the event type is not accumulable. + // However, it wouldn't be reasonable to ask users to remember the full ruleset and be aware at all times + // of the possibilites of the target platform, project settings and engine internals, which may change + // without prior notice. + // Therefore, the guideline is, "don't send the same event object more than once per frame". + WARN_PRINT_ONCE( + "An input event object is being parsed more than once in the same frame, which is unsafe.\n" + "If you are generating events in a script, you have to instantiate a new event instead of sending the same one more than once, unless the original one was sent on an earlier frame.\n" + "You can call duplicate() on the event to get a new instance with identical values."); + } else { + frame_parsed_events.insert(p_event); + } +#endif + if (use_accumulated_input) { if (buffered_events.is_empty() || !buffered_events.back()->get()->accumulate(p_event)) { buffered_events.push_back(p_event); diff --git a/core/input/input.h b/core/input/input.h index f2de56b6b9..c254650ef8 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -223,6 +223,10 @@ private: void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); List<Ref<InputEvent>> buffered_events; +#ifdef DEBUG_ENABLED + HashSet<Ref<InputEvent>> frame_parsed_events; + uint64_t last_parsed_frame = UINT64_MAX; +#endif friend class DisplayServer; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index df5486512d..61f5cfc22a 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -369,6 +369,14 @@ void ScriptServer::save_global_classes() { ProjectSettings::get_singleton()->store_global_class_list(gcarr); } +bool ScriptServer::has_global_classes() { + return !global_classes.is_empty(); +} + +String ScriptServer::get_global_class_cache_file_path() { + return ProjectSettings::get_singleton()->get_global_class_list_path(); +} + //////////////////// Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { diff --git a/core/object/script_language.h b/core/object/script_language.h index f82b58439f..b1393c7196 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -91,6 +91,8 @@ public: static void get_global_class_list(List<StringName> *r_global_classes); static void get_inheriters_list(const StringName &p_base_type, List<StringName> *r_classes); static void save_global_classes(); + static bool has_global_classes(); + static String get_global_class_cache_file_path(); static void init_languages(); static void finish_languages(); diff --git a/core/variant/array.cpp b/core/variant/array.cpp index 94d1596514..d3c5ca801f 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -64,7 +64,7 @@ void Array::_ref(const Array &p_from) const { _unref(); - _p = p_from._p; + _p = _fp; } void Array::_unref() const { @@ -191,62 +191,73 @@ uint32_t Array::recursive_hash(int recursion_count) const { return hash_fmix32(h); } -bool Array::_assign(const Array &p_array) { - bool can_convert = p_array._p->typed.type == Variant::NIL; - can_convert |= _p->typed.type == Variant::STRING && p_array._p->typed.type == Variant::STRING_NAME; - can_convert |= _p->typed.type == Variant::STRING_NAME && p_array._p->typed.type == Variant::STRING; +void Array::operator=(const Array &p_array) { + if (this == &p_array) { + return; + } + _ref(p_array); +} + +void Array::assign(const Array &p_array) { + const ContainerTypeValidate &typed = _p->typed; + const ContainerTypeValidate &source_typed = p_array._p->typed; - if (_p->typed.type != Variant::OBJECT && _p->typed.type == p_array._p->typed.type) { - //same type or untyped, just reference, should be fine - _ref(p_array); - } else if (_p->typed.type == Variant::NIL) { //from typed to untyped, must copy, but this is cheap anyway + if (typed == source_typed || typed.type == Variant::NIL || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) { + // from same to same or + // from anything to variants or + // from subclasses to base classes _p->array = p_array._p->array; - } else if (can_convert) { //from untyped to typed, must try to check if they are all valid - if (_p->typed.type == Variant::OBJECT) { - //for objects, it needs full validation, either can be converted or fail - for (int i = 0; i < p_array._p->array.size(); i++) { - const Variant &element = p_array._p->array[i]; - if (element.get_type() != Variant::OBJECT || !_p->typed.validate_object(element, "assign")) { - return false; - } - } - _p->array = p_array._p->array; //then just copy, which is cheap anyway + return; + } - } else { - //for non objects, we need to check if there is a valid conversion, which needs to happen one by one, so this is the worst case. - Vector<Variant> new_array; - new_array.resize(p_array._p->array.size()); - for (int i = 0; i < p_array._p->array.size(); i++) { - Variant src_val = p_array._p->array[i]; - if (src_val.get_type() == _p->typed.type) { - new_array.write[i] = src_val; - } else if (Variant::can_convert_strict(src_val.get_type(), _p->typed.type)) { - Variant *ptr = &src_val; - Callable::CallError ce; - Variant::construct(_p->typed.type, new_array.write[i], (const Variant **)&ptr, 1, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'."); - } - } else { - ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'."); - } + const Variant *source = p_array._p->array.ptr(); + int size = p_array._p->array.size(); + + if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) { + // from variants to objects or + // from base classes to subclasses + for (int i = 0; i < size; i++) { + const Variant &element = source[i]; + if (element.get_type() != Variant::NIL && (element.get_type() != Variant::OBJECT || !typed.validate_object(element, "assign"))) { + ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type))); } + } + _p->array = p_array._p->array; + return; + } - _p->array = new_array; + Vector<Variant> array; + array.resize(size); + Variant *data = array.ptrw(); + + if (source_typed.type == Variant::NIL && typed.type != Variant::OBJECT) { + // from variants to primitives + for (int i = 0; i < size; i++) { + const Variant *value = source + i; + if (value->get_type() == typed.type) { + data[i] = *value; + continue; + } + if (!Variant::can_convert_strict(value->get_type(), typed.type)) { + ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(value->get_type()) + "' to '" + Variant::get_type_name(typed.type) + "'."); + } + Callable::CallError ce; + Variant::construct(typed.type, data[i], &value, 1, ce); + ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type))); + } + } else if (Variant::can_convert_strict(source_typed.type, typed.type)) { + // from primitives to different convertable primitives + for (int i = 0; i < size; i++) { + const Variant *value = source + i; + Callable::CallError ce; + Variant::construct(typed.type, data[i], &value, 1, ce); + ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type))); } - } else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible - _ref(p_array); } else { - ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types."); + ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)", Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type))); } - return true; -} -void Array::operator=(const Array &p_array) { - if (this == &p_array) { - return; - } - _ref(p_array); + _p->array = array; } void Array::push_back(const Variant &p_value) { @@ -269,7 +280,15 @@ void Array::append_array(const Array &p_array) { Error Array::resize(int p_new_size) { ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state."); - return _p->array.resize(p_new_size); + Variant::Type &variant_type = _p->typed.type; + int old_size = _p->array.size(); + Error err = _p->array.resize_zeroed(p_new_size); + if (!err && variant_type != Variant::NIL && variant_type != Variant::OBJECT) { + for (int i = old_size; i < p_new_size; i++) { + VariantInternal::initialize(&_p->array.write[i], variant_type); + } + } + return err; } Error Array::insert(int p_pos, const Variant &p_value) { @@ -403,24 +422,22 @@ Array Array::duplicate(bool p_deep) const { Array Array::recursive_duplicate(bool p_deep, int recursion_count) const { Array new_arr; + new_arr._p->typed = _p->typed; if (recursion_count > MAX_RECURSION) { ERR_PRINT("Max recursion reached"); return new_arr; } - int element_count = size(); - new_arr.resize(element_count); - new_arr._p->typed = _p->typed; if (p_deep) { recursion_count++; + int element_count = size(); + new_arr.resize(element_count); for (int i = 0; i < element_count; i++) { new_arr[i] = get(i).recursive_duplicate(true, recursion_count); } } else { - for (int i = 0; i < element_count; i++) { - new_arr[i] = get(i); - } + new_arr._p->array = _p->array; } return new_arr; @@ -737,11 +754,7 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam _p = memnew(ArrayPrivate); _p->refcount.init(); set_typed(p_type, p_class_name, p_script); - _assign(p_from); -} - -bool Array::typed_assign(const Array &p_other) { - return _assign(p_other); + assign(p_from); } void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) { @@ -763,6 +776,10 @@ bool Array::is_typed() const { return _p->typed.type != Variant::NIL; } +bool Array::is_same_typed(const Array &p_other) const { + return _p->typed == p_other._p->typed; +} + uint32_t Array::get_typed_builtin() const { return _p->typed.type; } diff --git a/core/variant/array.h b/core/variant/array.h index d9ca3278fb..4ef8ba8ce7 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -43,13 +43,11 @@ class Callable; class Array { mutable ArrayPrivate *_p; - void _ref(const Array &p_from) const; void _unref() const; -protected: - bool _assign(const Array &p_array); - public: + void _ref(const Array &p_from) const; + Variant &operator[](int p_idx); const Variant &operator[](int p_idx) const; @@ -68,6 +66,7 @@ public: uint32_t recursive_hash(int recursion_count) const; void operator=(const Array &p_array); + void assign(const Array &p_array); void push_back(const Variant &p_value); _FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility void append_array(const Array &p_array); @@ -120,9 +119,9 @@ public: const void *id() const; - bool typed_assign(const Array &p_other); void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script); bool is_typed() const; + bool is_same_typed(const Array &p_other) const; uint32_t get_typed_builtin() const; StringName get_typed_class_name() const; Variant get_typed_script() const; diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h index 377be03bdf..796b66dc77 100644 --- a/core/variant/container_type_validate.h +++ b/core/variant/container_type_validate.h @@ -74,8 +74,15 @@ struct ContainerTypeValidate { return true; } + _FORCE_INLINE_ bool operator==(const ContainerTypeValidate &p_type) const { + return type == p_type.type && class_name == p_type.class_name && script == p_type.script; + } + _FORCE_INLINE_ bool operator!=(const ContainerTypeValidate &p_type) const { + return type != p_type.type || class_name != p_type.class_name || script != p_type.script; + } + // Coerces String and StringName into each other when needed. - _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") { + _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") const { if (type == Variant::NIL) { return true; } @@ -102,7 +109,7 @@ struct ContainerTypeValidate { return validate_object(inout_variant, p_operation); } - _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") { + _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") const { ERR_FAIL_COND_V(p_variant.get_type() != Variant::OBJECT, false); #ifdef DEBUG_ENABLED diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h index 5aeabbacf8..03e557819b 100644 --- a/core/variant/typed_array.h +++ b/core/variant/typed_array.h @@ -40,14 +40,9 @@ template <class T> class TypedArray : public Array { public: - template <class U> - _FORCE_INLINE_ void operator=(const TypedArray<U> &p_array) { - static_assert(__is_base_of(T, U)); - _assign(p_array); - } - _FORCE_INLINE_ void operator=(const Array &p_array) { - _assign(p_array); + ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type."); + _ref(p_array); } _FORCE_INLINE_ TypedArray(const Variant &p_variant) : Array(Array(p_variant), Variant::OBJECT, T::get_class_static(), Variant()) { @@ -62,22 +57,23 @@ public: //specialization for the rest of variant types -#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \ - template <> \ - class TypedArray<m_type> : public Array { \ - public: \ - _FORCE_INLINE_ void operator=(const Array &p_array) { \ - _assign(p_array); \ - } \ - _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \ - Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \ - } \ - _FORCE_INLINE_ TypedArray(const Array &p_array) : \ - Array(p_array, m_variant_type, StringName(), Variant()) { \ - } \ - _FORCE_INLINE_ TypedArray() { \ - set_typed(m_variant_type, StringName(), Variant()); \ - } \ +#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \ + template <> \ + class TypedArray<m_type> : public Array { \ + public: \ + _FORCE_INLINE_ void operator=(const Array &p_array) { \ + ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type."); \ + _ref(p_array); \ + } \ + _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \ + Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \ + } \ + _FORCE_INLINE_ TypedArray(const Array &p_array) : \ + Array(p_array, m_variant_type, StringName(), Variant()) { \ + } \ + _FORCE_INLINE_ TypedArray() { \ + set_typed(m_variant_type, StringName(), Variant()); \ + } \ }; MAKE_TYPED_ARRAY(bool, Variant::BOOL) diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index e7d1a2478f..0c0c8f657a 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2189,6 +2189,7 @@ static void _register_variant_builtin_methods() { bind_method(Array, is_empty, sarray(), varray()); bind_method(Array, clear, sarray(), varray()); bind_method(Array, hash, sarray(), varray()); + bind_method(Array, assign, sarray("array"), varray()); bind_method(Array, push_back, sarray("value"), varray()); bind_method(Array, push_front, sarray("value"), varray()); bind_method(Array, append, sarray("value"), varray()); @@ -2223,8 +2224,8 @@ static void _register_variant_builtin_methods() { bind_method(Array, all, sarray("method"), varray()); bind_method(Array, max, sarray(), varray()); bind_method(Array, min, sarray(), varray()); - bind_method(Array, typed_assign, sarray("array"), varray()); bind_method(Array, is_typed, sarray(), varray()); + bind_method(Array, is_same_typed, sarray("array"), varray()); bind_method(Array, get_typed_builtin, sarray(), varray()); bind_method(Array, get_typed_class_name, sarray(), varray()); bind_method(Array, get_typed_script, sarray(), varray()); @@ -2402,7 +2403,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedStringArray, remove_at, sarray("index"), varray()); bind_method(PackedStringArray, insert, sarray("at_index", "value"), varray()); bind_method(PackedStringArray, fill, sarray("value"), varray()); - bind_method(PackedStringArray, resize, sarray("new_size"), varray()); + bind_methodv(PackedStringArray, resize, &PackedStringArray::resize_zeroed, sarray("new_size"), varray()); bind_method(PackedStringArray, clear, sarray(), varray()); bind_method(PackedStringArray, has, sarray("value"), varray()); bind_method(PackedStringArray, reverse, sarray(), varray()); @@ -2426,7 +2427,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedVector2Array, remove_at, sarray("index"), varray()); bind_method(PackedVector2Array, insert, sarray("at_index", "value"), varray()); bind_method(PackedVector2Array, fill, sarray("value"), varray()); - bind_method(PackedVector2Array, resize, sarray("new_size"), varray()); + bind_methodv(PackedVector2Array, resize, &PackedVector2Array::resize_zeroed, sarray("new_size"), varray()); bind_method(PackedVector2Array, clear, sarray(), varray()); bind_method(PackedVector2Array, has, sarray("value"), varray()); bind_method(PackedVector2Array, reverse, sarray(), varray()); @@ -2450,7 +2451,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedVector3Array, remove_at, sarray("index"), varray()); bind_method(PackedVector3Array, insert, sarray("at_index", "value"), varray()); bind_method(PackedVector3Array, fill, sarray("value"), varray()); - bind_method(PackedVector3Array, resize, sarray("new_size"), varray()); + bind_methodv(PackedVector3Array, resize, &PackedVector3Array::resize_zeroed, sarray("new_size"), varray()); bind_method(PackedVector3Array, clear, sarray(), varray()); bind_method(PackedVector3Array, has, sarray("value"), varray()); bind_method(PackedVector3Array, reverse, sarray(), varray()); @@ -2474,7 +2475,7 @@ static void _register_variant_builtin_methods() { bind_method(PackedColorArray, remove_at, sarray("index"), varray()); bind_method(PackedColorArray, insert, sarray("at_index", "value"), varray()); bind_method(PackedColorArray, fill, sarray("value"), varray()); - bind_method(PackedColorArray, resize, sarray("new_size"), varray()); + bind_methodv(PackedColorArray, resize, &PackedColorArray::resize_zeroed, sarray("new_size"), varray()); bind_method(PackedColorArray, clear, sarray(), varray()); bind_method(PackedColorArray, has, sarray("value"), varray()); bind_method(PackedColorArray, reverse, sarray(), varray()); diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 21e1d21342..0d55ee4ae2 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -58,7 +58,13 @@ public: init_basis(v); break; case Variant::TRANSFORM3D: - init_transform(v); + init_transform3d(v); + break; + case Variant::PROJECTION: + init_projection(v); + break; + case Variant::COLOR: + init_color(v); break; case Variant::STRING_NAME: init_string_name(v); @@ -209,13 +215,12 @@ public: // Should be in the same order as Variant::Type for consistency. // Those primitive and vector types don't need an `init_` method: - // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, Color, RID. + // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, RID. // Object is a special case, handled via `object_assign_null`. _FORCE_INLINE_ static void init_string(Variant *v) { memnew_placement(v->_data._mem, String); v->type = Variant::STRING; } - _FORCE_INLINE_ static void init_transform2d(Variant *v) { v->_data._transform2d = (Transform2D *)Variant::Pools::_bucket_small.alloc(); memnew_placement(v->_data._transform2d, Transform2D); @@ -231,7 +236,7 @@ public: memnew_placement(v->_data._basis, Basis); v->type = Variant::BASIS; } - _FORCE_INLINE_ static void init_transform(Variant *v) { + _FORCE_INLINE_ static void init_transform3d(Variant *v) { v->_data._transform3d = (Transform3D *)Variant::Pools::_bucket_medium.alloc(); memnew_placement(v->_data._transform3d, Transform3D); v->type = Variant::TRANSFORM3D; @@ -241,6 +246,10 @@ public: memnew_placement(v->_data._projection, Projection); v->type = Variant::PROJECTION; } + _FORCE_INLINE_ static void init_color(Variant *v) { + memnew_placement(v->_data._mem, Color); + v->type = Variant::COLOR; + } _FORCE_INLINE_ static void init_string_name(Variant *v) { memnew_placement(v->_data._mem, StringName); v->type = Variant::STRING_NAME; @@ -1191,7 +1200,7 @@ struct VariantInitializer<Basis> { template <> struct VariantInitializer<Transform3D> { - static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); } + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform3d(v); } }; template <> struct VariantInitializer<Projection> { diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 87874deb8d..a40fcfbd47 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -910,7 +910,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, bool at_key = true; String key; - Token token2; bool need_comma = false; while (true) { @@ -920,18 +919,18 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } if (at_key) { - Error err = get_token(p_stream, token2, line, r_err_str); + Error err = get_token(p_stream, token, line, r_err_str); if (err != OK) { return err; } - if (token2.type == TK_PARENTHESIS_CLOSE) { + if (token.type == TK_PARENTHESIS_CLOSE) { value = ref.is_valid() ? Variant(ref) : Variant(obj); return OK; } if (need_comma) { - if (token2.type != TK_COMMA) { + if (token.type != TK_COMMA) { r_err_str = "Expected '}' or ','"; return ERR_PARSE_ERROR; } else { @@ -940,31 +939,31 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } } - if (token2.type != TK_STRING) { + if (token.type != TK_STRING) { r_err_str = "Expected property name as string"; return ERR_PARSE_ERROR; } - key = token2.value; + key = token.value; - err = get_token(p_stream, token2, line, r_err_str); + err = get_token(p_stream, token, line, r_err_str); if (err != OK) { return err; } - if (token2.type != TK_COLON) { + if (token.type != TK_COLON) { r_err_str = "Expected ':'"; return ERR_PARSE_ERROR; } at_key = false; } else { - Error err = get_token(p_stream, token2, line, r_err_str); + Error err = get_token(p_stream, token, line, r_err_str); if (err != OK) { return err; } Variant v; - err = parse_value(token2, v, p_stream, line, r_err_str, p_res_parser); + err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser); if (err) { return err; } @@ -1026,6 +1025,89 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, return ERR_PARSE_ERROR; } } + } else if (id == "Array") { + Error err = OK; + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_BRACKET_OPEN) { + r_err_str = "Expected '['"; + return ERR_PARSE_ERROR; + } + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_IDENTIFIER) { + r_err_str = "Expected type identifier"; + return ERR_PARSE_ERROR; + } + + static HashMap<StringName, Variant::Type> builtin_types; + if (builtin_types.is_empty()) { + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i; + } + } + + Array array = Array(); + bool got_bracket_token = false; + if (builtin_types.has(token.value)) { + array.set_typed(builtin_types.get(token.value), StringName(), Variant()); + } else if (token.value == "Resource" || token.value == "SubResource" || token.value == "ExtResource") { + Variant resource; + err = parse_value(token, resource, p_stream, line, r_err_str, p_res_parser); + if (err) { + if (token.value == "Resource" && err == ERR_PARSE_ERROR && r_err_str == "Expected '('" && token.type == TK_BRACKET_CLOSE) { + err = OK; + r_err_str = String(); + array.set_typed(Variant::OBJECT, token.value, Variant()); + got_bracket_token = true; + } else { + return err; + } + } else { + Ref<Script> script = resource; + if (script.is_valid() && script->is_valid()) { + array.set_typed(Variant::OBJECT, script->get_instance_base_type(), script); + } + } + } else if (ClassDB::class_exists(token.value)) { + array.set_typed(Variant::OBJECT, token.value, Variant()); + } + + if (!got_bracket_token) { + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_BRACKET_CLOSE) { + r_err_str = "Expected ']'"; + return ERR_PARSE_ERROR; + } + } + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_PARENTHESIS_OPEN) { + r_err_str = "Expected '('"; + return ERR_PARSE_ERROR; + } + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_BRACKET_OPEN) { + r_err_str = "Expected '['"; + return ERR_PARSE_ERROR; + } + + Array values; + err = _parse_array(values, p_stream, line, r_err_str, p_res_parser); + if (err) { + return err; + } + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')'"; + return ERR_PARSE_ERROR; + } + + array.assign(values); + + value = array; } else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") { Vector<uint8_t> args; Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str); @@ -1843,6 +1925,38 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } break; case Variant::ARRAY: { + Array array = p_variant; + if (array.get_typed_builtin() != Variant::NIL) { + p_store_string_func(p_store_string_ud, "Array["); + + Variant::Type builtin_type = (Variant::Type)array.get_typed_builtin(); + StringName class_name = array.get_typed_class_name(); + Ref<Script> script = array.get_typed_script(); + + if (script.is_valid()) { + String resource_text = String(); + if (p_encode_res_func) { + resource_text = p_encode_res_func(p_encode_res_ud, script); + } + if (resource_text.is_empty() && script->get_path().is_resource_file()) { + resource_text = "Resource(\"" + script->get_path() + "\")"; + } + + if (!resource_text.is_empty()) { + p_store_string_func(p_store_string_ud, resource_text); + } else { + ERR_PRINT("Failed to encode a path to a custom script for an array type."); + p_store_string_func(p_store_string_ud, class_name); + } + } else if (class_name != StringName()) { + p_store_string_func(p_store_string_ud, class_name); + } else { + p_store_string_func(p_store_string_ud, Variant::get_type_name(builtin_type)); + } + + p_store_string_func(p_store_string_ud, "]("); + } + if (recursion_count > MAX_RECURSION) { ERR_PRINT("Max recursion reached"); p_store_string_func(p_store_string_ud, "[]"); @@ -1850,7 +1964,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str recursion_count++; p_store_string_func(p_store_string_ud, "["); - Array array = p_variant; int len = array.size(); for (int i = 0; i < len; i++) { if (i > 0) { @@ -1862,11 +1975,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "]"); } + if (array.get_typed_builtin() != Variant::NIL) { + p_store_string_func(p_store_string_ud, ")"); + } + } break; case Variant::PACKED_BYTE_ARRAY: { p_store_string_func(p_store_string_ud, "PackedByteArray("); - String s; Vector<uint8_t> data = p_variant; int len = data.size(); const uint8_t *ptr = data.ptr(); @@ -1954,15 +2070,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str int len = data.size(); const String *ptr = data.ptr(); - String s; - //write_string("\n"); - for (int i = 0; i < len; i++) { if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - String str = ptr[i]; - p_store_string_func(p_store_string_ud, "\"" + str.c_escape() + "\""); + p_store_string_func(p_store_string_ud, "\"" + ptr[i].c_escape() + "\""); } p_store_string_func(p_store_string_ud, ")"); @@ -2010,9 +2122,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str if (i > 0) { p_store_string_func(p_store_string_ud, ", "); } - p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a)); } + p_store_string_func(p_store_string_ud, ")"); } break; diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml index 0f1ce127cd..9a7872f50e 100644 --- a/doc/classes/AnimationNodeBlendSpace1D.xml +++ b/doc/classes/AnimationNodeBlendSpace1D.xml @@ -67,6 +67,9 @@ </method> </methods> <members> + <member name="blend_mode" type="int" setter="set_blend_mode" getter="get_blend_mode" enum="AnimationNodeBlendSpace1D.BlendMode" default="0"> + Controls the interpolation between animations. See [enum BlendMode] constants. + </member> <member name="max_space" type="float" setter="set_max_space" getter="get_max_space" default="1.0"> The blend space's axis's upper limit for the points' position. See [method add_blend_point]. </member> @@ -84,4 +87,15 @@ Label of the virtual axis of the blend space. </member> </members> + <constants> + <constant name="BLEND_MODE_INTERPOLATED" value="0" enum="BlendMode"> + The interpolation between animations is linear. + </constant> + <constant name="BLEND_MODE_DISCRETE" value="1" enum="BlendMode"> + The blend space plays the animation of the node the blending position is closest to. Useful for frame-by-frame 2D animations. + </constant> + <constant name="BLEND_MODE_DISCRETE_CARRY" value="2" enum="BlendMode"> + Similar to [constant BLEND_MODE_DISCRETE], but starts the new animation at the last animation's playback position. + </constant> + </constants> </class> diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml index 3f76cc16ec..8a98921a60 100644 --- a/doc/classes/Area2D.xml +++ b/doc/classes/Area2D.xml @@ -87,8 +87,9 @@ <member name="gravity_point_center" type="Vector2" setter="set_gravity_point_center" getter="get_gravity_point_center" default="Vector2(0, 1)"> If gravity is a point (see [member gravity_point]), this will be the point of attraction. </member> - <member name="gravity_point_distance_scale" type="float" setter="set_gravity_point_distance_scale" getter="get_gravity_point_distance_scale" default="0.0"> - The falloff factor for point gravity. The greater the value, the faster gravity decreases with distance. + <member name="gravity_point_unit_distance" type="float" setter="set_gravity_point_unit_distance" getter="get_gravity_point_unit_distance" default="0.0"> + The distance at which the gravity strength is equal to [member gravity]. For example, on a planet 100 pixels in radius with a surface gravity of 4.0 px/s², set the [member gravity] to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 pixels from the center the gravity will be 1.0 px/s² (twice the distance, 1/4th the gravity), at 50 pixels it will be 16.0 px/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </member> <member name="gravity_space_override" type="int" setter="set_gravity_space_override_mode" getter="get_gravity_space_override_mode" enum="Area2D.SpaceOverride" default="0"> Override mode for gravity calculations within this area. See [enum SpaceOverride] for possible values. diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml index d40bca99d8..bd046b7cb8 100644 --- a/doc/classes/Area3D.xml +++ b/doc/classes/Area3D.xml @@ -86,8 +86,9 @@ <member name="gravity_point_center" type="Vector3" setter="set_gravity_point_center" getter="get_gravity_point_center" default="Vector3(0, -1, 0)"> If gravity is a point (see [member gravity_point]), this will be the point of attraction. </member> - <member name="gravity_point_distance_scale" type="float" setter="set_gravity_point_distance_scale" getter="get_gravity_point_distance_scale" default="0.0"> - The falloff factor for point gravity. The greater the value, the faster gravity decreases with distance. + <member name="gravity_point_unit_distance" type="float" setter="set_gravity_point_unit_distance" getter="get_gravity_point_unit_distance" default="0.0"> + The distance at which the gravity strength is equal to [member gravity]. For example, on a planet 100 meters in radius with a surface gravity of 4.0 m/s², set the [member gravity] to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 meters from the center the gravity will be 1.0 m/s² (twice the distance, 1/4th the gravity), at 50 meters it will be 16.0 m/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </member> <member name="gravity_space_override" type="int" setter="set_gravity_space_override_mode" getter="get_gravity_space_override_mode" enum="Area3D.SpaceOverride" default="0"> Override mode for gravity calculations within this area. See [enum SpaceOverride] for possible values. diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml index 9be5e60f4a..213a2254af 100644 --- a/doc/classes/Array.xml +++ b/doc/classes/Array.xml @@ -59,14 +59,14 @@ <param index="2" name="class_name" type="StringName" /> <param index="3" name="script" type="Variant" /> <description> - Creates a typed array from the [param base] array. The base array can't be already typed. + Creates a typed array from the [param base] array. </description> </constructor> <constructor name="Array"> <return type="Array" /> <param index="0" name="from" type="Array" /> <description> - Constructs an [Array] as a copy of the given [Array]. + Returns the same array as [param from]. If you need a copy of the array, use [method duplicate]. </description> </constructor> <constructor name="Array"> @@ -200,6 +200,13 @@ [/codeblock] </description> </method> + <method name="assign"> + <return type="void" /> + <param index="0" name="array" type="Array" /> + <description> + Assigns elements of another [param array] into the array. Resizes the array to match [param array]. Performs type conversions if the array is typed. + </description> + </method> <method name="back" qualifiers="const"> <return type="Variant" /> <description> @@ -395,6 +402,13 @@ Returns [code]true[/code] if the array is read-only. See [method make_read_only]. Arrays are automatically read-only if declared with [code]const[/code] keyword. </description> </method> + <method name="is_same_typed" qualifiers="const"> + <return type="bool" /> + <param index="0" name="array" type="Array" /> + <description> + Returns [code]true[/code] if the array is typed the same as [param array]. + </description> + </method> <method name="is_typed" qualifiers="const"> <return type="bool" /> <description> @@ -609,13 +623,6 @@ [/codeblocks] </description> </method> - <method name="typed_assign"> - <return type="bool" /> - <param index="0" name="array" type="Array" /> - <description> - Assigns a different [Array] to this array reference. It the array is typed, the new array's type must be compatible and its elements will be automatically converted. - </description> - </method> </methods> <operators> <operator name="operator !="> diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml index 36f12dd5c4..ae331f8491 100644 --- a/doc/classes/AudioServer.xml +++ b/doc/classes/AudioServer.xml @@ -29,13 +29,6 @@ Adds an [AudioEffect] effect to the bus [param bus_idx] at [param at_position]. </description> </method> - <method name="capture_get_device_list"> - <return type="PackedStringArray" /> - <description> - Returns the names of all audio input devices detected on the system. - [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. - </description> - </method> <method name="generate_bus_layout" qualifiers="const"> <return type="AudioBusLayout" /> <description> @@ -117,10 +110,11 @@ Returns the volume of the bus at index [param bus_idx] in dB. </description> </method> - <method name="get_device_list"> + <method name="get_input_device_list"> <return type="PackedStringArray" /> <description> - Returns the names of all audio devices detected on the system. + Returns the names of all audio input devices detected on the system. + [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </description> </method> <method name="get_mix_rate" qualifiers="const"> @@ -129,6 +123,12 @@ Returns the sample rate at the output of the [AudioServer]. </description> </method> + <method name="get_output_device_list"> + <return type="PackedStringArray" /> + <description> + Returns the names of all audio output devices detected on the system. + </description> + </method> <method name="get_output_latency" qualifiers="const"> <return type="float" /> <description> @@ -302,12 +302,12 @@ <member name="bus_count" type="int" setter="set_bus_count" getter="get_bus_count" default="1"> Number of available audio buses. </member> - <member name="capture_device" type="String" setter="capture_set_device" getter="capture_get_device" default=""Default""> - Name of the current device for audio input (see [method capture_get_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. + <member name="input_device" type="String" setter="set_input_device" getter="get_input_device" default=""Default""> + Name of the current device for audio input (see [method get_input_device_list]). On systems with multiple audio inputs (such as analog, USB and HDMI audio), this can be used to select the audio input device. The value [code]"Default"[/code] will record audio on the system-wide default audio input. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </member> - <member name="device" type="String" setter="set_device" getter="get_device" default=""Default""> - Name of the current device for audio output (see [method get_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. + <member name="output_device" type="String" setter="set_output_device" getter="get_output_device" default=""Default""> + Name of the current device for audio output (see [method get_output_device_list]). On systems with multiple audio outputs (such as analog, USB and HDMI audio), this can be used to select the audio output device. The value [code]"Default"[/code] will play audio on the system-wide default audio output. If an invalid device name is set, the value will be reverted back to [code]"Default"[/code]. </member> <member name="playback_speed_scale" type="float" setter="set_playback_speed_scale" getter="get_playback_speed_scale" default="1.0"> Scales the rate at which audio is played (i.e. setting it to [code]0.5[/code] will make the audio be played at half its speed). diff --git a/doc/classes/Camera2D.xml b/doc/classes/Camera2D.xml index 9315a85e1f..4156c9451a 100644 --- a/doc/classes/Camera2D.xml +++ b/doc/classes/Camera2D.xml @@ -55,6 +55,18 @@ [b]Note:[/b] The returned value is not the same as [member Node2D.global_position], as it is affected by the drag properties. It is also not the same as the current position if [member position_smoothing_enabled] is [code]true[/code] (see [method get_screen_center_position]). </description> </method> + <method name="is_current" qualifiers="const"> + <return type="bool" /> + <description> + Returns [code]true[/code] if this [Camera2D] is the active camera (see [method Viewport.get_camera_2d]). + </description> + </method> + <method name="make_current"> + <return type="void" /> + <description> + Forces this [Camera2D] to become the current active one. [member enabled] must be [code]true[/code]. + </description> + </method> <method name="reset_smoothing"> <return type="void" /> <description> @@ -83,9 +95,6 @@ <member name="anchor_mode" type="int" setter="set_anchor_mode" getter="get_anchor_mode" enum="Camera2D.AnchorMode" default="1"> The Camera2D's anchor point. See [enum AnchorMode] constants. </member> - <member name="current" type="bool" setter="set_current" getter="is_current" default="false"> - If [code]true[/code], the camera acts as the active camera for its [Viewport] ancestor. Only one camera can be current in a given viewport, so setting a different camera in the same viewport [code]current[/code] will disable whatever camera was already active in that viewport. - </member> <member name="custom_viewport" type="Node" setter="set_custom_viewport" getter="get_custom_viewport"> The custom [Viewport] node attached to the [Camera2D]. If [code]null[/code] or not a [Viewport], uses the default viewport instead. </member> @@ -124,6 +133,10 @@ <member name="editor_draw_screen" type="bool" setter="set_screen_drawing_enabled" getter="is_screen_drawing_enabled" default="true"> If [code]true[/code], draws the camera's screen rectangle in the editor. </member> + <member name="enabled" type="bool" setter="set_enabled" getter="is_enabled" default="true"> + Controls whether the camera can be active or not. If [code]true[/code], the [Camera2D] will become the main camera when it enters the scene tree and there is no active camera currently (see [method Viewport.get_camera_2d]). + When the camera is currently active and [member enabled] is set to [code]false[/code], the next enabled [Camera2D] in the scene tree will become active. + </member> <member name="ignore_rotation" type="bool" setter="set_ignore_rotation" getter="is_ignoring_rotation" default="true"> If [code]true[/code], the camera's rendered view is not affected by its [member Node2D.rotation] and [member Node2D.global_rotation]. </member> diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml index 776a9f6fc1..1591aa59bf 100644 --- a/doc/classes/Dictionary.xml +++ b/doc/classes/Dictionary.xml @@ -153,7 +153,7 @@ <return type="Dictionary" /> <param index="0" name="from" type="Dictionary" /> <description> - Returns the same array as [param from]. If you need a copy of the array, use [method duplicate]. + Returns the same dictionary as [param from]. If you need a copy of the dictionary, use [method duplicate]. </description> </constructor> </constructors> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 326c4f6456..c097c8f685 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -409,6 +409,7 @@ <description> Adds a custom type, which will appear in the list of nodes or resources. An icon can be optionally passed. When a given node or resource is selected, the base type will be instantiated (e.g. "Node3D", "Control", "Resource"), then the script will be loaded and set to this object. + [b]Note:[/b] The base type is the base engine class which this type's class hierarchy inherits, not any custom type parent classes. You can use the virtual method [method _handles] to check if your custom object is being edited by checking the script or using the [code]is[/code] keyword. During run-time, this will be a simple object with a script so this function does not need to be called then. [b]Note:[/b] Custom types added this way are not true classes. They are just a helper to create a node with specific script. diff --git a/doc/classes/Label3D.xml b/doc/classes/Label3D.xml index fb3c949e48..cfb47ff4c3 100644 --- a/doc/classes/Label3D.xml +++ b/doc/classes/Label3D.xml @@ -32,6 +32,12 @@ </method> </methods> <members> + <member name="alpha_antialiasing_edge" type="float" setter="set_alpha_antialiasing_edge" getter="get_alpha_antialiasing_edge" default="0.0"> + Threshold at which antialiasing will be applied on the alpha channel. + </member> + <member name="alpha_antialiasing_mode" type="int" setter="set_alpha_antialiasing" getter="get_alpha_antialiasing" enum="BaseMaterial3D.AlphaAntiAliasing" default="0"> + The type of alpha antialiasing to apply. See [enum BaseMaterial3D.AlphaAntiAliasing]. + </member> <member name="alpha_cut" type="int" setter="set_alpha_cut_mode" getter="get_alpha_cut_mode" enum="Label3D.AlphaCutMode" default="0"> The alpha cutting mode to use for the sprite. See [enum AlphaCutMode] for possible values. </member> diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index f1316fa991..93e88347d4 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -984,25 +984,23 @@ <constant name="AREA_PARAM_GRAVITY_IS_POINT" value="3" enum="AreaParameter"> Constant to set/get whether the gravity vector of an area is a direction, or a center point. </constant> - <constant name="AREA_PARAM_GRAVITY_DISTANCE_SCALE" value="4" enum="AreaParameter"> - Constant to set/get the falloff factor for point gravity of an area. The greater this value is, the faster the strength of gravity decreases with the square of distance. + <constant name="AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE" value="4" enum="AreaParameter"> + Constant to set/get the distance at which the gravity strength is equal to the gravity controlled by [constant AREA_PARAM_GRAVITY]. For example, on a planet 100 pixels in radius with a surface gravity of 4.0 px/s², set the gravity to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 pixels from the center the gravity will be 1.0 px/s² (twice the distance, 1/4th the gravity), at 50 pixels it will be 16.0 px/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When the unit distance is set to 0.0, the gravity will be constant regardless of distance. </constant> - <constant name="AREA_PARAM_GRAVITY_POINT_ATTENUATION" value="5" enum="AreaParameter"> - This constant was used to set/get the falloff factor for point gravity. It has been superseded by [constant AREA_PARAM_GRAVITY_DISTANCE_SCALE]. - </constant> - <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="6" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="5" enum="AreaParameter"> Constant to set/get linear damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_LINEAR_DAMP" value="7" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP" value="6" enum="AreaParameter"> Constant to set/get the linear damping factor of an area. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="8" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="7" enum="AreaParameter"> Constant to set/get angular damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP" value="9" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP" value="8" enum="AreaParameter"> Constant to set/get the angular damping factor of an area. </constant> - <constant name="AREA_PARAM_PRIORITY" value="10" enum="AreaParameter"> + <constant name="AREA_PARAM_PRIORITY" value="9" enum="AreaParameter"> Constant to set/get the priority (order of processing) of an area. </constant> <constant name="AREA_SPACE_OVERRIDE_DISABLED" value="0" enum="AreaSpaceOverrideMode"> diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index e62bda0dd3..5b261d8414 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -1315,37 +1315,35 @@ <constant name="AREA_PARAM_GRAVITY_IS_POINT" value="3" enum="AreaParameter"> Constant to set/get whether the gravity vector of an area is a direction, or a center point. </constant> - <constant name="AREA_PARAM_GRAVITY_DISTANCE_SCALE" value="4" enum="AreaParameter"> - Constant to set/get the falloff factor for point gravity of an area. The greater this value is, the faster the strength of gravity decreases with the square of distance. + <constant name="AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE" value="4" enum="AreaParameter"> + Constant to set/get the distance at which the gravity strength is equal to the gravity controlled by [constant AREA_PARAM_GRAVITY]. For example, on a planet 100 meters in radius with a surface gravity of 4.0 m/s², set the gravity to 4.0 and the unit distance to 100.0. The gravity will have falloff according to the inverse square law, so in the example, at 200 meters from the center the gravity will be 1.0 m/s² (twice the distance, 1/4th the gravity), at 50 meters it will be 16.0 m/s² (half the distance, 4x the gravity), and so on. + The above is true only when the unit distance is a positive number. When this is set to 0.0, the gravity will be constant regardless of distance. </constant> - <constant name="AREA_PARAM_GRAVITY_POINT_ATTENUATION" value="5" enum="AreaParameter"> - This constant was used to set/get the falloff factor for point gravity. It has been superseded by [constant AREA_PARAM_GRAVITY_DISTANCE_SCALE]. - </constant> - <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="6" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE" value="5" enum="AreaParameter"> Constant to set/get linear damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_LINEAR_DAMP" value="7" enum="AreaParameter"> + <constant name="AREA_PARAM_LINEAR_DAMP" value="6" enum="AreaParameter"> Constant to set/get the linear damping factor of an area. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="8" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE" value="7" enum="AreaParameter"> Constant to set/get angular damping override mode in an area. See [enum AreaSpaceOverrideMode] for possible values. </constant> - <constant name="AREA_PARAM_ANGULAR_DAMP" value="9" enum="AreaParameter"> + <constant name="AREA_PARAM_ANGULAR_DAMP" value="8" enum="AreaParameter"> Constant to set/get the angular damping factor of an area. </constant> - <constant name="AREA_PARAM_PRIORITY" value="10" enum="AreaParameter"> + <constant name="AREA_PARAM_PRIORITY" value="9" enum="AreaParameter"> Constant to set/get the priority (order of processing) of an area. </constant> - <constant name="AREA_PARAM_WIND_FORCE_MAGNITUDE" value="11" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_FORCE_MAGNITUDE" value="10" enum="AreaParameter"> Constant to set/get the magnitude of area-specific wind force. </constant> - <constant name="AREA_PARAM_WIND_SOURCE" value="12" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_SOURCE" value="11" enum="AreaParameter"> Constant to set/get the 3D vector that specifies the origin from which an area-specific wind blows. </constant> - <constant name="AREA_PARAM_WIND_DIRECTION" value="13" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_DIRECTION" value="12" enum="AreaParameter"> Constant to set/get the 3D vector that specifies the direction in which an area-specific wind blows. </constant> - <constant name="AREA_PARAM_WIND_ATTENUATION_FACTOR" value="14" enum="AreaParameter"> + <constant name="AREA_PARAM_WIND_ATTENUATION_FACTOR" value="13" enum="AreaParameter"> Constant to set/get the exponential rate at which wind force decreases with distance from its origin. </constant> <constant name="AREA_SPACE_OVERRIDE_DISABLED" value="0" enum="AreaSpaceOverrideMode"> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index f1ada58e0d..95bd060fc6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -426,6 +426,9 @@ <member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await. </member> + <member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1"> + When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs. + </member> <member name="debug/gdscript/warnings/return_value_discarded" type="int" setter="" getter="" default="0"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum. </member> @@ -474,6 +477,9 @@ <member name="debug/gdscript/warnings/unsafe_property_access" type="int" setter="" getter="" default="0"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when accessing a property whose presence is not guaranteed at compile-time in the class. </member> + <member name="debug/gdscript/warnings/unsafe_void_return" type="int" setter="" getter="" default="0"> + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when returning a call from a [code]void[/code] function when such call cannot be guaranteed to be also [code]void[/code]. + </member> <member name="debug/gdscript/warnings/unused_local_constant" type="int" setter="" getter="" default="1"> When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local constant is never used. </member> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index fc08a16619..8bb3073000 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3245,12 +3245,12 @@ <description> </description> </method> - <method name="viewport_set_disable_environment"> + <method name="viewport_set_environment_mode"> <return type="void" /> <param index="0" name="viewport" type="RID" /> - <param index="1" name="disabled" type="bool" /> + <param index="1" name="mode" type="int" enum="RenderingServer.ViewportEnvironmentMode" /> <description> - If [code]true[/code], rendering of a viewport's environment is disabled. + Sets the viewport's environment mode which allows enabling or disabling rendering of 3D environment over 2D canvas. When disabled, 2D will not be affected by the environment. When enabled, 2D will be affected by the environment if the environment background mode is [constant ENV_BG_CANVAS]. The default behaviour is to inherit the setting from the viewport's parent. If the topmost parent is also set to [constant VIEWPORT_ENVIRONMENT_INHERIT], then the behavior will be the same as if it was set to [constant VIEWPORT_ENVIRONMENT_ENABLED]. </description> </method> <method name="viewport_set_fsr_sharpness"> @@ -4135,6 +4135,18 @@ <constant name="VIEWPORT_CLEAR_ONLY_NEXT_FRAME" value="2" enum="ViewportClearMode"> The viewport is cleared once, then the clear mode is set to [constant VIEWPORT_CLEAR_NEVER]. </constant> + <constant name="VIEWPORT_ENVIRONMENT_DISABLED" value="0" enum="ViewportEnvironmentMode"> + Disable rendering of 3D environment over 2D canvas. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_ENABLED" value="1" enum="ViewportEnvironmentMode"> + Enable rendering of 3D environment over 2D canvas. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_INHERIT" value="2" enum="ViewportEnvironmentMode"> + Inherit enable/disable value from parent. If topmost parent is also set to inherit, then this has the same behavior as [constant VIEWPORT_ENVIRONMENT_ENABLED]. + </constant> + <constant name="VIEWPORT_ENVIRONMENT_MAX" value="3" enum="ViewportEnvironmentMode"> + Max value of [enum ViewportEnvironmentMode] enum. + </constant> <constant name="VIEWPORT_SDF_OVERSIZE_100_PERCENT" value="0" enum="ViewportSDFOversize"> </constant> <constant name="VIEWPORT_SDF_OVERSIZE_120_PERCENT" value="1" enum="ViewportSDFOversize"> diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml index 7ff45edcbc..e1f3a52a1d 100644 --- a/doc/classes/SpriteBase3D.xml +++ b/doc/classes/SpriteBase3D.xml @@ -38,6 +38,12 @@ </method> </methods> <members> + <member name="alpha_antialiasing_edge" type="float" setter="set_alpha_antialiasing_edge" getter="get_alpha_antialiasing_edge" default="0.0"> + Threshold at which antialiasing will be applied on the alpha channel. + </member> + <member name="alpha_antialiasing_mode" type="int" setter="set_alpha_antialiasing" getter="get_alpha_antialiasing" enum="BaseMaterial3D.AlphaAntiAliasing" default="0"> + The type of alpha antialiasing to apply. See [enum BaseMaterial3D.AlphaAntiAliasing]. + </member> <member name="alpha_cut" type="int" setter="set_alpha_cut_mode" getter="get_alpha_cut_mode" enum="SpriteBase3D.AlphaCutMode" default="0"> The alpha cutting mode to use for the sprite. See [enum AlphaCutMode] for possible values. </member> diff --git a/doc/classes/VideoStream.xml b/doc/classes/VideoStream.xml index 2797ad3513..648c3edd73 100644 --- a/doc/classes/VideoStream.xml +++ b/doc/classes/VideoStream.xml @@ -8,4 +8,18 @@ </description> <tutorials> </tutorials> + <methods> + <method name="_instantiate_playback" qualifiers="virtual"> + <return type="VideoStreamPlayback" /> + <description> + Called when the video starts playing, to initialize and return a subclass of [VideoStreamPlayback]. + </description> + </method> + </methods> + <members> + <member name="file" type="String" setter="set_file" getter="get_file" default=""""> + The video file path or URI that this [VideoStream] resource handles. + For [VideoStreamTheora], this filename should be an Ogg Theora video file with the [code].ogv[/code] extension. + </member> + </members> </class> diff --git a/doc/classes/VideoStreamPlayback.xml b/doc/classes/VideoStreamPlayback.xml new file mode 100644 index 0000000000..8d8b4fe5b1 --- /dev/null +++ b/doc/classes/VideoStreamPlayback.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VideoStreamPlayback" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> + <brief_description> + Internal class used by [VideoStream] to manage playback state when played from a [VideoStreamPlayer]. + </brief_description> + <description> + This class is intended to be overridden by video decoder extensions with custom implementations of [VideoStream]. + </description> + <tutorials> + </tutorials> + <methods> + <method name="_get_channels" qualifiers="virtual const"> + <return type="int" /> + <description> + Returns the number of audio channels. + </description> + </method> + <method name="_get_length" qualifiers="virtual const"> + <return type="float" /> + <description> + Returns the video duration in seconds, if known, or 0 if unknown. + </description> + </method> + <method name="_get_mix_rate" qualifiers="virtual const"> + <return type="int" /> + <description> + Returns the audio sample rate used for mixing. + </description> + </method> + <method name="_get_playback_position" qualifiers="virtual const"> + <return type="float" /> + <description> + Return the current playback timestamp. Called in response to the [member VideoStreamPlayer.stream_position] getter. + </description> + </method> + <method name="_get_texture" qualifiers="virtual const"> + <return type="Texture2D" /> + <description> + Allocates a [Texture2D] in which decoded video frames will be drawn. + </description> + </method> + <method name="_is_paused" qualifiers="virtual const"> + <return type="bool" /> + <description> + Returns the paused status, as set by [method _set_paused]. + </description> + </method> + <method name="_is_playing" qualifiers="virtual const"> + <return type="bool" /> + <description> + Returns the playback state, as determined by calls to [method _play] and [method _stop]. + </description> + </method> + <method name="_play" qualifiers="virtual"> + <return type="void" /> + <description> + Called in response to [member VideoStreamPlayer.autoplay] or [method VideoStreamPlayer.play]. Note that manual playback may also invoke [method _stop] multiple times before this method is called. [method _is_playing] should return true once playing. + </description> + </method> + <method name="_seek" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="time" type="float" /> + <description> + Seeks to [code]time[/code] seconds. Called in response to the [member VideoStreamPlayer.stream_position] setter. + </description> + </method> + <method name="_set_audio_track" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="idx" type="int" /> + <description> + Select the audio track [code]idx[/code]. Called when playback starts, and in response to the [member VideoStreamPlayer.audio_track] setter. + </description> + </method> + <method name="_set_paused" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="paused" type="bool" /> + <description> + Set the paused status of video playback. [method _is_paused] must return [code]paused[/code]. Called in response to the [member VideoStreamPlayer.paused] setter. + </description> + </method> + <method name="_stop" qualifiers="virtual"> + <return type="void" /> + <description> + Stops playback. May be called multiple times before [method _play], or in response to [method VideoStreamPlayer.stop]. [method _is_playing] should return false once stopped. + </description> + </method> + <method name="_update" qualifiers="virtual"> + <return type="void" /> + <param index="0" name="delta" type="float" /> + <description> + Ticks video playback for [code]delta[/code] seconds. Called every frame as long as [method _is_paused] and [method _is_playing] return true. + </description> + </method> + <method name="mix_audio"> + <return type="int" /> + <param index="0" name="num_frames" type="int" /> + <param index="1" name="buffer" type="PackedFloat32Array" default="PackedFloat32Array()" /> + <param index="2" name="offset" type="int" default="0" /> + <description> + Render [code]num_frames[/code] audio frames (of [method _get_channels] floats each) from [code]buffer[/code], starting from index [code]offset[/code] in the array. Returns the number of audio frames rendered, or -1 on error. + </description> + </method> + </methods> +</class> diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp index e9eb11e2e3..c454da8e23 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.cpp +++ b/drivers/coreaudio/audio_driver_coreaudio.cpp @@ -44,10 +44,10 @@ OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, void *inClientData) { AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); - // If our selected device is the Default call set_device to update the + // If our selected input device is the Default, call set_input_device to update the // kAudioOutputUnitProperty_CurrentDevice property - if (driver->capture_device_name == "Default") { - driver->capture_set_device("Default"); + if (driver->input_device_name == "Default") { + driver->set_input_device("Default"); } return noErr; @@ -58,10 +58,10 @@ OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID void *inClientData) { AudioDriverCoreAudio *driver = static_cast<AudioDriverCoreAudio *>(inClientData); - // If our selected device is the Default call set_device to update the + // If our selected output device is the Default call set_output_device to update the // kAudioOutputUnitProperty_CurrentDevice property - if (driver->device_name == "Default") { - driver->set_device("Default"); + if (driver->output_device_name == "Default") { + driver->set_output_device("Default"); } return noErr; @@ -495,7 +495,7 @@ Error AudioDriverCoreAudio::capture_stop() { #ifdef MACOS_ENABLED -PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { +PackedStringArray AudioDriverCoreAudio::_get_device_list(bool input) { PackedStringArray list; list.push_back("Default"); @@ -514,7 +514,7 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount; i++) { - prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); @@ -555,10 +555,10 @@ PackedStringArray AudioDriverCoreAudio::_get_device_list(bool capture) { return list; } -void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { +void AudioDriverCoreAudio::_set_device(const String &output_device, bool input) { AudioDeviceID deviceId; bool found = false; - if (device != "Default") { + if (output_device != "Default") { AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; @@ -573,7 +573,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { UInt32 deviceCount = size / sizeof(AudioDeviceID); for (UInt32 i = 0; i < deviceCount && !found; i++) { - prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + prop.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; prop.mSelector = kAudioDevicePropertyStreamConfiguration; AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); @@ -602,7 +602,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { ERR_FAIL_NULL_MSG(buffer, "Out of memory."); if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { String name = String::utf8(buffer) + " (" + itos(audioDevices[i]) + ")"; - if (name == device) { + if (name == output_device) { deviceId = audioDevices[i]; found = true; } @@ -618,7 +618,7 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { if (!found) { // If we haven't found the desired device get the system default one UInt32 size = sizeof(AudioDeviceID); - UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + UInt32 elem = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId); @@ -628,10 +628,10 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } if (found) { - OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); + OSStatus result = AudioUnitSetProperty(input ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); ERR_FAIL_COND(result != noErr); - if (capture) { + if (input) { // Reset audio input to keep synchronization. input_position = 0; input_size = 0; @@ -639,34 +639,34 @@ void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { } } -PackedStringArray AudioDriverCoreAudio::get_device_list() { +PackedStringArray AudioDriverCoreAudio::get_output_device_list() { return _get_device_list(); } -String AudioDriverCoreAudio::get_device() { - return device_name; +String AudioDriverCoreAudio::get_output_device() { + return output_device_name; } -void AudioDriverCoreAudio::set_device(String device) { - device_name = device; +void AudioDriverCoreAudio::set_output_device(String output_device) { + output_device_name = output_device; if (active) { - _set_device(device_name); + _set_device(output_device_name); } } -void AudioDriverCoreAudio::capture_set_device(const String &p_name) { - capture_device_name = p_name; +void AudioDriverCoreAudio::set_input_device(const String &p_name) { + input_device_name = p_name; if (active) { - _set_device(capture_device_name, true); + _set_device(input_device_name, true); } } -PackedStringArray AudioDriverCoreAudio::capture_get_device_list() { +PackedStringArray AudioDriverCoreAudio::get_input_device_list() { return _get_device_list(true); } -String AudioDriverCoreAudio::capture_get_device() { - return capture_device_name; +String AudioDriverCoreAudio::get_input_device() { + return input_device_name; } #endif diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 675265757b..2b192e630e 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -47,8 +47,8 @@ class AudioDriverCoreAudio : public AudioDriver { bool active = false; Mutex mutex; - String device_name = "Default"; - String capture_device_name = "Default"; + String output_device_name = "Default"; + String input_device_name = "Default"; int mix_rate = 0; unsigned int channels = 2; @@ -60,7 +60,7 @@ class AudioDriverCoreAudio : public AudioDriver { #ifdef MACOS_ENABLED PackedStringArray _get_device_list(bool capture = false); - void _set_device(const String &device, bool capture = false); + void _set_device(const String &output_device, bool capture = false); static OSStatus input_device_address_cb(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, @@ -107,13 +107,13 @@ public: void stop(); #ifdef MACOS_ENABLED - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); #endif AudioDriverCoreAudio(); diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index c65c396591..7f381b3f3e 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1499,6 +1499,9 @@ void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { if (cl->texture == p_texture) { return; } + + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (cl->texture.is_valid()) { texture_storage->texture_remove_from_texture_atlas(cl->texture); } diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index e14c3c7f7a..f96247a473 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -106,15 +106,15 @@ void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_inf ERR_FAIL_COND_MSG(!i, "PulseAudio server info is null."); AudioDriverPulseAudio *ad = static_cast<AudioDriverPulseAudio *>(userdata); - ad->capture_default_device = i->default_source_name; - ad->default_device = i->default_sink_name; + ad->default_input_device = i->default_source_name; + ad->default_output_device = i->default_sink_name; ad->pa_status++; } -Error AudioDriverPulseAudio::detect_channels(bool capture) { - pa_channel_map_init_stereo(capture ? &pa_rec_map : &pa_map); +Error AudioDriverPulseAudio::detect_channels(bool input) { + pa_channel_map_init_stereo(input ? &pa_rec_map : &pa_map); - String device = capture ? capture_device_name : device_name; + String device = input ? input_device_name : output_device_name; if (device == "Default") { // Get the default output device name pa_status = 0; @@ -136,7 +136,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { char dev[1024]; if (device == "Default") { - strcpy(dev, capture ? capture_default_device.utf8().get_data() : default_device.utf8().get_data()); + strcpy(dev, input ? default_input_device.utf8().get_data() : default_output_device.utf8().get_data()); } else { strcpy(dev, device.utf8().get_data()); } @@ -145,7 +145,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { // Now using the device name get the amount of channels pa_status = 0; pa_operation *pa_op; - if (capture) { + if (input) { pa_op = pa_context_get_source_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_source_info_cb, (void *)this); } else { pa_op = pa_context_get_sink_info_by_name(pa_ctx, dev, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this); @@ -165,7 +165,7 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { return FAILED; } } else { - if (capture) { + if (input) { ERR_PRINT("pa_context_get_source_info_by_name error"); } else { ERR_PRINT("pa_context_get_sink_info_by_name error"); @@ -175,13 +175,13 @@ Error AudioDriverPulseAudio::detect_channels(bool capture) { return OK; } -Error AudioDriverPulseAudio::init_device() { - // If there is a specified device check that it is really present - if (device_name != "Default") { - PackedStringArray list = get_device_list(); - if (list.find(device_name) == -1) { - device_name = "Default"; - new_device = "Default"; +Error AudioDriverPulseAudio::init_output_device() { + // If there is a specified output device, check that it is really present + if (output_device_name != "Default") { + PackedStringArray list = get_output_device_list(); + if (list.find(output_device_name) == -1) { + output_device_name = "Default"; + new_output_device = "Default"; } } @@ -192,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() { Error err = detect_channels(); if (err != OK) { // This most likely means there are no sinks. - ERR_PRINT("PulseAudio: init device failed to detect number of output channels"); + ERR_PRINT("PulseAudio: init_output_device failed to detect number of output channels"); return err; } @@ -256,7 +256,7 @@ Error AudioDriverPulseAudio::init_device() { attr.maxlength = (uint32_t)-1; attr.minreq = (uint32_t)-1; - const char *dev = device_name == "Default" ? nullptr : device_name.utf8().get_data(); + const char *dev = output_device_name == "Default" ? nullptr : output_device_name.utf8().get_data(); pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, nullptr, nullptr); ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN); @@ -346,7 +346,7 @@ Error AudioDriverPulseAudio::init() { return ERR_CANT_OPEN; } - init_device(); + init_output_device(); thread.start(AudioDriverPulseAudio::thread_func, this); return OK; @@ -448,18 +448,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // User selected a new device, finish the current one so we'll init the new device - if (ad->device_name != ad->new_device) { - ad->device_name = ad->new_device; - ad->finish_device(); + // User selected a new output device, finish the current one so we'll init the new output device + if (ad->output_device_name != ad->new_output_device) { + ad->output_device_name = ad->new_output_device; + ad->finish_output_device(); - Error err = ad->init_device(); + Error err = ad->init_output_device(); if (err != OK) { - ERR_PRINT("PulseAudio: init_device error"); - ad->device_name = "Default"; - ad->new_device = "Default"; + ERR_PRINT("PulseAudio: init_output_device error"); + ad->output_device_name = "Default"; + ad->new_output_device = "Default"; - err = ad->init_device(); + err = ad->init_output_device(); if (err != OK) { ad->active.clear(); ad->exit_thread.set(); @@ -471,11 +471,11 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { write_ofs = 0; } - // If we're using the default device check that the current device is still the default - if (ad->device_name == "Default") { + // If we're using the default output device, check that the current output device is still the default + if (ad->output_device_name == "Default") { uint64_t msec = OS::get_singleton()->get_ticks_msec(); if (msec > (default_device_msec + 1000)) { - String old_default_device = ad->default_device; + String old_default_device = ad->default_output_device; default_device_msec = msec; @@ -494,12 +494,12 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { ERR_PRINT("pa_context_get_server_info error: " + String(pa_strerror(pa_context_errno(ad->pa_ctx)))); } - if (old_default_device != ad->default_device) { - ad->finish_device(); + if (old_default_device != ad->default_output_device) { + ad->finish_output_device(); - Error err = ad->init_device(); + Error err = ad->init_output_device(); if (err != OK) { - ERR_PRINT("PulseAudio: init_device error"); + ERR_PRINT("PulseAudio: init_output_device error"); ad->active.clear(); ad->exit_thread.set(); break; @@ -541,18 +541,18 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { } } - // User selected a new device, finish the current one so we'll init the new device - if (ad->capture_device_name != ad->capture_new_device) { - ad->capture_device_name = ad->capture_new_device; - ad->capture_finish_device(); + // User selected a new input device, finish the current one so we'll init the new input device + if (ad->input_device_name != ad->new_input_device) { + ad->input_device_name = ad->new_input_device; + ad->finish_input_device(); - Error err = ad->capture_init_device(); + Error err = ad->init_input_device(); if (err != OK) { - ERR_PRINT("PulseAudio: capture_init_device error"); - ad->capture_device_name = "Default"; - ad->capture_new_device = "Default"; + ERR_PRINT("PulseAudio: init_input_device error"); + ad->input_device_name = "Default"; + ad->new_input_device = "Default"; - err = ad->capture_init_device(); + err = ad->init_input_device(); if (err != OK) { ad->active.clear(); ad->exit_thread.set(); @@ -596,7 +596,7 @@ void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, ad->pa_status++; } -PackedStringArray AudioDriverPulseAudio::get_device_list() { +PackedStringArray AudioDriverPulseAudio::get_output_device_list() { pa_devices.clear(); pa_devices.push_back("Default"); @@ -606,7 +606,7 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() { lock(); - // Get the device list + // Get the output device list pa_status = 0; pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this); if (pa_op) { @@ -627,13 +627,13 @@ PackedStringArray AudioDriverPulseAudio::get_device_list() { return pa_devices; } -String AudioDriverPulseAudio::get_device() { - return device_name; +String AudioDriverPulseAudio::get_output_device() { + return output_device_name; } -void AudioDriverPulseAudio::set_device(String device) { +void AudioDriverPulseAudio::set_output_device(String output_device) { lock(); - new_device = device; + new_output_device = output_device; unlock(); } @@ -645,7 +645,7 @@ void AudioDriverPulseAudio::unlock() { mutex.unlock(); } -void AudioDriverPulseAudio::finish_device() { +void AudioDriverPulseAudio::finish_output_device() { if (pa_str) { pa_stream_disconnect(pa_str); pa_stream_unref(pa_str); @@ -661,7 +661,7 @@ void AudioDriverPulseAudio::finish() { exit_thread.set(); thread.wait_to_finish(); - finish_device(); + finish_output_device(); if (pa_ctx) { pa_context_disconnect(pa_ctx); @@ -675,13 +675,13 @@ void AudioDriverPulseAudio::finish() { } } -Error AudioDriverPulseAudio::capture_init_device() { - // If there is a specified device check that it is really present - if (capture_device_name != "Default") { - PackedStringArray list = capture_get_device_list(); - if (list.find(capture_device_name) == -1) { - capture_device_name = "Default"; - capture_new_device = "Default"; +Error AudioDriverPulseAudio::init_input_device() { + // If there is a specified input device, check that it is really present + if (input_device_name != "Default") { + PackedStringArray list = get_input_device_list(); + if (list.find(input_device_name) == -1) { + input_device_name = "Default"; + new_input_device = "Default"; } } @@ -718,7 +718,7 @@ Error AudioDriverPulseAudio::capture_init_device() { ERR_FAIL_V(ERR_CANT_OPEN); } - const char *dev = capture_device_name == "Default" ? nullptr : capture_device_name.utf8().get_data(); + const char *dev = input_device_name == "Default" ? nullptr : input_device_name.utf8().get_data(); pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE); int error_code = pa_stream_connect_record(pa_rec_str, dev, &attr, flags); if (error_code < 0) { @@ -734,7 +734,7 @@ Error AudioDriverPulseAudio::capture_init_device() { return OK; } -void AudioDriverPulseAudio::capture_finish_device() { +void AudioDriverPulseAudio::finish_input_device() { if (pa_rec_str) { int ret = pa_stream_disconnect(pa_rec_str); if (ret != 0) { @@ -745,25 +745,25 @@ void AudioDriverPulseAudio::capture_finish_device() { } } -Error AudioDriverPulseAudio::capture_start() { +Error AudioDriverPulseAudio::input_start() { lock(); - Error err = capture_init_device(); + Error err = init_input_device(); unlock(); return err; } -Error AudioDriverPulseAudio::capture_stop() { +Error AudioDriverPulseAudio::input_stop() { lock(); - capture_finish_device(); + finish_input_device(); unlock(); return OK; } -void AudioDriverPulseAudio::capture_set_device(const String &p_name) { +void AudioDriverPulseAudio::set_input_device(const String &p_name) { lock(); - capture_new_device = p_name; + new_input_device = p_name; unlock(); } @@ -782,7 +782,7 @@ void AudioDriverPulseAudio::pa_sourcelist_cb(pa_context *c, const pa_source_info ad->pa_status++; } -PackedStringArray AudioDriverPulseAudio::capture_get_device_list() { +PackedStringArray AudioDriverPulseAudio::get_input_device_list() { pa_rec_devices.clear(); pa_rec_devices.push_back("Default"); @@ -813,9 +813,9 @@ PackedStringArray AudioDriverPulseAudio::capture_get_device_list() { return pa_rec_devices; } -String AudioDriverPulseAudio::capture_get_device() { +String AudioDriverPulseAudio::get_input_device() { lock(); - String name = capture_device_name; + String name = input_device_name; unlock(); return name; diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 16398e67eb..68df03cb60 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -51,13 +51,13 @@ class AudioDriverPulseAudio : public AudioDriver { pa_channel_map pa_map = {}; pa_channel_map pa_rec_map = {}; - String device_name = "Default"; - String new_device = "Default"; - String default_device; + String output_device_name = "Default"; + String new_output_device = "Default"; + String default_output_device; - String capture_device_name; - String capture_new_device; - String capture_default_device; + String input_device_name; + String new_input_device; + String default_input_device; Vector<int32_t> samples_in; Vector<int16_t> samples_out; @@ -83,11 +83,11 @@ class AudioDriverPulseAudio : public AudioDriver { static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); - Error init_device(); - void finish_device(); + Error init_output_device(); + void finish_output_device(); - Error capture_init_device(); - void capture_finish_device(); + Error init_input_device(); + void finish_input_device(); Error detect_channels(bool capture = false); @@ -103,13 +103,13 @@ public: virtual int get_mix_rate() const; virtual SpeakerMode get_speaker_mode() const; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); virtual void lock(); virtual void unlock(); @@ -117,8 +117,8 @@ public: virtual float get_latency(); - virtual Error capture_start(); - virtual Error capture_stop(); + virtual Error input_start(); + virtual Error input_stop(); AudioDriverPulseAudio(); ~AudioDriverPulseAudio() {} diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index b2745b1e86..42a2b85200 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -118,8 +118,8 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); #define CAPTURE_BUFFER_CHANNELS 2 -static bool default_render_device_changed = false; -static bool default_capture_device_changed = false; +static bool default_output_device_changed = false; +static bool default_input_device_changed = false; // Silence warning due to a COM API weirdness (GH-35194). #if defined(__GNUC__) && !defined(__clang__) @@ -181,9 +181,9 @@ public: HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { if (role == eConsole) { if (flow == eRender) { - default_render_device_changed = true; + default_output_device_changed = true; } else if (flow == eCapture) { - default_capture_device_changed = true; + default_input_device_changed = true; } } @@ -201,10 +201,10 @@ public: static CMMNotificationClient notif_client; -Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3) { +Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; - IMMDevice *device = nullptr; + IMMDevice *output_device = nullptr; CoInitialize(nullptr); @@ -212,11 +212,11 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); if (p_device->device_name == "Default") { - hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); + hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device); } else { IMMDeviceCollection *devices = nullptr; - hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); LPWSTR strId = nullptr; @@ -255,20 +255,20 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } if (found) { - hr = enumerator->GetDevice(strId, &device); + hr = enumerator->GetDevice(strId, &output_device); } if (strId) { CoTaskMemFree(strId); } - if (device == nullptr) { - hr = enumerator->GetDefaultAudioEndpoint(p_capture ? eCapture : eRender, eConsole, &device); + if (output_device == nullptr) { + hr = enumerator->GetDefaultAudioEndpoint(p_input ? eCapture : eRender, eConsole, &output_device); } } if (p_reinit) { - // In case we're trying to re-initialize the device prevent throwing this error on the console, + // In case we're trying to re-initialize the device, prevent throwing this error on the console, // otherwise if there is currently no device available this will spam the console. if (hr != S_OK) { return ERR_CANT_OPEN; @@ -284,28 +284,28 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c ERR_PRINT("WASAPI: RegisterEndpointNotificationCallback error"); } - using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + using_audio_client_3 = !p_input; // IID_IAudioClient3 is only used for adjustable output latency (not input) if (p_no_audio_client_3) { using_audio_client_3 = false; } if (using_audio_client_3) { - hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + hr = output_device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { // IID_IAudioClient3 will never activate on OS versions before Windows 10. // Older Windows versions should fall back gracefully. using_audio_client_3 = false; - print_verbose("WASAPI: Couldn't activate device with IAudioClient3 interface, falling back to IAudioClient interface"); + print_verbose("WASAPI: Couldn't activate output_device with IAudioClient3 interface, falling back to IAudioClient interface"); } else { - print_verbose("WASAPI: Activated device using IAudioClient3 interface"); + print_verbose("WASAPI: Activated output_device using IAudioClient3 interface"); } } if (!using_audio_client_3) { - hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); + hr = output_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); } - SAFE_RELEASE(device) + SAFE_RELEASE(output_device) if (p_reinit) { if (hr != S_OK) { @@ -339,7 +339,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c WAVEFORMATEX *closest = nullptr; hr = p_device->audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, pwfex, &closest); if (hr == S_FALSE) { - WARN_PRINT("WASAPI: Mix format is not supported by the Device"); + WARN_PRINT("WASAPI: Mix format is not supported by the output_device"); if (closest) { print_verbose("WASAPI: closest->wFormatTag = " + itos(closest->wFormatTag)); print_verbose("WASAPI: closest->nChannels = " + itos(closest->nChannels)); @@ -385,14 +385,14 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c pwfex->nSamplesPerSec = mix_rate; pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8); } - hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); + hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_input ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr); ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + "."); UINT32 max_frames; hr = p_device->audio_client->GetBufferSize(&max_frames); ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN); // Due to WASAPI Shared Mode we have no control of the buffer size - if (!p_capture) { + if (!p_input) { buffer_frames = max_frames; int64_t latency = 0; @@ -421,8 +421,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c if (hr != S_OK) { print_verbose("WASAPI: GetSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device) - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device) + return audio_device_init(p_device, p_input, p_reinit, true); } // Period frames must be an integral multiple of fundamental_period_frames or IAudioClient3 initialization will fail, @@ -443,8 +443,8 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c if (hr != S_OK) { print_verbose("WASAPI: InitializeSharedAudioStream failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device); - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device); + return audio_device_init(p_device, p_input, p_reinit, true); } else { uint32_t output_latency_in_frames; WAVEFORMATEX *current_pwfex; @@ -455,13 +455,13 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } else { print_verbose("WASAPI: GetCurrentSharedModeEnginePeriod failed with error 0x" + String::num_uint64(hr, 16) + ", falling back to IAudioClient."); CoTaskMemFree(pwfex); - SAFE_RELEASE(device); - return audio_device_init(p_device, p_capture, p_reinit, true); + SAFE_RELEASE(output_device); + return audio_device_init(p_device, p_input, p_reinit, true); } } } - if (p_capture) { + if (p_input) { hr = p_device->audio_client->GetService(IID_IAudioCaptureClient, (void **)&p_device->capture_client); } else { hr = p_device->audio_client->GetService(IID_IAudioRenderClient, (void **)&p_device->render_client); @@ -470,12 +470,12 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c // Free memory CoTaskMemFree(pwfex); - SAFE_RELEASE(device) + SAFE_RELEASE(output_device) return OK; } -Error AudioDriverWASAPI::init_render_device(bool p_reinit) { +Error AudioDriverWASAPI::init_output_device(bool p_reinit) { Error err = audio_device_init(&audio_output, false, p_reinit); if (err != OK) { return err; @@ -507,7 +507,7 @@ Error AudioDriverWASAPI::init_render_device(bool p_reinit) { return OK; } -Error AudioDriverWASAPI::init_capture_device(bool p_reinit) { +Error AudioDriverWASAPI::init_input_device(bool p_reinit) { Error err = audio_device_init(&audio_input, true, p_reinit); if (err != OK) { return err; @@ -538,11 +538,11 @@ Error AudioDriverWASAPI::audio_device_finish(AudioDeviceWASAPI *p_device) { return OK; } -Error AudioDriverWASAPI::finish_render_device() { +Error AudioDriverWASAPI::finish_output_device() { return audio_device_finish(&audio_output); } -Error AudioDriverWASAPI::finish_capture_device() { +Error AudioDriverWASAPI::finish_input_device() { return audio_device_finish(&audio_input); } @@ -551,9 +551,9 @@ Error AudioDriverWASAPI::init() { target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); - Error err = init_render_device(); + Error err = init_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_render_device error"); + ERR_PRINT("WASAPI: init_output_device error"); } exit_thread.clear(); @@ -575,7 +575,7 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const { return get_speaker_mode_by_total_channels(channels); } -PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { +PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_input) { PackedStringArray list; IMMDeviceCollection *devices = nullptr; IMMDeviceEnumerator *enumerator = nullptr; @@ -587,7 +587,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator); ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); - hr = enumerator->EnumAudioEndpoints(p_capture ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); + hr = enumerator->EnumAudioEndpoints(p_input ? eCapture : eRender, DEVICE_STATE_ACTIVE, &devices); ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); UINT count = 0; @@ -595,13 +595,13 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { ERR_FAIL_COND_V(hr != S_OK, PackedStringArray()); for (ULONG i = 0; i < count; i++) { - IMMDevice *device = nullptr; + IMMDevice *output_device = nullptr; - hr = devices->Item(i, &device); + hr = devices->Item(i, &output_device); ERR_BREAK(hr != S_OK); IPropertyStore *props = nullptr; - hr = device->OpenPropertyStore(STGM_READ, &props); + hr = output_device->OpenPropertyStore(STGM_READ, &props); ERR_BREAK(hr != S_OK); PROPVARIANT propvar; @@ -614,7 +614,7 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { PropVariantClear(&propvar); props->Release(); - device->Release(); + output_device->Release(); } devices->Release(); @@ -622,11 +622,11 @@ PackedStringArray AudioDriverWASAPI::audio_device_get_list(bool p_capture) { return list; } -PackedStringArray AudioDriverWASAPI::get_device_list() { +PackedStringArray AudioDriverWASAPI::get_output_device_list() { return audio_device_get_list(false); } -String AudioDriverWASAPI::get_device() { +String AudioDriverWASAPI::get_output_device() { lock(); String name = audio_output.device_name; unlock(); @@ -634,9 +634,9 @@ String AudioDriverWASAPI::get_device() { return name; } -void AudioDriverWASAPI::set_device(String device) { +void AudioDriverWASAPI::set_output_device(String output_device) { lock(); - audio_output.new_device = device; + audio_output.new_device = output_device; unlock(); } @@ -769,13 +769,13 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { avail_frames -= write_frames; written_frames += write_frames; } else if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - // Device is not valid anymore, reopen it + // output_device is not valid anymore, reopen it - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } else { - // We reopened the device and samples_in may have resized, so invalidate the current avail_frames + // We reopened the output device and samples_in may have resized, so invalidate the current avail_frames avail_frames = 0; } } else { @@ -790,37 +790,37 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } if (invalidated) { - // Device is not valid anymore - WARN_PRINT("WASAPI: Current device invalidated, closing device"); + // output_device is not valid anymore + WARN_PRINT("WASAPI: Current output_device invalidated, closing output_device"); - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } } } - // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->audio_output.device_name == "Default" && default_render_device_changed) { - Error err = ad->finish_render_device(); + // If we're using the Default output device and it changed finish it so we'll re-init the output device + if (ad->audio_output.device_name == "Default" && default_output_device_changed) { + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } - default_render_device_changed = false; + default_output_device_changed = false; } - // User selected a new device, finish the current one so we'll init the new device + // User selected a new output device, finish the current one so we'll init the new output device if (ad->audio_output.device_name != ad->audio_output.new_device) { ad->audio_output.device_name = ad->audio_output.new_device; - Error err = ad->finish_render_device(); + Error err = ad->finish_output_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_render_device error"); + ERR_PRINT("WASAPI: finish_output_device error"); } } if (!ad->audio_output.audio_client) { - Error err = ad->init_render_device(true); + Error err = ad->init_output_device(true); if (err == OK) { ad->start(); } @@ -873,29 +873,29 @@ void AudioDriverWASAPI::thread_func(void *p_udata) { } } - // If we're using the Default device and it changed finish it so we'll re-init the device - if (ad->audio_input.device_name == "Default" && default_capture_device_changed) { - Error err = ad->finish_capture_device(); + // If we're using the Default output device and it changed finish it so we'll re-init the output device + if (ad->audio_input.device_name == "Default" && default_input_device_changed) { + Error err = ad->finish_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_capture_device error"); + ERR_PRINT("WASAPI: finish_input_device error"); } - default_capture_device_changed = false; + default_input_device_changed = false; } - // User selected a new device, finish the current one so we'll init the new device + // User selected a new input device, finish the current one so we'll init the new input device if (ad->audio_input.device_name != ad->audio_input.new_device) { ad->audio_input.device_name = ad->audio_input.new_device; - Error err = ad->finish_capture_device(); + Error err = ad->finish_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: finish_capture_device error"); + ERR_PRINT("WASAPI: finish_input_device error"); } } if (!ad->audio_input.audio_client) { - Error err = ad->init_capture_device(true); + Error err = ad->init_input_device(true); if (err == OK) { - ad->capture_start(); + ad->input_start(); } } } @@ -933,14 +933,14 @@ void AudioDriverWASAPI::finish() { exit_thread.set(); thread.wait_to_finish(); - finish_capture_device(); - finish_render_device(); + finish_input_device(); + finish_output_device(); } -Error AudioDriverWASAPI::capture_start() { - Error err = init_capture_device(); +Error AudioDriverWASAPI::input_start() { + Error err = init_input_device(); if (err != OK) { - ERR_PRINT("WASAPI: init_capture_device error"); + ERR_PRINT("WASAPI: init_input_device error"); return err; } @@ -953,7 +953,7 @@ Error AudioDriverWASAPI::capture_start() { return OK; } -Error AudioDriverWASAPI::capture_stop() { +Error AudioDriverWASAPI::input_stop() { if (audio_input.active.is_set()) { audio_input.audio_client->Stop(); audio_input.active.clear(); @@ -964,17 +964,17 @@ Error AudioDriverWASAPI::capture_stop() { return FAILED; } -void AudioDriverWASAPI::capture_set_device(const String &p_name) { +void AudioDriverWASAPI::set_input_device(const String &p_name) { lock(); audio_input.new_device = p_name; unlock(); } -PackedStringArray AudioDriverWASAPI::capture_get_device_list() { +PackedStringArray AudioDriverWASAPI::get_input_device_list() { return audio_device_get_list(true); } -String AudioDriverWASAPI::capture_get_device() { +String AudioDriverWASAPI::get_input_device() { lock(); String name = audio_input.device_name; unlock(); diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index 6eea24f78a..bf18ba8c99 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -47,8 +47,8 @@ class AudioDriverWASAPI : public AudioDriver { class AudioDeviceWASAPI { public: IAudioClient *audio_client = nullptr; - IAudioRenderClient *render_client = nullptr; - IAudioCaptureClient *capture_client = nullptr; + IAudioRenderClient *render_client = nullptr; // Output + IAudioCaptureClient *capture_client = nullptr; // Input SafeFlag active; WORD format_tag = 0; @@ -56,8 +56,8 @@ class AudioDriverWASAPI : public AudioDriver { unsigned int channels = 0; unsigned int frame_size = 0; - String device_name = "Default"; - String new_device = "Default"; + String device_name = "Default"; // Output OR Input + String new_device = "Default"; // Output OR Input AudioDeviceWASAPI() {} }; @@ -83,15 +83,15 @@ class AudioDriverWASAPI : public AudioDriver { static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); - Error init_render_device(bool p_reinit = false); - Error init_capture_device(bool p_reinit = false); + Error init_output_device(bool p_reinit = false); + Error init_input_device(bool p_reinit = false); - Error finish_render_device(); - Error finish_capture_device(); + Error finish_output_device(); + Error finish_input_device(); - Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool p_reinit, bool p_no_audio_client_3 = false); + Error audio_device_init(AudioDeviceWASAPI *p_device, bool p_input, bool p_reinit, bool p_no_audio_client_3 = false); Error audio_device_finish(AudioDeviceWASAPI *p_device); - PackedStringArray audio_device_get_list(bool p_capture); + PackedStringArray audio_device_get_list(bool p_input); public: virtual const char *get_name() const { @@ -103,18 +103,18 @@ public: virtual int get_mix_rate() const; virtual float get_latency(); virtual SpeakerMode get_speaker_mode() const; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device); + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device); virtual void lock(); virtual void unlock(); virtual void finish(); - virtual Error capture_start(); - virtual Error capture_stop(); - virtual PackedStringArray capture_get_device_list(); - virtual void capture_set_device(const String &p_name); - virtual String capture_get_device(); + virtual Error input_start(); + virtual Error input_stop(); + virtual PackedStringArray get_input_device_list(); + virtual void set_input_device(const String &p_name); + virtual String get_input_device(); AudioDriverWASAPI(); }; diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 5bdef32c60..d71ef78d88 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -461,7 +461,7 @@ void DocTools::generate(bool p_basic_types) { } if (default_value_valid && default_value.get_type() != Variant::OBJECT) { - prop.default_value = default_value.get_construct_string().replace("\n", " "); + prop.default_value = DocData::get_default_value_string(default_value); } StringName setter = ClassDB::get_property_setter(name, E.name); @@ -591,7 +591,7 @@ void DocTools::generate(bool p_basic_types) { tid.name = E; tid.type = "Color"; tid.data_type = "color"; - tid.default_value = Variant(ThemeDB::get_singleton()->get_default_theme()->get_color(E, cname)).get_construct_string().replace("\n", " "); + tid.default_value = DocData::get_default_value_string(ThemeDB::get_singleton()->get_default_theme()->get_color(E, cname)); c.theme_properties.push_back(tid); } @@ -772,8 +772,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = mi.default_arguments.size() - mi.arguments.size() + j; if (darg_idx >= 0) { - Variant default_arg = mi.default_arguments[darg_idx]; - ad.default_value = default_arg.get_construct_string().replace("\n", " "); + ad.default_value = DocData::get_default_value_string(mi.default_arguments[darg_idx]); } method.arguments.push_back(ad); @@ -817,7 +816,7 @@ void DocTools::generate(bool p_basic_types) { DocData::PropertyDoc property; property.name = pi.name; property.type = Variant::get_type_name(pi.type); - property.default_value = v.get(pi.name).get_construct_string().replace("\n", " "); + property.default_value = DocData::get_default_value_string(v.get(pi.name)); c.properties.push_back(property); } @@ -948,8 +947,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = j - (mi.arguments.size() - mi.default_arguments.size()); if (darg_idx >= 0) { - Variant default_arg = mi.default_arguments[darg_idx]; - ad.default_value = default_arg.get_construct_string().replace("\n", " "); + ad.default_value = DocData::get_default_value_string(mi.default_arguments[darg_idx]); } md.arguments.push_back(ad); @@ -993,8 +991,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = j - (ai.arguments.size() - ai.default_arguments.size()); if (darg_idx >= 0) { - Variant default_arg = ai.default_arguments[darg_idx]; - ad.default_value = default_arg.get_construct_string().replace("\n", " "); + ad.default_value = DocData::get_default_value_string(ai.default_arguments[darg_idx]); } atd.arguments.push_back(ad); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index ef33b82390..644c32e8a4 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1591,6 +1591,11 @@ void EditorFileSystem::_update_script_classes() { void EditorFileSystem::_update_pending_script_classes() { if (!update_script_paths.is_empty()) { _update_script_classes(); + } else { + // In case the class cache file was removed somehow, regenerate it. + if (!FileAccess::exists(ScriptServer::get_global_class_cache_file_path())) { + ScriptServer::save_global_classes(); + } } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a1028a14c5..0405749147 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -581,6 +581,7 @@ void EditorNode::_notification(int p_what) { ResourceImporterTexture::get_singleton()->update_imports(); + bottom_panel_updating = false; } break; case NOTIFICATION_ENTER_TREE: { @@ -643,7 +644,7 @@ void EditorNode::_notification(int p_what) { } RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true); - RenderingServer::get_singleton()->viewport_set_disable_environment(get_viewport()->get_viewport_rid(), true); + RenderingServer::get_singleton()->viewport_set_environment_mode(get_viewport()->get_viewport_rid(), RenderingServer::VIEWPORT_ENVIRONMENT_DISABLED); feature_profile_manager->notify_changed(); @@ -5601,12 +5602,16 @@ void EditorNode::remove_bottom_panel_item(Control *p_item) { } void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) { + if (bottom_panel_updating) { + return; + } ERR_FAIL_INDEX(p_idx, bottom_panel_items.size()); if (bottom_panel_items[p_idx].control->is_visible() == p_enable) { return; } + bottom_panel_updating = true; if (p_enable) { for (int i = 0; i < bottom_panel_items.size(); i++) { bottom_panel_items[i].button->set_pressed(i == p_idx); diff --git a/editor/editor_node.h b/editor/editor_node.h index bb10abb589..3967f64c6b 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -460,6 +460,7 @@ private: EditorToaster *editor_toaster = nullptr; LinkButton *version_btn = nullptr; Button *bottom_panel_raise = nullptr; + bool bottom_panel_updating = false; Tree *disk_changed_list = nullptr; ConfirmationDialog *disk_changed = nullptr; diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 28c0b047d8..b96ac9dbcb 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -158,17 +158,32 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { ///////////////////// ARRAY /////////////////////////// +void EditorPropertyArray::initialize_array(Variant &p_array) { + if (array_type == Variant::ARRAY && subtype != Variant::NIL) { + Array array; + StringName subtype_class; + Ref<Script> subtype_script; + if (subtype == Variant::OBJECT && !subtype_hint_string.is_empty()) { + if (ClassDB::class_exists(subtype_hint_string)) { + subtype_class = subtype_hint_string; + } + } + array.set_typed(subtype, subtype_class, subtype_script); + p_array = array; + } else { + VariantInternal::initialize(&p_array, array_type); + } +} + void EditorPropertyArray::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { if (p_property.begins_with("indices")) { int index = p_property.get_slice("/", 1).to_int(); - Variant array = object->get_array(); + + Variant array = object->get_array().duplicate(); array.set(index, p_value); - emit_changed(get_edited_property(), array, "", true); - if (array.get_type() == Variant::ARRAY) { - array = array.call("duplicate"); // Duplicate, so undo/redo works better. - } object->set_array(array); + emit_changed(get_edited_property(), array, "", true); } } @@ -188,18 +203,12 @@ void EditorPropertyArray::_change_type_menu(int p_index) { } Variant value; - Callable::CallError ce; - Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce); - Variant array = object->get_array(); + VariantInternal::initialize(&value, Variant::Type(p_index)); + + Variant array = object->get_array().duplicate(); array.set(changing_type_index, value); emit_changed(get_edited_property(), array, "", true); - - if (array.get_type() == Variant::ARRAY) { - array = array.call("duplicate"); // Duplicate, so undo/redo works better. - } - - object->set_array(array); update_property(); } @@ -234,6 +243,8 @@ void EditorPropertyArray::update_property() { return; } + object->set_array(array); + int size = array.call("size"); int max_page = MAX(0, size - 1) / page_length; page_index = MIN(page_index, max_page); @@ -305,12 +316,6 @@ void EditorPropertyArray::update_property() { paginator->update(page_index, max_page); paginator->set_visible(max_page > 0); - if (array.get_type() == Variant::ARRAY) { - array = array.call("duplicate"); - } - - object->set_array(array); - int amount = MIN(size - offset, page_length); for (int i = 0; i < amount; i++) { bool reorder_is_from_current_page = reorder_from_index / page_length == page_index; @@ -401,7 +406,7 @@ void EditorPropertyArray::update_property() { } void EditorPropertyArray::_remove_pressed(int p_index) { - Variant array = object->get_array(); + Variant array = object->get_array().duplicate(); array.call("remove_at", p_index); emit_changed(get_edited_property(), array, "", false); @@ -469,8 +474,9 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d // Handle the case where array is not initialized yet. if (!array.is_array()) { - Callable::CallError ce; - Variant::construct(array_type, array, nullptr, 0, ce); + initialize_array(array); + } else { + array = array.duplicate(); } // Loop the file array and add to existing array. @@ -483,13 +489,7 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d } } - if (array.get_type() == Variant::ARRAY) { - array = array.call("duplicate"); - } - emit_changed(get_edited_property(), array, "", false); - object->set_array(array); - update_property(); } } @@ -536,10 +536,8 @@ void EditorPropertyArray::_notification(int p_what) { void EditorPropertyArray::_edit_pressed() { Variant array = get_edited_object()->get(get_edited_property()); - if (!array.is_array()) { - Callable::CallError ce; - Variant::construct(array_type, array, nullptr, 0, ce); - + if (!array.is_array() && edit->is_pressed()) { + initialize_array(array); get_edited_object()->set(get_edited_property(), array); } @@ -560,37 +558,10 @@ void EditorPropertyArray::_length_changed(double p_page) { return; } - Variant array = object->get_array(); - int previous_size = array.call("size"); - + Variant array = object->get_array().duplicate(); array.call("resize", int(p_page)); - if (array.get_type() == Variant::ARRAY) { - if (subtype != Variant::NIL) { - int size = array.call("size"); - for (int i = previous_size; i < size; i++) { - if (array.get(i).get_type() == Variant::NIL) { - Callable::CallError ce; - Variant r; - Variant::construct(subtype, r, nullptr, 0, ce); - array.set(i, r); - } - } - } - array = array.call("duplicate"); // Duplicate, so undo/redo works better. - } else { - int size = array.call("size"); - // Pool*Array don't initialize their elements, have to do it manually. - for (int i = previous_size; i < size; i++) { - Callable::CallError ce; - Variant r; - Variant::construct(array.get(i).get_type(), r, nullptr, 0, ce); - array.set(i, r); - } - } - emit_changed(get_edited_property(), array, "", false); - object->set_array(array); update_property(); } @@ -677,14 +648,13 @@ void EditorPropertyArray::_reorder_button_up() { if (reorder_from_index != reorder_to_index) { // Move the element. - Variant array = object->get_array(); + Variant array = object->get_array().duplicate(); Variant value_to_move = array.get(reorder_from_index); array.call("remove_at", reorder_from_index); array.call("insert", reorder_to_index, value_to_move); emit_changed(get_edited_property(), array, "", false); - object->set_array(array); update_property(); } @@ -742,14 +712,13 @@ void EditorPropertyDictionary::_property_changed(const String &p_property, Varia object->set_new_item_value(p_value); } else if (p_property.begins_with("indices")) { int index = p_property.get_slice("/", 1).to_int(); - Dictionary dict = object->get_dict(); + + Dictionary dict = object->get_dict().duplicate(); Variant key = dict.get_key_at_index(index); dict[key] = p_value; - emit_changed(get_edited_property(), dict, "", true); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. object->set_dict(dict); + emit_changed(get_edited_property(), dict, "", true); } } @@ -769,24 +738,19 @@ void EditorPropertyDictionary::_add_key_value() { return; } - Dictionary dict = object->get_dict(); - + Dictionary dict = object->get_dict().duplicate(); dict[object->get_new_item_key()] = object->get_new_item_value(); object->set_new_item_key(Variant()); object->set_new_item_value(Variant()); emit_changed(get_edited_property(), dict, "", false); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. - object->set_dict(dict); update_property(); } void EditorPropertyDictionary::_change_type_menu(int p_index) { if (changing_type_index < 0) { Variant value; - Callable::CallError ce; - Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce); + VariantInternal::initialize(&value, Variant::Type(p_index)); if (changing_type_index == -1) { object->set_new_item_key(value); } else { @@ -796,12 +760,10 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) { return; } - Dictionary dict = object->get_dict(); - + Dictionary dict = object->get_dict().duplicate(); if (p_index < Variant::VARIANT_MAX) { Variant value; - Callable::CallError ce; - Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce); + VariantInternal::initialize(&value, Variant::Type(p_index)); Variant key = dict.get_key_at_index(changing_type_index); dict[key] = value; } else { @@ -810,9 +772,6 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) { } emit_changed(get_edited_property(), dict, "", false); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. - object->set_dict(dict); update_property(); } @@ -836,6 +795,7 @@ void EditorPropertyDictionary::update_property() { } Dictionary dict = updated_val; + object->set_dict(updated_val); edit->set_text(vformat(TTR("Dictionary (size %d)"), dict.size())); @@ -883,9 +843,6 @@ void EditorPropertyDictionary::update_property() { int amount = MIN(size - offset, page_length); int total_amount = page_index == max_page ? amount + 2 : amount; // For the "Add Key/Value Pair" box on last page. - dict = dict.duplicate(); - - object->set_dict(dict); VBoxContainer *add_vbox = nullptr; double default_float_step = EDITOR_GET("interface/inspector/default_float_step"); @@ -1225,9 +1182,8 @@ void EditorPropertyDictionary::_notification(int p_what) { void EditorPropertyDictionary::_edit_pressed() { Variant prop_val = get_edited_object()->get(get_edited_property()); - if (prop_val.get_type() == Variant::NIL) { - Callable::CallError ce; - Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce); + if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) { + VariantInternal::initialize(&prop_val, Variant::DICTIONARY); get_edited_object()->set(get_edited_property(), prop_val); } @@ -1272,14 +1228,13 @@ EditorPropertyDictionary::EditorPropertyDictionary() { void EditorPropertyLocalizableString::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { if (p_property.begins_with("indices")) { int index = p_property.get_slice("/", 1).to_int(); - Dictionary dict = object->get_dict(); + + Dictionary dict = object->get_dict().duplicate(); Variant key = dict.get_key_at_index(index); dict[key] = p_value; - emit_changed(get_edited_property(), dict, "", true); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. object->set_dict(dict); + emit_changed(get_edited_property(), dict, "", true); } } @@ -1288,29 +1243,22 @@ void EditorPropertyLocalizableString::_add_locale_popup() { } void EditorPropertyLocalizableString::_add_locale(const String &p_locale) { - Dictionary dict = object->get_dict(); - + Dictionary dict = object->get_dict().duplicate(); object->set_new_item_key(p_locale); object->set_new_item_value(String()); dict[object->get_new_item_key()] = object->get_new_item_value(); emit_changed(get_edited_property(), dict, "", false); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. - object->set_dict(dict); update_property(); } void EditorPropertyLocalizableString::_remove_item(Object *p_button, int p_index) { - Dictionary dict = object->get_dict(); + Dictionary dict = object->get_dict().duplicate(); Variant key = dict.get_key_at_index(p_index); dict.erase(key); emit_changed(get_edited_property(), dict, "", false); - - dict = dict.duplicate(); // Duplicate, so undo/redo works better. - object->set_dict(dict); update_property(); } @@ -1330,6 +1278,7 @@ void EditorPropertyLocalizableString::update_property() { } Dictionary dict = updated_val; + object->set_dict(dict); edit->set_text(vformat(TTR("Localizable String (size %d)"), dict.size())); @@ -1376,10 +1325,6 @@ void EditorPropertyLocalizableString::update_property() { int amount = MIN(size - offset, page_length); - dict = dict.duplicate(); - - object->set_dict(dict); - for (int i = 0; i < amount; i++) { String prop_name; Variant key; @@ -1451,9 +1396,8 @@ void EditorPropertyLocalizableString::_notification(int p_what) { void EditorPropertyLocalizableString::_edit_pressed() { Variant prop_val = get_edited_object()->get(get_edited_property()); - if (prop_val.get_type() == Variant::NIL) { - Callable::CallError ce; - Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce); + if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) { + VariantInternal::initialize(&prop_val, Variant::DICTIONARY); get_edited_object()->set(get_edited_property(), prop_val); } diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 73a16e3687..3b880c60a8 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -102,6 +102,8 @@ class EditorPropertyArray : public EditorProperty { HBoxContainer *reorder_selected_element_hbox = nullptr; Button *reorder_selected_button = nullptr; + void initialize_array(Variant &p_array); + void _page_changed(int p_page); void _reorder_button_gui_input(const Ref<InputEvent> &p_event); diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index fe67813d65..a46484bb0e 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -814,7 +814,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & HashSet<String> paths; Vector<String> path_remaps; - paths.insert(ProjectSettings::get_singleton()->get_project_data_path().path_join("global_script_class_cache.cfg")); + paths.insert(ProjectSettings::get_singleton()->get_global_class_list_path()); if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_ALL_RESOURCES) { //find stuff _export_find_resources(EditorFileSystem::get_singleton()->get_filesystem(), paths); diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index aa5f9ff29a..6c6c89bcc0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -1085,10 +1085,10 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co return p_node; } -Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) { +Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale) { // children first for (int i = 0; i < p_node->get_child_count(); i++) { - Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps); + Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_occluder_arrays, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps, p_applied_root_scale); if (!r) { i--; //was erased } @@ -1231,7 +1231,8 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< } else { shapes = get_collision_shapes( m->get_mesh(), - node_settings); + node_settings, + p_applied_root_scale); } if (shapes.size()) { @@ -1242,6 +1243,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< p_node->add_child(col, true); col->set_owner(p_node->get_owner()); col->set_transform(get_collision_shapes_transform(node_settings)); + col->set_position(p_applied_root_scale * col->get_position()); base = col; } break; case MESH_PHYSICS_RIGID_BODY_AND_MESH: { @@ -1249,6 +1251,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< rigid_body->set_name(p_node->get_name()); p_node->replace_by(rigid_body); rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + rigid_body->set_position(p_applied_root_scale * rigid_body->get_position()); p_node = rigid_body; mi->set_transform(Transform3D()); rigid_body->add_child(mi, true); @@ -1258,6 +1261,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< case MESH_PHYSICS_STATIC_COLLIDER_ONLY: { StaticBody3D *col = memnew(StaticBody3D); col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + col->set_position(p_applied_root_scale * col->get_position()); col->set_name(p_node->get_name()); p_node->replace_by(col); memdelete(p_node); @@ -1267,6 +1271,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< case MESH_PHYSICS_AREA_ONLY: { Area3D *area = memnew(Area3D); area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); + area->set_position(p_applied_root_scale * area->get_position()); area->set_name(p_node->get_name()); p_node->replace_by(area); memdelete(p_node); @@ -2398,7 +2403,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p fps = (float)p_options[SNAME("animation/fps")]; } _pre_fix_animations(scene, scene, node_data, animation_data, fps); - _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps); + _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps, apply_root ? root_scale : 1.0); _post_fix_animations(scene, scene, node_data, animation_data, fps); String root_type = p_options["nodes/root_type"]; diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 2d08d4df50..aa057d3404 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -279,7 +279,7 @@ public: Node *_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames); Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); - Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps); + Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale); Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps); Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks); @@ -298,7 +298,7 @@ public: ResourceImporterScene(bool p_animation_import = false); template <class M> - static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options); + static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale); template <class M> static Transform3D get_collision_shapes_transform(const M &p_options); @@ -314,7 +314,7 @@ public: }; template <class M> -Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options) { +Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale) { ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX; if (p_options.has(SNAME("physics/shape_type"))) { generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int(); @@ -409,7 +409,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<BoxShape3D> box; box.instantiate(); if (p_options.has(SNAME("primitive/size"))) { - box->set_size(p_options[SNAME("primitive/size")]); + box->set_size(p_options[SNAME("primitive/size")].operator Vector3() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -420,7 +420,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<SphereShape3D> sphere; sphere.instantiate(); if (p_options.has(SNAME("primitive/radius"))) { - sphere->set_radius(p_options[SNAME("primitive/radius")]); + sphere->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -430,10 +430,10 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<CylinderShape3D> cylinder; cylinder.instantiate(); if (p_options.has(SNAME("primitive/height"))) { - cylinder->set_height(p_options[SNAME("primitive/height")]); + cylinder->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { - cylinder->set_radius(p_options[SNAME("primitive/radius")]); + cylinder->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; @@ -443,10 +443,10 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> Ref<CapsuleShape3D> capsule; capsule.instantiate(); if (p_options.has(SNAME("primitive/height"))) { - capsule->set_height(p_options[SNAME("primitive/height")]); + capsule->set_height(p_options[SNAME("primitive/height")].operator float() * p_applied_root_scale); } if (p_options.has(SNAME("primitive/radius"))) { - capsule->set_radius(p_options[SNAME("primitive/radius")]); + capsule->set_radius(p_options[SNAME("primitive/radius")].operator float() * p_applied_root_scale); } Vector<Ref<Shape3D>> shapes; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 044f7475c2..8d26feebf4 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -441,7 +441,7 @@ void SceneImportSettings::_update_view_gizmos() { // This collider_view doesn't have a mesh so we need to generate a new one. // Generate the mesh collider. - Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings); + Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings, 1.0); const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings); Ref<ArrayMesh> collider_view_mesh; diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 33aebe5883..df94815105 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -38,6 +38,7 @@ #include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/check_box.h" +#include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { @@ -335,6 +336,7 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { min_value->set_value(blend_space->get_min_space()); sync->set_pressed(blend_space->is_using_sync()); + interpolation->select(blend_space->get_blend_mode()); label_value->set_text(blend_space->get_value_label()); @@ -361,6 +363,8 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) { undo_redo->add_undo_method(blend_space.ptr(), "set_snap", blend_space->get_snap()); undo_redo->add_do_method(blend_space.ptr(), "set_use_sync", sync->is_pressed()); undo_redo->add_undo_method(blend_space.ptr(), "set_use_sync", blend_space->is_using_sync()); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_mode", interpolation->get_selected()); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_mode", blend_space->get_blend_mode()); undo_redo->add_do_method(this, "_update_space"); undo_redo->add_undo_method(this, "_update_space"); undo_redo->commit_action(); @@ -579,6 +583,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); snap->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); open_editor->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + interpolation->clear(); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), "", 0); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), "", 1); + interpolation->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), "", 2); } break; case NOTIFICATION_PROCESS: { @@ -639,6 +647,7 @@ void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { min_value->set_editable(!read_only); max_value->set_editable(!read_only); sync->set_disabled(read_only); + interpolation->set_disabled(read_only); } AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr; @@ -707,6 +716,13 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { top_hb->add_child(sync); sync->connect("toggled", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); + top_hb->add_child(memnew(VSeparator)); + + top_hb->add_child(memnew(Label(TTR("Blend:")))); + interpolation = memnew(OptionButton); + top_hb->add_child(interpolation); + interpolation->connect("item_selected", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_config_changed)); + edit_hb = memnew(HBoxContainer); top_hb->add_child(edit_hb); edit_hb->add_child(memnew(VSeparator)); diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index e71a4bd1b9..90104fc310 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -41,6 +41,7 @@ #include "scene/gui/tree.h" class CheckBox; +class OptionButton; class PanelContainer; class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { @@ -66,6 +67,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { SpinBox *min_value = nullptr; CheckBox *sync = nullptr; + OptionButton *interpolation = nullptr; HBoxContainer *edit_hb = nullptr; SpinBox *edit_value = nullptr; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index e09636d297..0f9ce89f02 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5401,11 +5401,13 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) { canvas_item_editor->show(); canvas_item_editor->set_physics_process(true); RenderingServer::get_singleton()->viewport_set_disable_2d(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), false); + RenderingServer::get_singleton()->viewport_set_environment_mode(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), RS::VIEWPORT_ENVIRONMENT_ENABLED); } else { canvas_item_editor->hide(); canvas_item_editor->set_physics_process(false); RenderingServer::get_singleton()->viewport_set_disable_2d(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), true); + RenderingServer::get_singleton()->viewport_set_environment_mode(EditorNode::get_singleton()->get_scene_root()->get_viewport_rid(), RS::VIEWPORT_ENVIRONMENT_DISABLED); } } diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index a7b32ce0c3..14b5f7cefb 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -425,6 +425,7 @@ void SpriteFramesEditor::_notification(int p_what) { _update_stop_icon(); autoplay->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons"))); + anim_loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); play->set_icon(get_theme_icon(SNAME("PlayStart"), SNAME("EditorIcons"))); play_from->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); play_bw->set_icon(get_theme_icon(SNAME("PlayStartBackwards"), SNAME("EditorIcons"))); @@ -1114,18 +1115,19 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { } for (int i = 0; i < frames->get_frame_count(edited_anim); i++) { - String name; + String name = itos(i); Ref<Texture2D> texture = frames->get_frame_texture(edited_anim, i); float duration = frames->get_frame_duration(edited_anim, i); - String duration_string; - if (duration != 1.0f) { - duration_string = String::utf8(" [ ×") + String::num_real(frames->get_frame_duration(edited_anim, i)) + " ]"; - } if (texture.is_null()) { - name = itos(i) + ": " + TTR("(empty)") + duration_string; - } else { - name = itos(i) + ": " + texture->get_name() + duration_string; + texture = empty_icon; + name += ": " + TTR("(empty)"); + } else if (!texture->get_name().is_empty()) { + name += ": " + texture->get_name(); + } + + if (duration != 1.0f) { + name += String::utf8(" [× ") + String::num(duration, 2) + "]"; } frame_list->add_item(name, texture); @@ -1523,12 +1525,33 @@ SpriteFramesEditor::SpriteFramesEditor() { autoplay_container = memnew(HBoxContainer); hbc_animlist->add_child(autoplay_container); + autoplay_container->add_child(memnew(VSeparator)); + autoplay = memnew(Button); autoplay->set_flat(true); autoplay->set_tooltip_text(TTR("Autoplay on Load")); autoplay_container->add_child(autoplay); + hbc_animlist->add_child(memnew(VSeparator)); + + anim_loop = memnew(Button); + anim_loop->set_toggle_mode(true); + anim_loop->set_flat(true); + anim_loop->set_tooltip_text(TTR("Animation Looping")); + anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); + hbc_animlist->add_child(anim_loop); + + anim_speed = memnew(SpinBox); + anim_speed->set_suffix(TTR("FPS")); + anim_speed->set_min(0); + anim_speed->set_max(120); + anim_speed->set_step(0.01); + anim_speed->set_custom_arrow_step(1); + anim_speed->set_tooltip_text(TTR("Animation Speed")); + anim_speed->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_animation_speed_changed)); + hbc_animlist->add_child(anim_speed); + anim_search_box = memnew(LineEdit); sub_vb->add_child(anim_search_box); anim_search_box->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1549,23 +1572,6 @@ SpriteFramesEditor::SpriteFramesEditor() { delete_anim->set_shortcut_context(animations); delete_anim->set_shortcut(ED_SHORTCUT("sprite_frames/delete_animation", TTR("Delete Animation"), Key::KEY_DELETE)); - HBoxContainer *hbc_anim_speed = memnew(HBoxContainer); - hbc_anim_speed->add_child(memnew(Label(TTR("Speed:")))); - vbc_animlist->add_child(hbc_anim_speed); - anim_speed = memnew(SpinBox); - anim_speed->set_suffix(TTR("FPS")); - anim_speed->set_min(0); - anim_speed->set_max(120); - anim_speed->set_step(0.01); - anim_speed->set_h_size_flags(SIZE_EXPAND_FILL); - hbc_anim_speed->add_child(anim_speed); - anim_speed->connect("value_changed", callable_mp(this, &SpriteFramesEditor::_animation_speed_changed)); - - anim_loop = memnew(CheckButton); - anim_loop->set_text(TTR("Loop")); - vbc_animlist->add_child(anim_loop); - anim_loop->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_loop_changed)); - VBoxContainer *vbc = memnew(VBoxContainer); add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); @@ -1667,6 +1673,7 @@ SpriteFramesEditor::SpriteFramesEditor() { frame_duration->set_min(SPRITE_FRAME_MINIMUM_DURATION); // Avoid zero div. frame_duration->set_max(10); frame_duration->set_step(0.01); + frame_duration->set_custom_arrow_step(0.1); frame_duration->set_allow_lesser(false); frame_duration->set_allow_greater(true); hbc->add_child(frame_duration); @@ -1946,9 +1953,7 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) { EditorNode::get_singleton()->make_bottom_panel_item_visible(frames_editor); } else { button->hide(); - if (frames_editor->is_visible_in_tree()) { - EditorNode::get_singleton()->hide_bottom_panel(); - } + frames_editor->edit(Ref<SpriteFrames>()); } } diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 19ecfb00ed..1dfb909388 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -73,6 +73,7 @@ class SpriteFramesEditor : public HSplitContainer { Ref<Texture2D> autoplay_icon; Ref<Texture2D> stop_icon; Ref<Texture2D> pause_icon; + Ref<Texture2D> empty_icon = memnew(ImageTexture); HBoxContainer *playback_container = nullptr; Button *stop = nullptr; @@ -100,13 +101,14 @@ class SpriteFramesEditor : public HSplitContainer { Button *add_anim = nullptr; Button *delete_anim = nullptr; + SpinBox *anim_speed = nullptr; + Button *anim_loop = nullptr; + HBoxContainer *autoplay_container = nullptr; Button *autoplay = nullptr; - LineEdit *anim_search_box = nullptr; + LineEdit *anim_search_box = nullptr; Tree *animations = nullptr; - SpinBox *anim_speed = nullptr; - CheckButton *anim_loop = nullptr; EditorFileDialog *file = nullptr; diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 37f941c7b2..9695a7042d 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -257,6 +257,7 @@ ProgressDialog::ProgressDialog() { add_child(main); main->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); set_exclusive(true); + set_flag(Window::FLAG_POPUP, false); last_progress_tick = 0; singleton = this; cancel_hb = memnew(HBoxContainer); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 93d3a323ea..d3e16211f7 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -32,10 +32,11 @@ #include "modules/modules_enabled.gen.h" -const int ERROR_CODE = 77; - +#ifndef DISABLE_DEPRECATED #ifdef MODULE_REGEX_ENABLED +const int ERROR_CODE = 77; + #include "modules/regex/regex.h" #include "core/io/dir_access.h" @@ -44,7 +45,7 @@ const int ERROR_CODE = 77; #include "core/templates/list.h" #include "core/templates/local_vector.h" -static const char *enum_renames[][2] = { +const char *ProjectConverter3To4::enum_renames[][2] = { //// constants { "TYPE_COLOR_ARRAY", "TYPE_PACKED_COLOR_ARRAY" }, { "TYPE_FLOAT64_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY" }, @@ -164,7 +165,7 @@ static const char *enum_renames[][2] = { { nullptr, nullptr }, }; -static const char *gdscript_function_renames[][2] = { +const char *ProjectConverter3To4::gdscript_function_renames[][2] = { // { "_set_name", "get_tracker_name"}, // XRPositionalTracker - CameraFeed use this // { "_unhandled_input", "_unhandled_key_input"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer // { "create_gizmo", "_create_gizmo"}, // EditorNode3DGizmoPlugin - may be used @@ -240,6 +241,9 @@ static const char *gdscript_function_renames[][2] = { { "can_generate_small_preview", "_can_generate_small_preview" }, // EditorResourcePreviewGenerator { "can_instance", "can_instantiate" }, // PackedScene, Script { "canvas_light_set_scale", "canvas_light_set_texture_scale" }, // RenderingServer + { "capture_get_device", "get_input_device" }, // AudioServer + { "capture_get_device_list", "get_input_device_list" }, // AudioServer + { "capture_set_device", "set_input_device" }, // AudioServer { "center_viewport_to_cursor", "center_viewport_to_caret" }, // TextEdit { "change_scene", "change_scene_to_file" }, // SceneTree { "change_scene_to", "change_scene_to_packed" }, // SceneTree @@ -300,6 +304,8 @@ static const char *gdscript_function_renames[][2] = { { "get_cursor_position", "get_caret_column" }, // LineEdit { "get_d", "get_distance" }, // LineShape2D { "get_depth_bias_enable", "get_depth_bias_enabled" }, // RDPipelineRasterizationState + { "get_device", "get_output_device" }, // AudioServer + { "get_device_list", "get_output_device_list" }, // AudioServer { "get_drag_data", "_get_drag_data" }, // Control { "get_editor_viewport", "get_editor_main_screen" }, // EditorPlugin { "get_enabled_focus_mode", "get_focus_mode" }, // BaseButton @@ -311,8 +317,8 @@ static const char *gdscript_function_renames[][2] = { { "get_font_types", "get_font_type_list" }, // Theme { "get_frame_color", "get_color" }, // ColorRect { "get_global_rate_scale", "get_playback_speed_scale" }, // AudioServer - { "get_gravity_distance_scale", "get_gravity_point_distance_scale" }, //Area2D - { "get_gravity_vector", "get_gravity_direction" }, //Area2D + { "get_gravity_distance_scale", "get_gravity_point_unit_distance" }, // Area(2D/3D) + { "get_gravity_vector", "get_gravity_direction" }, // Area(2D/3D) { "get_h_scrollbar", "get_h_scroll_bar" }, //ScrollContainer { "get_hand", "get_tracker_hand" }, // XRPositionalTracker { "get_handle_name", "_get_handle_name" }, // EditorNode3DGizmo @@ -498,6 +504,7 @@ static const char *gdscript_function_renames[][2] = { { "set_cursor_position", "set_caret_column" }, // LineEdit { "set_d", "set_distance" }, // WorldMarginShape2D { "set_depth_bias_enable", "set_depth_bias_enabled" }, // RDPipelineRasterizationState + { "set_device", "set_output_device" }, // AudioServer { "set_doubleclick", "set_double_click" }, // InputEventMouseButton { "set_draw_red", "set_draw_warning" }, // EditorProperty { "set_enable_follow_smoothing", "set_position_smoothing_enabled" }, // Camera2D @@ -509,8 +516,8 @@ static const char *gdscript_function_renames[][2] = { { "set_follow_smoothing", "set_position_smoothing_speed" }, // Camera2D { "set_frame_color", "set_color" }, // ColorRect { "set_global_rate_scale", "set_playback_speed_scale" }, // AudioServer - { "set_gravity_distance_scale", "set_gravity_point_distance_scale" }, // Area2D - { "set_gravity_vector", "set_gravity_direction" }, // Area2D + { "set_gravity_distance_scale", "set_gravity_point_unit_distance" }, // Area(2D/3D) + { "set_gravity_vector", "set_gravity_direction" }, // Area(2D/3D) { "set_h_drag_enabled", "set_drag_horizontal_enabled" }, // Camera2D { "set_icon_align", "set_icon_alignment" }, // Button { "set_interior_ambient", "set_ambient_color" }, // ReflectionProbe @@ -619,7 +626,7 @@ static const char *gdscript_function_renames[][2] = { }; // gdscript_function_renames clone with CamelCase -static const char *csharp_function_renames[][2] = { +const char *ProjectConverter3To4::csharp_function_renames[][2] = { // { "_SetName", "GetTrackerName"}, // XRPositionalTracker - CameraFeed use this // { "_UnhandledInput", "_UnhandledKeyInput"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer // { "CreateGizmo", "_CreateGizmo"}, // EditorNode3DGizmoPlugin - may be used @@ -696,6 +703,9 @@ static const char *csharp_function_renames[][2] = { { "CanGenerateSmallPreview", "_CanGenerateSmallPreview" }, // EditorResourcePreviewGenerator { "CanInstance", "CanInstantiate" }, // PackedScene, Script { "CanvasLightSetScale", "CanvasLightSetTextureScale" }, // RenderingServer + { "CaptureGetDevice", "GetInputDevice" }, // AudioServer + { "CaptureGetDeviceList", "GetInputDeviceList" }, // AudioServer + { "CaptureSetDevice", "SetInputDevice" }, // AudioServer { "CenterViewportToCursor", "CenterViewportToCaret" }, // TextEdit { "ChangeScene", "ChangeSceneToFile" }, // SceneTree { "ChangeSceneTo", "ChangeSceneToPacked" }, // SceneTree @@ -753,6 +763,8 @@ static const char *csharp_function_renames[][2] = { { "GetCursorPosition", "GetCaretColumn" }, // LineEdit { "GetD", "GetDistance" }, // LineShape2D { "GetDepthBiasEnable", "GetDepthBiasEnabled" }, // RDPipelineRasterizationState + { "GetDevice", "GetOutputDevice" }, // AudioServer + { "GetDeviceList", "GetOutputDeviceList" }, // AudioServer { "GetDragDataFw", "_GetDragDataFw" }, // ScriptEditor { "GetEditorViewport", "GetViewport" }, // EditorPlugin { "GetEnabledFocusMode", "GetFocusMode" }, // BaseButton @@ -941,6 +953,7 @@ static const char *csharp_function_renames[][2] = { { "SetCursorPosition", "SetCaretColumn" }, // LineEdit { "SetD", "SetDistance" }, // WorldMarginShape2D { "SetDepthBiasEnable", "SetDepthBiasEnabled" }, // RDPipelineRasterizationState + { "SetDevice", "SetOutputDevice" }, // AudioServer { "SetDoubleclick", "SetDoubleClick" }, // InputEventMouseButton { "SetEnableFollowSmoothing", "SetFollowSmoothingEnabled" }, // Camera2D { "SetEnabledFocusMode", "SetFocusMode" }, // BaseButton @@ -1056,7 +1069,7 @@ static const char *csharp_function_renames[][2] = { }; // Some needs to be disabled, because users can use this names as variables -static const char *gdscript_properties_renames[][2] = { +const char *ProjectConverter3To4::gdscript_properties_renames[][2] = { // // { "d", "distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą` // // {"alt","alt_pressed"}, // This may broke a lot of comments and user variables // // {"command","command_pressed"},// This may broke a lot of comments and user variables @@ -1069,6 +1082,7 @@ static const char *gdscript_properties_renames[][2] = { // // {"shift","shift_pressed"},// This may broke a lot of comments and user variables // { "autowrap", "autowrap_mode" }, // Label // { "cast_to", "target_position" }, // RayCast2D, RayCast3D + // { "device", "output_device"}, // AudioServer - Too vague, most likely breaks comments & variables // { "doubleclick", "double_click" }, // InputEventMouseButton // { "group", "button_group" }, // BaseButton // { "process_mode", "process_callback" }, // AnimationTree, Camera2D @@ -1084,6 +1098,7 @@ static const char *gdscript_properties_renames[][2] = { { "bbcode_text", "text" }, // RichTextLabel { "bg", "panel" }, // Theme { "bg_focus", "focus" }, // Theme + { "capture_device", "input_device" }, // AudioServer { "caret_blink_speed", "caret_blink_interval" }, // TextEdit, LineEdit { "caret_moving_by_right_click", "caret_move_on_right_click" }, // TextEdit { "caret_position", "caret_column" }, // LineEdit @@ -1111,8 +1126,8 @@ static const char *gdscript_properties_renames[][2] = { { "files_disabled", "file_disabled_color" }, // Theme { "folder_icon_modulate", "folder_icon_color" }, // Theme { "global_rate_scale", "playback_speed_scale" }, // AudioServer - { "gravity_distance_scale", "gravity_point_distance_scale" }, // Area2D - { "gravity_vec", "gravity_direction" }, // Area2D + { "gravity_distance_scale", "gravity_point_unit_distance" }, // Area(2D/3D) + { "gravity_vec", "gravity_direction" }, // Area(2D/3D) { "hint_tooltip", "tooltip_text" }, // Control { "hseparation", "h_separation" }, // Theme { "icon_align", "icon_alignment" }, // Button @@ -1173,7 +1188,7 @@ static const char *gdscript_properties_renames[][2] = { }; // Some needs to be disabled, because users can use this names as variables -static const char *csharp_properties_renames[][2] = { +const char *ProjectConverter3To4::csharp_properties_renames[][2] = { // // { "D", "Distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą` // // {"Alt","AltPressed"}, // This may broke a lot of comments and user variables // // {"Command","CommandPressed"},// This may broke a lot of comments and user variables @@ -1278,7 +1293,7 @@ static const char *csharp_properties_renames[][2] = { { nullptr, nullptr }, }; -static const char *gdscript_signals_renames[][2] = { +const char *ProjectConverter3To4::gdscript_signals_renames[][2] = { // {"instantiate","instance"}, // FileSystemDock // { "hide", "hidden" }, // CanvasItem - function with same name exists // { "tween_all_completed","loop_finished"}, // Tween - TODO, not sure @@ -1303,7 +1318,7 @@ static const char *gdscript_signals_renames[][2] = { { nullptr, nullptr }, }; -static const char *csharp_signals_renames[][2] = { +const char *ProjectConverter3To4::csharp_signals_renames[][2] = { // {"Instantiate","Instance"}, // FileSystemDock // { "Hide", "Hidden" }, // CanvasItem - function with same name exists // { "TweenAllCompleted","LoopFinished"}, // Tween - TODO, not sure @@ -1327,7 +1342,7 @@ static const char *csharp_signals_renames[][2] = { }; -static const char *project_settings_renames[][2] = { +const char *ProjectConverter3To4::project_settings_renames[][2] = { { "audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db" }, { "audio/channel_disable_time", "audio/buses/channel_disable_time" }, { "audio/default_bus_layout", "audio/buses/default_bus_layout" }, @@ -1372,7 +1387,7 @@ static const char *project_settings_renames[][2] = { { nullptr, nullptr }, }; -static const char *input_map_renames[][2] = { +const char *ProjectConverter3To4::input_map_renames[][2] = { { ",\"alt\":", ",\"alt_pressed\":" }, { ",\"shift\":", ",\"shift_pressed\":" }, { ",\"control\":", ",\"ctrl_pressed\":" }, @@ -1384,7 +1399,7 @@ static const char *input_map_renames[][2] = { { nullptr, nullptr }, }; -static const char *builtin_types_renames[][2] = { +const char *ProjectConverter3To4::builtin_types_renames[][2] = { { "PoolByteArray", "PackedByteArray" }, { "PoolColorArray", "PackedColorArray" }, { "PoolIntArray", "PackedInt32Array" }, @@ -1398,7 +1413,7 @@ static const char *builtin_types_renames[][2] = { { nullptr, nullptr }, }; -static const char *shaders_renames[][2] = { +const char *ProjectConverter3To4::shaders_renames[][2] = { { "ALPHA_SCISSOR", "ALPHA_SCISSOR_THRESHOLD" }, { "CAMERA_MATRIX", "INV_VIEW_MATRIX" }, { "INV_CAMERA_MATRIX", "VIEW_MATRIX" }, @@ -1416,7 +1431,7 @@ static const char *shaders_renames[][2] = { { nullptr, nullptr }, }; -static const char *class_renames[][2] = { +const char *ProjectConverter3To4::class_renames[][2] = { // { "BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D" }, // Class is not visible in ClassDB // { "BulletPhysicsServer", "BulletPhysicsServer3D" }, // Class is not visible in ClassDB // { "GDScriptFunctionState", "Node3D" }, // TODO - not sure to which should be changed @@ -1641,7 +1656,7 @@ static const char *class_renames[][2] = { { nullptr, nullptr }, }; -static const char *color_renames[][2] = { +const char *ProjectConverter3To4::ProjectConverter3To4::color_renames[][2] = { { "aliceblue", "ALICE_BLUE" }, { "antiquewhite", "ANTIQUE_WHITE" }, { "aqua", "AQUA" }, @@ -4350,3 +4365,4 @@ int ProjectConverter3To4::validate_conversion() { } #endif // MODULE_REGEX_ENABLED +#endif // DISABLE_DEPRECATED diff --git a/editor/project_converter_3_to_4.h b/editor/project_converter_3_to_4.h index b3aa52f1e3..6ec2dd188d 100644 --- a/editor/project_converter_3_to_4.h +++ b/editor/project_converter_3_to_4.h @@ -29,6 +29,7 @@ /**************************************************************************/ #ifndef PROJECT_CONVERTER_3_TO_4_H +#ifndef DISABLE_DEPRECATED #define PROJECT_CONVERTER_3_TO_4_H #include "core/io/file_access.h" @@ -41,6 +42,19 @@ class RegEx; class ProjectConverter3To4 { public: class RegExContainer; + static const char *enum_renames[][2]; + static const char *gdscript_function_renames[][2]; + static const char *csharp_function_renames[][2]; + static const char *gdscript_properties_renames[][2]; + static const char *csharp_properties_renames[][2]; + static const char *gdscript_signals_renames[][2]; + static const char *csharp_signals_renames[][2]; + static const char *project_settings_renames[][2]; + static const char *input_map_renames[][2]; + static const char *builtin_types_renames[][2]; + static const char *shaders_renames[][2]; + static const char *class_renames[][2]; + static const char *color_renames[][2]; private: uint64_t maximum_file_size; @@ -97,4 +111,6 @@ public: int convert(); }; +#endif // DISABLE_DEPRECATED + #endif // PROJECT_CONVERTER_3_TO_4_H diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 9cc3d0413d..13c7a8202c 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -558,7 +558,7 @@ void CSGShape3D::_notification(int p_what) { set_collision_layer(collision_layer); set_collision_mask(collision_mask); set_collision_priority(collision_priority); - _update_collision_faces(); + _make_dirty(); } } break; @@ -1763,7 +1763,7 @@ CSGBrush *CSGPolygon3D::_build_brush() { } } - if (!path) { + if (!path || !path->is_inside_tree()) { return new_brush; } diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 7f9d4ae253..cd837b3a91 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -563,6 +563,7 @@ [/codeblock] [b]Note:[/b] Only the script can have a custom icon. Inner classes are not supported. [b]Note:[/b] As annotations describe their subject, the [code]@icon[/code] annotation must be placed before the class definition and inheritance. + [b]Note:[/b] Unlike other annotations, the argument of the [code]@icon[/code] annotation must be a string literal (constant expressions are not supported). </description> </annotation> <annotation name="@onready"> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d9b8a540c0..8324cb0fe0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -706,11 +706,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc } members_cache.push_back(member.variable->export_info); - Variant default_value; - if (member.variable->initializer && member.variable->initializer->is_constant) { - default_value = member.variable->initializer->reduced_value; - GDScriptCompiler::convert_to_initializer_type(default_value, member.variable); - } + Variant default_value = analyzer.make_variable_default_value(member.variable); member_default_values_cache[member.variable->identifier->name] = default_value; } break; case GDScriptParser::ClassNode::Member::SIGNAL: { @@ -1525,41 +1521,24 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); if (E) { const GDScript::MemberInfo *member = &E->value; - if (member->setter) { - const Variant *val = &p_value; + Variant value = p_value; + if (member->data_type.has_type && !member->data_type.is_type(value)) { + const Variant *args = &p_value; Callable::CallError err; - callp(member->setter, &val, 1, err); - if (err.error == Callable::CallError::CALL_OK) { - return true; //function exists, call was successful - } else { + Variant::construct(member->data_type.builtin_type, value, &args, 1, err); + if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { return false; } + } + if (member->setter) { + const Variant *args = &value; + Callable::CallError err; + callp(member->setter, &args, 1, err); + return err.error == Callable::CallError::CALL_OK; } else { - if (member->data_type.has_type) { - if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) { - // Typed array. - if (p_value.get_type() == Variant::ARRAY) { - return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value); - } else { - return false; - } - } else if (!member->data_type.is_type(p_value)) { - // Try conversion - Callable::CallError ce; - const Variant *value = &p_value; - Variant converted; - Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce); - if (ce.error == Callable::CallError::CALL_OK) { - members.write[member->index] = converted; - return true; - } else { - return false; - } - } - } - members.write[member->index] = p_value; + members.write[member->index] = value; + return true; } - return true; } } @@ -2458,21 +2437,25 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b GDScriptParser parser; err = parser.parse(source, p_path, false); - if (err) { - return String(); - } - - GDScriptAnalyzer analyzer(&parser); - err = analyzer.resolve_inheritance(); - if (err) { - return String(); - } const GDScriptParser::ClassNode *c = parser.get_tree(); - - if (r_base_type) { - *r_base_type = c->get_datatype().native_type; - } + if (!c) { + return String(); // No class parsed. + } + + /* **WARNING** + * + * This function is written with the goal to be *extremely* error tolerant, as such + * it should meet the following requirements: + * + * - It must not rely on the analyzer (in fact, the analyzer must not be used here), + * because at the time global classes are parsed, the dependencies may not be present + * yet, hence the function will fail (which is unintended). + * - It must not fail even if the parsing fails, because even if the file is broken, + * it should attempt its best to retrieve the inheritance metadata. + * + * Before changing this function, please ask the current maintainer of EditorFileSystem. + */ if (r_icon_path) { if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { @@ -2481,7 +2464,73 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b *r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path(); } } + if (r_base_type) { + const GDScriptParser::ClassNode *subclass = c; + String path = p_path; + GDScriptParser subparser; + while (subclass) { + if (subclass->extends_used) { + if (!subclass->extends_path.is_empty()) { + if (subclass->extends.size() == 0) { + get_global_class_name(subclass->extends_path, r_base_type); + subclass = nullptr; + break; + } else { + Vector<StringName> extend_classes = subclass->extends; + + Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); + if (subfile.is_null()) { + break; + } + String subsource = subfile->get_as_utf8_string(); + + if (subsource.is_empty()) { + break; + } + String subpath = subclass->extends_path; + if (subpath.is_relative_path()) { + subpath = path.get_base_dir().path_join(subpath).simplify_path(); + } + if (OK != subparser.parse(subsource, subpath, false)) { + break; + } + path = subpath; + subclass = subparser.get_tree(); + + while (extend_classes.size() > 0) { + bool found = false; + for (int i = 0; i < subclass->members.size(); i++) { + if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { + continue; + } + + const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; + if (inner_class->identifier->name == extend_classes[0]) { + extend_classes.remove_at(0); + found = true; + subclass = inner_class; + break; + } + } + if (!found) { + subclass = nullptr; + break; + } + } + } + } else if (subclass->extends.size() == 1) { + *r_base_type = subclass->extends[0]; + subclass = nullptr; + } else { + break; + } + } else { + *r_base_type = "RefCounted"; + subclass = nullptr; + } + } + } return c->identifier != nullptr ? String(c->identifier->name) : String(); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 8dd65a700a..1c2b743909 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -580,6 +580,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type if (result.builtin_type == Variant::ARRAY) { GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type)); if (container_type.kind != GDScriptParser::DataType::VARIANT) { + container_type.is_constant = false; result.set_container_element_type(container_type); } } @@ -1571,18 +1572,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer); - if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) { - update_array_literal_element_type(specified_type, array); + if (has_specified_type && specified_type.has_container_element_type()) { + update_array_literal_element_type(array, specified_type.get_container_element_type()); } } - if (is_constant) { - if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true); - } else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true); - } - if (!p_assignable->initializer->is_constant) { + if (is_constant && !p_assignable->initializer->is_constant) { + bool is_initializer_value_reduced = false; + Variant initializer_value = make_expression_reduced_value(p_assignable->initializer, is_initializer_value_reduced); + if (is_initializer_value_reduced) { + p_assignable->initializer->is_constant = true; + p_assignable->initializer->reduced_value = initializer_value; + } else { push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer); } } @@ -1630,6 +1631,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi } else { push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer); } + } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) { + mark_node_unsafe(p_assignable->initializer); #ifdef DEBUG_ENABLED } else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) { parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION); @@ -1969,20 +1972,40 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { } if (p_return->return_value != nullptr) { - reduce_expression(p_return->return_value); - if (p_return->return_value->type == GDScriptParser::Node::ARRAY) { - // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); + bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL; + bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL; + if (is_void_function && is_call) { + // Pretend the call is a root expression to allow those that are "void". + reduce_call(static_cast<GDScriptParser::CallNode *>(p_return->return_value), false, true); + } else { + reduce_expression(p_return->return_value); + } + if (is_void_function) { + p_return->void_return = true; + const GDScriptParser::DataType &return_type = p_return->return_value->datatype; + if (is_call && !return_type.is_hard_type()) { + String function_name = parser->current_function->identifier ? parser->current_function->identifier->name.operator String() : String("<anonymous function>"); + String called_function_name = static_cast<GDScriptParser::CallNode *>(p_return->return_value)->function_name.operator String(); +#ifdef DEBUG_ENABLED + parser->push_warning(p_return, GDScriptWarning::UNSAFE_VOID_RETURN, function_name, called_function_name); +#endif + mark_node_unsafe(p_return); + } else if (!is_call) { + push_error("A void function cannot return a value.", p_return); } + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::NIL; + result.is_constant = true; + } else { + if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type()); + } + if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { + update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); + } + result = p_return->return_value->get_datatype(); } - if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) { - push_error("A void function cannot return a value.", p_return); - } - if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) { - update_const_expression_builtin_type(p_return->return_value, expected_type, "return"); - } - result = p_return->return_value->get_datatype(); } else { // Return type is null by default. result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2183,49 +2206,26 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr // When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed. // This function determines which type is that (if any). -void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) { - GDScriptParser::DataType array_type = p_array_literal->get_datatype(); - if (p_array_literal->elements.size() == 0) { - // Empty array literal, just make the same type as the storage. - array_type.set_container_element_type(p_base_type.get_container_element_type()); - } else { - // Check if elements match. - bool all_same_type = true; - bool all_have_type = true; - - GDScriptParser::DataType element_type; - for (int i = 0; i < p_array_literal->elements.size(); i++) { - if (i == 0) { - element_type = p_array_literal->elements[0]->get_datatype(); - } else { - GDScriptParser::DataType this_element_type = p_array_literal->elements[i]->get_datatype(); - if (this_element_type.has_no_type()) { - all_same_type = false; - all_have_type = false; - break; - } else if (element_type != this_element_type) { - if (!is_type_compatible(element_type, this_element_type, false)) { - if (is_type_compatible(this_element_type, element_type, false)) { - // This element is a super-type to the previous type, so we use the super-type. - element_type = this_element_type; - } else { - // It's incompatible. - all_same_type = false; - break; - } - } - } - } +void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) { + for (int i = 0; i < p_array->elements.size(); i++) { + GDScriptParser::ExpressionNode *element_node = p_array->elements[i]; + if (element_node->is_constant) { + update_const_expression_builtin_type(element_node, p_element_type, "include"); } - if (all_same_type) { - element_type.is_constant = false; - array_type.set_container_element_type(element_type); - } else if (all_have_type) { - push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal); + const GDScriptParser::DataType &element_type = element_node->get_datatype(); + if (element_type.has_no_type() || element_type.is_variant() || !element_type.is_hard_type()) { + mark_node_unsafe(element_node); + continue; + } + if (!is_type_compatible(p_element_type, element_type, true, p_array)) { + push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", element_type.to_string(), p_element_type.to_string()), element_node); + return; } } - // Update the type on the value itself. - p_array_literal->set_datatype(array_type); + + GDScriptParser::DataType array_type = p_array->get_datatype(); + array_type.set_container_element_type(p_element_type); + p_array->set_datatype(array_type); } void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) { @@ -2243,8 +2243,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value)); + if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type()); } if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) { @@ -2322,6 +2322,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig // weak non-variant assignee and incompatible result downgrades_assignee = true; } + } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) { + // typed array assignee and untyped array result + mark_node_unsafe(p_assignment); } } } @@ -2484,6 +2487,62 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o p_binary_op->set_datatype(result); } +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED +const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String key) { + for (int index = 0; map[index][0]; index++) { + if (map[index][0] == key) { + return map[index][1]; + } + } + return nullptr; +} + +// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map. +// Returns the new name if found, nullptr otherwise. +const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) { + switch (type) { + case GDScriptParser::Node::IDENTIFIER: { + // Check properties + const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_properties_renames, identifier); + if (result) { + return result; + } + // Check enum values + result = get_rename_from_map(ProjectConverter3To4::enum_renames, identifier); + if (result) { + return result; + } + // Check color constants + result = get_rename_from_map(ProjectConverter3To4::color_renames, identifier); + if (result) { + return result; + } + // Check type names + result = get_rename_from_map(ProjectConverter3To4::class_renames, identifier); + if (result) { + return result; + } + return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier); + } + case GDScriptParser::Node::CALL: { + const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_function_renames, identifier); + if (result) { + return result; + } + // Built-in Types are mistaken for function calls when the built-in type is not found. + // Check built-in types if function rename not found + return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier); + } + // Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints. + default: + // No rename found, return null + return nullptr; + } +} +#endif // DISABLE_DEPRECATED +#endif // TOOLS_ENABLED + void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) { bool all_is_constant = true; HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing. @@ -2822,7 +2881,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; if (index < par_types.size() && par_types[index].has_container_element_type()) { - update_array_literal_element_type(par_types[index], E.value); + update_array_literal_element_type(E.value, par_types[index].get_container_element_type()); } } validate_call_arg(par_types, default_arg_count, is_vararg, p_call); @@ -2901,7 +2960,22 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) { String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string(); +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type); + if (renamed_function_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()"); + } + } + push_error(vformat(R"*(Function "%s()" not found in base %s.%s)*", p_call->function_name, base_name, rename_hint), p_call->is_super ? p_call : p_call->callee); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); +#endif // DISABLE_DEPRECATED +#else push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee); +#endif } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) { push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call); } @@ -2933,6 +3007,10 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } } + if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) { + update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type()); + } + if (!cast_type.is_variant()) { GDScriptParser::DataType op_type = p_cast->operand->get_datatype(); if (op_type.is_variant() || !op_type.is_hard_type()) { @@ -3038,10 +3116,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide p_identifier->set_datatype(p_identifier_datatype); Error err = OK; - GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path)); - scr = scr->find_class(p_identifier_datatype.class_type->fqcn); - p_identifier->reduced_value = scr; + Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err); + if (err) { + push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier); + return; + } + p_identifier->reduced_value = scr->find_class(p_identifier_datatype.class_type->fqcn); p_identifier->is_constant = true; } @@ -3085,7 +3165,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->reduced_value = result; p_identifier->set_datatype(type_from_variant(result, p_identifier)); } else if (base.is_hard_type()) { - push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif // DISABLE_DEPRECATED +#else + push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif } } else { switch (base.builtin_type) { @@ -3114,7 +3209,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } } if (base.is_hard_type()) { +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif // DISABLE_DEPRECATED +#else push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); +#endif } } } @@ -3453,7 +3563,22 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (GDScriptUtilityFunctions::function_exists(name)) { push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier); } else { +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + String rename_hint = String(); + if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) { + const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type); + if (renamed_identifier_name) { + rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name); + } + } + push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier); +#else // !DISABLE_DEPRECATED + push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); +#endif // DISABLE_DEPRECATED +#else push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier); +#endif } GDScriptParser::DataType dummy; dummy.kind = GDScriptParser::DataType::VARIANT; @@ -3585,12 +3710,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { reduce_expression(p_subscript->base); - - if (p_subscript->base->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false); - } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false); - } } GDScriptParser::DataType result_type; @@ -3915,58 +4034,146 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } -void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) { +Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) { + Variant value; + + if (p_expression->is_constant) { + is_reduced = true; + value = p_expression->reduced_value; + } else if (p_expression->type == GDScriptParser::Node::ARRAY) { + value = make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced); + } else if (p_expression->type == GDScriptParser::Node::DICTIONARY) { + value = make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced); + } else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) { + value = make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced); + } + + return value; +} + +Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) { + Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array(); + + array.resize(p_array->elements.size()); for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; - if (element->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const); - } else if (element->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const); + bool is_element_value_reduced = false; + Variant element_value = make_expression_reduced_value(element, is_element_value_reduced); + if (!is_element_value_reduced) { + return Variant(); } - if (!element->is_constant) { - return; - } + array[i] = element_value; } - Array array; - array.resize(p_array->elements.size()); - for (int i = 0; i < p_array->elements.size(); i++) { - array[i] = p_array->elements[i]->reduced_value; - } - if (p_is_const) { - array.make_read_only(); - } - p_array->is_constant = true; - p_array->reduced_value = array; + array.make_read_only(); + + is_reduced = true; + return array; } -void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) { +Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) { + Dictionary dictionary; + for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - if (element.value->type == GDScriptParser::Node::ARRAY) { - const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const); - } else if (element.value->type == GDScriptParser::Node::DICTIONARY) { - const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const); + bool is_element_key_reduced = false; + Variant element_key = make_expression_reduced_value(element.key, is_element_key_reduced); + if (!is_element_key_reduced) { + return Variant(); } - if (!element.key->is_constant || !element.value->is_constant) { - return; + bool is_element_value_reduced = false; + Variant element_value = make_expression_reduced_value(element.value, is_element_value_reduced); + if (!is_element_value_reduced) { + return Variant(); } + + dictionary[element_key] = element_value; } - Dictionary dict; - for (int i = 0; i < p_dictionary->elements.size(); i++) { - const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - dict[element.key->reduced_value] = element.value->reduced_value; + dictionary.make_read_only(); + + is_reduced = true; + return dictionary; +} + +Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced) { + bool is_base_value_reduced = false; + Variant base_value = make_expression_reduced_value(p_subscript->base, is_base_value_reduced); + if (!is_base_value_reduced) { + return Variant(); + } + + if (p_subscript->is_attribute) { + bool is_valid = false; + Variant value = base_value.get_named(p_subscript->attribute->name, is_valid); + if (is_valid) { + is_reduced = true; + return value; + } else { + return Variant(); + } + } else { + bool is_index_value_reduced = false; + Variant index_value = make_expression_reduced_value(p_subscript->index, is_index_value_reduced); + if (!is_index_value_reduced) { + return Variant(); + } + + bool is_valid = false; + Variant value = base_value.get(index_value, &is_valid); + if (is_valid) { + is_reduced = true; + return value; + } else { + return Variant(); + } } - if (p_is_const) { - dict.make_read_only(); +} + +Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) { + Array array; + + Ref<Script> script_type = p_element_datatype.script_type; + if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) { + Error err = OK; + Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err); + if (err) { + push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node); + return array; + } + script_type.reference_ptr(scr->find_class(p_element_datatype.class_type->fqcn)); } - p_dictionary->is_constant = true; - p_dictionary->reduced_value = dict; + + array.set_typed(p_element_datatype.builtin_type, p_element_datatype.native_type, script_type); + + return array; +} + +Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNode *p_variable) { + Variant result = Variant(); + + if (p_variable->initializer) { + bool is_initializer_value_reduced = false; + Variant initializer_value = make_expression_reduced_value(p_variable->initializer, is_initializer_value_reduced); + if (is_initializer_value_reduced) { + result = initializer_value; + } + } else { + GDScriptParser::DataType datatype = p_variable->get_datatype(); + if (datatype.is_hard_type() && datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) { + if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) { + result = make_array_from_element_datatype(datatype.get_container_element_type()); + } else { + VariantInternal::initialize(&result, datatype.builtin_type); + } + } + } + + return result; } GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { @@ -4437,14 +4644,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ } if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) { // Check the element type. - if (p_target.has_container_element_type()) { - if (!p_source.has_container_element_type()) { - // TODO: Maybe this is valid but unsafe? - // Variant array can't be appended to typed array. - valid = false; - } else { - valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), p_allow_implicit_conversion); - } + if (p_target.has_container_element_type() && p_source.has_container_element_type()) { + valid = p_target.get_container_element_type() == p_source.get_container_element_type(); } } return valid; @@ -4546,7 +4747,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ return ClassDB::is_parent_class(src_native, GDScript::get_class_static()); } while (src_class != nullptr) { - if (src_class->fqcn == p_target.class_type->fqcn) { + if (src_class == p_target.class_type || src_class->fqcn == p_target.class_type->fqcn) { return true; } src_class = src_class->base_type.class_type; @@ -4646,11 +4847,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { - // Apply annotations. - for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { - resolve_annotation(E); - E->apply(parser, parser->head); - } return resolve_class_inheritance(parser->head, true); } @@ -4684,6 +4880,12 @@ Error GDScriptAnalyzer::analyze() { return err; } + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { + resolve_annotation(E); + E->apply(parser, parser->head); + } + resolve_interface(); resolve_body(); if (!parser->errors.is_empty()) { diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index cd2c4c6569..b51564fb0a 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -37,6 +37,10 @@ #include "gdscript_cache.h" #include "gdscript_parser.h" +#ifdef TOOLS_ENABLED +#include "editor/project_converter_3_to_4.h" +#endif + class GDScriptAnalyzer { GDScriptParser *parser = nullptr; HashMap<String, Ref<GDScriptParserRef>> depended_parsers; @@ -102,10 +106,13 @@ class GDScriptAnalyzer { void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); - void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const); - void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const); + Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced); + Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced); + Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced); + Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced); // Helpers. + Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr); GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type); GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const; @@ -117,7 +124,7 @@ class GDScriptAnalyzer { GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source); GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source); void update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast = false); - void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal); + void update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type); bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr); void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr); void mark_node_unsafe(const GDScriptParser::Node *p_node); @@ -125,11 +132,18 @@ class GDScriptAnalyzer { void mark_lambda_use_self(); bool class_exists(const StringName &p_class) const; Ref<GDScriptParserRef> get_parser_for(const String &p_path); - static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); + void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype); #ifdef DEBUG_ENABLED bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context); #endif +#ifdef TOOLS_ENABLED +#ifndef DISABLE_DEPRECATED + const char *get_rename_from_map(const char *map[][2], String key); + const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type); +#endif // DISABLE_DEPRECATED +#endif // TOOLS_ENABLED + public: Error resolve_inheritance(); Error resolve_interface(); @@ -137,6 +151,8 @@ public: Error resolve_dependencies(); Error analyze(); + Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable); + GDScriptAnalyzer(GDScriptParser *p_parser); }; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index e19dda090e..ec7a2b0f1c 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -826,9 +826,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta switch (p_target.type.kind) { case GDScriptDataType::BUILTIN: { if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); + append(element_type.native_type); } else { append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); append(p_target); @@ -868,9 +872,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) { if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { + const GDScriptDataType &element_type = p_target.type.get_container_element_type(); append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY); append(p_target); append(p_source); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); + append(element_type.native_type); } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) { // Need conversion. append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); @@ -1326,14 +1334,7 @@ void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_tar append(p_arguments[i]); } append(get_call_target(p_target)); - if (p_element_type.script_type) { - Variant script_type = Ref<Script>(p_element_type.script_type); - int addr = get_constant_pos(script_type); - addr |= GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS; - append(addr); - } else { - append(Address()); // null. - } + append(get_constant_pos(p_element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); append(p_arguments.size()); append(p_element_type.builtin_type); append(p_element_type.native_type); @@ -1608,14 +1609,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { // Typed array. const GDScriptDataType &element_type = function->return_type.get_container_element_type(); - - Variant script = element_type.script_type; - int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); - append(script_idx); - append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); append(element_type.native_type); } else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) { // Add conversion. @@ -1636,15 +1633,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { case GDScriptDataType::BUILTIN: { if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) { const GDScriptDataType &element_type = function->return_type.get_container_element_type(); - - Variant script = function->return_type.script_type; - int script_idx = get_constant_pos(script); - script_idx |= (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); - append(script_idx); - append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT); + append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + append(element_type.builtin_type); append(element_type.native_type); } else { append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 6acdc9f212..46cd4b0d55 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1859,7 +1859,12 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } } - gen->write_return(return_value); + if (return_n->void_return) { + // Always return "null", even if the expression is a call to a void function. + gen->write_return(codegen.add_constant(Variant())); + } else { + gen->write_return(return_value); + } if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } @@ -1904,14 +1909,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui bool initialized = false; if (lv->initializer != nullptr) { - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) { - if (local_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer); if (err) { return err; @@ -2052,14 +2049,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ // Emit proper line change. codegen.generator->write_newline(field->initializer->start_line); - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - if (field_type.has_type && field_type.builtin_type == Variant::ARRAY) { - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>()); - } - } GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true); if (r_error) { memdelete(codegen.generator); @@ -2100,17 +2089,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ return nullptr; } GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name]; - - // For typed arrays we need to make sure this is already initialized correctly so typed assignment work. - GDScriptDataType par_type = dst_addr.type; - if (par_type.has_type && par_type.builtin_type == Variant::ARRAY) { - if (par_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_addr, par_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else { - codegen.generator->write_construct_array(dst_addr, Vector<GDScriptCodeGenerator::Address>()); - } - } - codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign); if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 66374d0a6d..713ad3ed17 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -51,46 +51,8 @@ static HashMap<StringName, Variant::Type> builtin_types; Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) { if (builtin_types.is_empty()) { - builtin_types["bool"] = Variant::BOOL; - builtin_types["int"] = Variant::INT; - builtin_types["float"] = Variant::FLOAT; - builtin_types["String"] = Variant::STRING; - builtin_types["Vector2"] = Variant::VECTOR2; - builtin_types["Vector2i"] = Variant::VECTOR2I; - builtin_types["Rect2"] = Variant::RECT2; - builtin_types["Rect2i"] = Variant::RECT2I; - builtin_types["Transform2D"] = Variant::TRANSFORM2D; - builtin_types["Vector3"] = Variant::VECTOR3; - builtin_types["Vector3i"] = Variant::VECTOR3I; - builtin_types["Vector4"] = Variant::VECTOR4; - builtin_types["Vector4i"] = Variant::VECTOR4I; - builtin_types["AABB"] = Variant::AABB; - builtin_types["Plane"] = Variant::PLANE; - builtin_types["Quaternion"] = Variant::QUATERNION; - builtin_types["Basis"] = Variant::BASIS; - builtin_types["Transform3D"] = Variant::TRANSFORM3D; - builtin_types["Projection"] = Variant::PROJECTION; - builtin_types["Color"] = Variant::COLOR; - builtin_types["RID"] = Variant::RID; - builtin_types["Object"] = Variant::OBJECT; - builtin_types["StringName"] = Variant::STRING_NAME; - builtin_types["NodePath"] = Variant::NODE_PATH; - builtin_types["Dictionary"] = Variant::DICTIONARY; - builtin_types["Callable"] = Variant::CALLABLE; - builtin_types["Signal"] = Variant::SIGNAL; - builtin_types["Array"] = Variant::ARRAY; - builtin_types["PackedByteArray"] = Variant::PACKED_BYTE_ARRAY; - builtin_types["PackedInt32Array"] = Variant::PACKED_INT32_ARRAY; - builtin_types["PackedInt64Array"] = Variant::PACKED_INT64_ARRAY; - builtin_types["PackedFloat32Array"] = Variant::PACKED_FLOAT32_ARRAY; - builtin_types["PackedFloat64Array"] = Variant::PACKED_FLOAT64_ARRAY; - builtin_types["PackedStringArray"] = Variant::PACKED_STRING_ARRAY; - builtin_types["PackedVector2Array"] = Variant::PACKED_VECTOR2_ARRAY; - builtin_types["PackedVector3Array"] = Variant::PACKED_VECTOR3_ARRAY; - builtin_types["PackedColorArray"] = Variant::PACKED_COLOR_ARRAY; - // NIL is not here, hence the -1. - if (builtin_types.size() != Variant::VARIANT_MAX - 1) { - ERR_PRINT("Outdated parser: amount of built-in types don't match the amount of types in Variant."); + for (int i = 1; i < Variant::VARIANT_MAX; i++) { + builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i; } } @@ -529,7 +491,12 @@ void GDScriptParser::parse_program() { AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL); if (annotation != nullptr) { if (annotation->applies_to(AnnotationInfo::SCRIPT)) { - head->annotations.push_back(annotation); + // `@icon` needs to be applied in the parser. See GH-72444. + if (annotation->name == SNAME("@icon")) { + annotation->apply(this, head); + } else { + head->annotations.push_back(annotation); + } } else { annotation_stack.push_back(annotation); // This annotation must appear after script-level annotations @@ -847,7 +814,7 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) { if (previous.type != GDScriptTokenizer::Token::NEWLINE) { push_error(R"(Expected newline after a standalone annotation.)"); } - if (annotation->name == "@export_category" || annotation->name == "@export_group" || annotation->name == "@export_subgroup") { + if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) { current_class->add_member_group(annotation); } else { // For potential non-group standalone annotations. @@ -982,14 +949,14 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var // Run with a loop because order doesn't matter. for (int i = 0; i < 2; i++) { - if (function->name == "set") { + if (function->name == SNAME("set")) { if (setter_used) { push_error(R"(Properties can only have one setter.)"); } else { parse_property_setter(property); setter_used = true; } - } else if (function->name == "get") { + } else if (function->name == SNAME("get")) { if (getter_used) { push_error(R"(Properties can only have one getter.)"); } else { @@ -1474,7 +1441,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional. if (valid) { - valid = validate_annotation_argument_count(annotation); + valid = validate_annotation_arguments(annotation); } return valid ? annotation : nullptr; @@ -2921,7 +2888,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre // Arguments. CompletionType ct = COMPLETION_CALL_ARGUMENTS; - if (call->function_name == "load") { + if (call->function_name == SNAME("load")) { ct = COMPLETION_RESOURCE_PATH; } push_completion_call(call); @@ -3589,7 +3556,7 @@ bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const { return (info->target_kind & p_target_kinds) > 0; } -bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annotation) { +bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) { ERR_FAIL_COND_V_MSG(!valid_annotations.has(p_annotation->name), false, vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); const MethodInfo &info = valid_annotations[p_annotation->name].info; @@ -3604,6 +3571,27 @@ bool GDScriptParser::validate_annotation_argument_count(AnnotationNode *p_annota return false; } + // `@icon`'s argument needs to be resolved in the parser. See GH-72444. + if (p_annotation->name == SNAME("@icon")) { + ExpressionNode *argument = p_annotation->arguments[0]; + + if (argument->type != Node::LITERAL) { + push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); + return false; + } + + Variant value = static_cast<LiteralNode *>(argument)->value; + + if (value.get_type() != Variant::STRING) { + push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument); + return false; + } + + p_annotation->resolved_arguments.push_back(value); + } + + // For other annotations, see `GDScriptAnalyzer::resolve_annotation()`. + return true; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 74e12d0b5e..07dac25ec5 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -190,7 +190,7 @@ public: case SCRIPT: return script_type == p_other.script_type; case CLASS: - return class_type == p_other.class_type; + return class_type == p_other.class_type || class_type->fqcn == p_other.class_type->fqcn; case RESOLVING: case UNRESOLVED: break; @@ -970,6 +970,7 @@ public: struct ReturnNode : public Node { ExpressionNode *return_value = nullptr; + bool void_return = false; ReturnNode() { type = RETURN; @@ -1401,7 +1402,7 @@ private: // Annotations AnnotationNode *parse_annotation(uint32_t p_valid_targets); bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false); - bool validate_annotation_argument_count(AnnotationNode *p_annotation); + bool validate_annotation_arguments(AnnotationNode *p_annotation); void clear_unused_annotations(); bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target); bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target); diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 4ea4438b5e..e18a4a6190 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -47,6 +47,16 @@ static String _get_script_name(const Ref<Script> p_script) { } } +static String _get_element_type(Variant::Type builtin_type, const StringName &native_type, const Ref<Script> &script_type) { + if (script_type.is_valid() && script_type->is_valid()) { + return _get_script_name(script_type); + } else if (native_type != StringName()) { + return native_type.operator String(); + } else { + return Variant::get_type_name(builtin_type); + } +} + static String _get_var_type(const Variant *p_var) { String basestr; @@ -75,15 +85,8 @@ static String _get_var_type(const Variant *p_var) { basestr = "Array"; const Array *p_array = VariantInternal::get_array(p_var); Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin(); - StringName native_type = p_array->get_typed_class_name(); - Ref<Script> script_type = p_array->get_typed_script(); - - if (script_type.is_valid() && script_type->is_valid()) { - basestr += "[" + _get_script_name(script_type) + "]"; - } else if (native_type != StringName()) { - basestr += "[" + native_type.operator String() + "]"; - } else if (builtin_type != Variant::NIL) { - basestr += "[" + Variant::get_type_name(builtin_type) + "]"; + if (builtin_type != Variant::NIL) { + basestr += "[" + _get_element_type(builtin_type, p_array->get_typed_class_name(), p_array->get_typed_script()) + "]"; } } else { basestr = Variant::get_type_name(p_var->get_type()); @@ -101,10 +104,7 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT // Typed array. if (p_data_type.has_container_element_type()) { const GDScriptDataType &element_type = p_data_type.get_container_element_type(); - array.set_typed( - element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT, - element_type.native_type, - element_type.script_type); + array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type); } return array; @@ -131,6 +131,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const #ifdef DEBUG_ENABLED if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) { err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class."; + } else if (p_err.expected == Variant::ARRAY && argptrs[errorarg]->get_type() == p_err.expected) { + err_text = "Invalid type in " + p_where + ". The array of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") does not have the same element type as the expected typed array argument."; } else #endif // DEBUG_ENABLED { @@ -518,7 +520,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (!argument_types[i].is_type(*p_args[i], true)) { r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_err.argument = i; - r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT; + r_err.expected = argument_types[i].builtin_type; return _get_default_variant_for_data_type(return_type); } if (argument_types[i].kind == GDScriptDataType::BUILTIN) { @@ -1174,27 +1176,37 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a DISPATCH_OPCODE; OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) { - CHECK_SPACE(3); + CHECK_SPACE(6); GET_VARIANT_PTR(dst, 0); GET_VARIANT_PTR(src, 1); - Array *dst_arr = VariantInternal::get_array(dst); + GET_VARIANT_PTR(script_type, 2); + Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 4]; + int native_type_idx = _code_ptr[ip + 5]; + GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count); + const StringName native_type = _global_names_ptr[native_type_idx]; if (src->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) + - "' to a variable of type '" + +"'."; -#endif + err_text = vformat(R"(Trying to assign a value of type "%s" to a variable of type "Array[%s]".)", + _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - if (!dst_arr->typed_assign(*src)) { + + Array *array = VariantInternal::get_array(src); + + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { #ifdef DEBUG_ENABLED - err_text = "Trying to assign a typed array with an array of different type.'"; -#endif + err_text = vformat(R"(Trying to assign an array of type "%s" to a variable of type "Array[%s]".)", + _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - ip += 3; + *dst = *src; + + ip += 6; } DISPATCH_OPCODE; @@ -1469,9 +1481,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a const StringName native_type = _global_names_ptr[native_type_idx]; Array array; - array.set_typed(builtin_type, native_type, *script_type); array.resize(argc); - for (int i = 0; i < argc; i++) { array[i] = *(instruction_args[i]); } @@ -1479,7 +1489,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GET_INSTRUCTION_ARG(dst, argc); *dst = Variant(); // Clear potential previous typed array. - *dst = array; + *dst = Array(array, builtin_type, native_type, *script_type); ip += 4; } @@ -2486,30 +2496,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "Array[%s]".)", - Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type)); -#endif + err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Array[%s]".)", + _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } - Array array; - array.set_typed(builtin_type, native_type, *script_type); + Array *array = VariantInternal::get_array(r); + if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) { #ifdef DEBUG_ENABLED - bool valid = array.typed_assign(*VariantInternal::get_array(r)); -#else - array.typed_assign(*VariantInternal::get_array(r)); + err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)", + _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); #endif // DEBUG_ENABLED - - // Assign the return value anyway since we want it to be the valid type. - retvalue = array; - -#ifdef DEBUG_ENABLED - if (!valid) { - err_text = "Trying to return a typed array with an array of different type.'"; OPCODE_BREAK; } + retvalue = *array; + +#ifdef DEBUG_ENABLED exit_ok = true; #endif // DEBUG_ENABLED OPCODE_BREAK; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 024fed8517..9436146bed 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -125,6 +125,10 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(4); return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; } break; + case UNSAFE_VOID_RETURN: { + CHECK_SYMBOLS(2); + return "The method '" + symbols[0] + "()' returns 'void' but it's trying to return a call to '" + symbols[1] + "()' that can't be ensured to also be 'void'."; + } break; case DEPRECATED_KEYWORD: { CHECK_SYMBOLS(2); return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; @@ -163,6 +167,9 @@ String GDScriptWarning::get_message() const { CHECK_SYMBOLS(1); return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]); } + case RENAMED_IN_GD4_HINT: { + break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here. + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -184,6 +191,9 @@ int GDScriptWarning::get_default_value(Code p_code) { PropertyInfo GDScriptWarning::get_property_info(Code p_code) { // Making this a separate function in case a warning needs different PropertyInfo in the future. + if (p_code == Code::RENAMED_IN_GD4_HINT) { + return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code)); + } return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error"); } @@ -218,6 +228,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "UNSAFE_METHOD_ACCESS", "UNSAFE_CAST", "UNSAFE_CALL_ARGUMENT", + "UNSAFE_VOID_RETURN", "DEPRECATED_KEYWORD", "STANDALONE_TERNARY", "ASSERT_ALWAYS_TRUE", @@ -229,6 +240,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "INT_AS_ENUM_WITHOUT_MATCH", "STATIC_CALLED_ON_INSTANCE", "CONFUSABLE_IDENTIFIER", + "RENAMED_IN_GODOT_4_HINT" }; static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names."); diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h index 7492972c1a..fa2907cdae 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -69,6 +69,7 @@ public: UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes). UNSAFE_CAST, // Cast used in an unknown type. UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the require argument. + UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked. DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced. STANDALONE_TERNARY, // Return value of ternary expression is discarded. ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true. @@ -80,6 +81,7 @@ public: INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member. STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself. CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e"). + RENAMED_IN_GD4_HINT, // A variable or function that could not be found has been renamed in Godot 4 WARNING_MAX, }; diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index d2c8b5c317..5b8af0ff34 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -132,9 +132,10 @@ void finish_language() { StringName GDScriptTestRunner::test_function_name; -GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language) { +GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames) { test_function_name = StaticCString::create("test"); do_init_languages = p_init_language; + print_filenames = p_print_filenames; source_dir = p_source_dir; if (!source_dir.ends_with("/")) { @@ -194,6 +195,9 @@ int GDScriptTestRunner::run_tests() { int failed = 0; for (int i = 0; i < tests.size(); i++) { GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } GDScriptTest::TestResult result = test.run_test(); String expected = FileAccess::get_file_as_string(test.get_output_file()); @@ -225,8 +229,13 @@ bool GDScriptTestRunner::generate_outputs() { } for (int i = 0; i < tests.size(); i++) { - OS::get_singleton()->print("."); GDScriptTest test = tests[i]; + if (print_filenames) { + print_line(test.get_source_relative_filepath()); + } else { + OS::get_singleton()->print("."); + } + bool result = test.generate_output(); if (!result) { @@ -337,15 +346,10 @@ GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_p void GDScriptTestRunner::handle_cmdline() { List<String> cmdline_args = OS::get_singleton()->get_cmdline_args(); - // TODO: this could likely be ported to use test commands: - // https://github.com/godotengine/godot/pull/41355 - // Currently requires to startup the whole engine, which is slow. - String test_cmd = "--gdscript-test"; - String gen_cmd = "--gdscript-generate-tests"; for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) { String &cmd = E->get(); - if (cmd == test_cmd || cmd == gen_cmd) { + if (cmd == "--gdscript-generate-tests") { if (E->next() == nullptr) { ERR_PRINT("Needed a path for the test files."); exit(-1); @@ -353,14 +357,10 @@ void GDScriptTestRunner::handle_cmdline() { const String &path = E->next()->get(); - GDScriptTestRunner runner(path, false); - int failed = 0; - if (cmd == test_cmd) { - failed = runner.run_tests(); - } else { - bool completed = runner.generate_outputs(); - failed = completed ? 0 : -1; - } + GDScriptTestRunner runner(path, false, cmdline_args.find("--print-filenames") != nullptr); + + bool completed = runner.generate_outputs(); + int failed = completed ? 0 : -1; exit(failed); } } diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h index b097f1b485..60b48c6a57 100644 --- a/modules/gdscript/tests/gdscript_test_runner.h +++ b/modules/gdscript/tests/gdscript_test_runner.h @@ -92,6 +92,7 @@ public: bool generate_output(); const String &get_source_file() const { return source_file; } + const String get_source_relative_filepath() const { return source_file.trim_prefix(base_dir); } const String &get_output_file() const { return output_file; } GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir); @@ -105,6 +106,7 @@ class GDScriptTestRunner { bool is_generating = false; bool do_init_languages = false; + bool print_filenames; // Whether filenames should be printed when generated/running tests bool make_tests(); bool make_tests_for_dir(const String &p_dir); @@ -117,7 +119,7 @@ public: int run_tests(); bool generate_outputs(); - GDScriptTestRunner(const String &p_source_dir, bool p_init_language); + GDScriptTestRunner(const String &p_source_dir, bool p_init_language, bool p_print_filenames = false); ~GDScriptTestRunner(); }; diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index aed0ac2baf..e27b6218f1 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -41,7 +41,8 @@ TEST_SUITE("[Modules][GDScript]") { // Allow the tests to fail, but do not ignore errors during development. // Update the scripts and expected output as needed. TEST_CASE("Script compilation and runtime") { - GDScriptTestRunner runner("modules/gdscript/tests/scripts", true); + bool print_filenames = OS::get_singleton()->get_cmdline_args().find("--print-filenames") != nullptr; + GDScriptTestRunner runner("modules/gdscript/tests/scripts", true, print_filenames); int fail_count = runner.run_tests(); INFO("Make sure `*.out` files have expected results."); REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass."); diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out index 6f7f0783f0..015ad756f8 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Cannot get index "true" from "[0, 1]". +Invalid index type "bool" for a base of type "Array". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd new file mode 100644 index 0000000000..ce50cccb3c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd @@ -0,0 +1,4 @@ +func test(): + var differently: Array[float] = [1.0] + var typed: Array[int] = differently + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out new file mode 100644 index 0000000000..c6d39781ee --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type Array[float] to variable "typed" with specified type Array[int]. diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd index 9f86d0531c..9f86d0531c 100644 --- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out new file mode 100644 index 0000000000..8530783673 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot include a value of type "String" as "int". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd new file mode 100644 index 0000000000..25cde1d40b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd @@ -0,0 +1,4 @@ +func test(): + var unconvertable := 1 + var typed: Array[Object] = [unconvertable] + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out new file mode 100644 index 0000000000..dfe3443761 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot have an element of type "int" in an array of type "Array[Object]". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd new file mode 100644 index 0000000000..1a90bd121e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var differently: Array[float] = [1.0] + expect_typed(differently) + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out new file mode 100644 index 0000000000..297e1283e8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Invalid argument for "expect_typed()" function: argument 1 should be "Array[int]" but is "Array[float]". diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd new file mode 100644 index 0000000000..df89137f40 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.gd @@ -0,0 +1,20 @@ +func test(): + return_call() + return_nothing() + return_side_effect() + var r = return_side_effect.call() # Untyped call to check return value. + prints(r, typeof(r) == TYPE_NIL) + print("end") + +func side_effect(v): + print("effect") + return v + +func return_call() -> void: + return print("hello") + +func return_nothing() -> void: + return + +func return_side_effect() -> void: + return side_effect("x") diff --git a/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out new file mode 100644 index 0000000000..7c0416371f --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/allow_void_function_to_return_void.out @@ -0,0 +1,10 @@ +GDTEST_OK +>> WARNING +>> Line: 20 +>> UNSAFE_VOID_RETURN +>> The method 'return_side_effect()' returns 'void' but it's trying to return a call to 'side_effect()' that can't be ensured to also be 'void'. +hello +effect +effect +<null> true +end diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out index 082e3ade19..2729c5b6c7 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out @@ -2,7 +2,7 @@ GDTEST_OK [0] 0 [1] -2 +0 [2] 2 ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd new file mode 100644 index 0000000000..e1e6134fd4 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd @@ -0,0 +1,204 @@ +class A: pass +class B extends A: pass + +enum E { E0 = 391 } + +func floats_identity(floats: Array[float]): return floats + +class Members: + var one: Array[int] = [104] + var two: Array[int] = one + + func check_passing() -> bool: + assert(str(one) == '[104]') + assert(str(two) == '[104]') + two.push_back(582) + assert(str(one) == '[104, 582]') + assert(str(two) == '[104, 582]') + two = [486] + assert(str(one) == '[104, 582]') + assert(str(two) == '[486]') + return true + + +@warning_ignore("unsafe_method_access") +@warning_ignore("assert_always_true") +@warning_ignore("return_value_discarded") +func test(): + var untyped_basic = [459] + assert(str(untyped_basic) == '[459]') + assert(untyped_basic.get_typed_builtin() == TYPE_NIL) + + var inferred_basic := [366] + assert(str(inferred_basic) == '[366]') + assert(inferred_basic.get_typed_builtin() == TYPE_NIL) + + var typed_basic: Array = [521] + assert(str(typed_basic) == '[521]') + assert(typed_basic.get_typed_builtin() == TYPE_NIL) + + + var empty_floats: Array[float] = [] + assert(str(empty_floats) == '[]') + assert(empty_floats.get_typed_builtin() == TYPE_FLOAT) + + untyped_basic = empty_floats + assert(untyped_basic.get_typed_builtin() == TYPE_FLOAT) + + inferred_basic = empty_floats + assert(inferred_basic.get_typed_builtin() == TYPE_FLOAT) + + typed_basic = empty_floats + assert(typed_basic.get_typed_builtin() == TYPE_FLOAT) + + empty_floats.push_back(705.0) + untyped_basic.push_back(430.0) + inferred_basic.push_back(263.0) + typed_basic.push_back(518.0) + assert(str(empty_floats) == '[705, 430, 263, 518]') + assert(str(untyped_basic) == '[705, 430, 263, 518]') + assert(str(inferred_basic) == '[705, 430, 263, 518]') + assert(str(typed_basic) == '[705, 430, 263, 518]') + + + const constant_float := 950.0 + const constant_int := 170 + var typed_float := 954.0 + var filled_floats: Array[float] = [constant_float, constant_int, typed_float, empty_floats[1] + empty_floats[2]] + assert(str(filled_floats) == '[950, 170, 954, 693]') + assert(filled_floats.get_typed_builtin() == TYPE_FLOAT) + + var casted_floats := [empty_floats[2] * 2] as Array[float] + assert(str(casted_floats) == '[526]') + assert(casted_floats.get_typed_builtin() == TYPE_FLOAT) + + var returned_floats = (func () -> Array[float]: return [554]).call() + assert(str(returned_floats) == '[554]') + assert(returned_floats.get_typed_builtin() == TYPE_FLOAT) + + var passed_floats = floats_identity([663.0 if randf() > 0.5 else 663.0]) + assert(str(passed_floats) == '[663]') + assert(passed_floats.get_typed_builtin() == TYPE_FLOAT) + + var default_floats = (func (floats: Array[float] = [364.0]): return floats).call() + assert(str(default_floats) == '[364]') + assert(default_floats.get_typed_builtin() == TYPE_FLOAT) + + var typed_int := 556 + var converted_floats: Array[float] = [typed_int] + assert(str(converted_floats) == '[556]') + assert(converted_floats.get_typed_builtin() == TYPE_FLOAT) + + + const constant_basic = [228] + assert(str(constant_basic) == '[228]') + assert(constant_basic.get_typed_builtin() == TYPE_NIL) + + const constant_floats: Array[float] = [constant_float - constant_basic[0] - constant_int] + assert(str(constant_floats) == '[552]') + assert(constant_floats.get_typed_builtin() == TYPE_FLOAT) + + + var source_floats: Array[float] = [999.74] + untyped_basic = source_floats + var destination_floats: Array[float] = untyped_basic + destination_floats[0] -= 0.74 + assert(str(source_floats) == '[999]') + assert(str(untyped_basic) == '[999]') + assert(str(destination_floats) == '[999]') + assert(destination_floats.get_typed_builtin() == TYPE_FLOAT) + + + var duplicated_floats := empty_floats.duplicate().slice(2, 3) + duplicated_floats[0] *= 3 + assert(str(duplicated_floats) == '[789]') + assert(duplicated_floats.get_typed_builtin() == TYPE_FLOAT) + + + var b_objects: Array[B] = [B.new(), null] + assert(b_objects.size() == 2) + assert(b_objects.get_typed_builtin() == TYPE_OBJECT) + assert(b_objects.get_typed_script() == B) + + var a_objects: Array[A] = [A.new(), B.new(), null, b_objects[0]] + assert(a_objects.size() == 4) + assert(a_objects.get_typed_builtin() == TYPE_OBJECT) + assert(a_objects.get_typed_script() == A) + + var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects) + assert(a_passed == 4) + + var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects) + assert(b_passed == true) + + + var empty_strings: Array[String] = [] + var empty_bools: Array[bool] = [] + var empty_basic_one := [] + var empty_basic_two := [] + assert(empty_strings == empty_bools) + assert(empty_basic_one == empty_basic_two) + assert(empty_strings.hash() == empty_bools.hash()) + assert(empty_basic_one.hash() == empty_basic_two.hash()) + + + var assign_source: Array[int] = [527] + var assign_target: Array[int] = [] + assign_target.assign(assign_source) + assert(str(assign_source) == '[527]') + assert(str(assign_target) == '[527]') + assign_source.push_back(657) + assert(str(assign_source) == '[527, 657]') + assert(str(assign_target) == '[527]') + + + var defaults_passed = (func check_defaults_passing(one: Array[int] = [], two := one): + one.push_back(887) + two.push_back(198) + assert(str(one) == '[887, 198]') + assert(str(two) == '[887, 198]') + two = [130] + assert(str(one) == '[887, 198]') + assert(str(two) == '[130]') + return true + ).call() + assert(defaults_passed == true) + + + var members := Members.new() + var members_passed := members.check_passing() + assert(members_passed == true) + + + var resized_basic: Array = [] + resized_basic.resize(1) + assert(typeof(resized_basic[0]) == TYPE_NIL) + assert(resized_basic[0] == null) + + var resized_ints: Array[int] = [] + resized_ints.resize(1) + assert(typeof(resized_ints[0]) == TYPE_INT) + assert(resized_ints[0] == 0) + + var resized_arrays: Array[Array] = [] + resized_arrays.resize(1) + assert(typeof(resized_arrays[0]) == TYPE_ARRAY) + resized_arrays[0].resize(1) + resized_arrays[0][0] = 523 + assert(str(resized_arrays) == '[[523]]') + + var resized_objects: Array[Object] = [] + resized_objects.resize(1) + assert(typeof(resized_objects[0]) == TYPE_NIL) + assert(resized_objects[0] == null) + + + var typed_enums: Array[E] = [] + typed_enums.resize(1) + assert(str(typed_enums) == '[0]') + typed_enums[0] = E.E0 + assert(str(typed_enums) == '[391]') + assert(typed_enums.get_typed_builtin() == TYPE_INT) + + + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out deleted file mode 100644 index ad2e6558d7..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int]. diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd index 0085b3f367..2470fe978e 100644 --- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd +++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd @@ -1,5 +1,5 @@ # Error here. Annotations should be used before `class_name`, not after. -class_name HelloWorld +class_name WrongAnnotationPlace @icon("res://path/to/optional/icon.svg") func test(): diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd new file mode 100644 index 0000000000..e9dbc1b640 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd @@ -0,0 +1,4 @@ +func test(): + var basic := [1] + var typed: Array[int] = basic + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out new file mode 100644 index 0000000000..bca700b4ec --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_assign_basic_to_typed.gd +>> 3 +>> Trying to assign an array of type "Array" to a variable of type "Array[int]". diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd new file mode 100644 index 0000000000..920352a6ea --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd @@ -0,0 +1,4 @@ +func test(): + var differently: Variant = [1.0] as Array[float] + var typed: Array[int] = differently + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out new file mode 100644 index 0000000000..402ab38fb3 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_assign_differently_typed.gd +>> 3 +>> Trying to assign an array of type "Array[float]" to a variable of type "Array[int]". diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd new file mode 100644 index 0000000000..e1fd0f7168 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var basic := [1] + expect_typed(basic) + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out new file mode 100644 index 0000000000..6f210e944e --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_pass_basic_to_typed.gd +>> 6 +>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array) does not have the same element type as the expected typed array argument. diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd new file mode 100644 index 0000000000..e2d2721e8c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd @@ -0,0 +1,7 @@ +func expect_typed(typed: Array[int]): + print(typed.size()) + +func test(): + var differently: Variant = [1.0] as Array[float] + expect_typed(differently) + print('not ok') diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out new file mode 100644 index 0000000000..3cd4e25bd8 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out @@ -0,0 +1,6 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR +>> on function: test() +>> runtime/errors/typed_array_pass_differently_to_typed.gd +>> 6 +>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array[float]) does not have the same element type as the expected typed array argument. diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd new file mode 100644 index 0000000000..ec444b4ffa --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd @@ -0,0 +1,6 @@ +func test(): + var untyped: Variant = 32 + var typed: Array[int] = [untyped] + assert(typed.get_typed_builtin() == TYPE_INT) + assert(str(typed) == '[32]') + print('ok') diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 0b519bd6a3..5950ad33b5 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3065,6 +3065,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p // Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images const Array &images = p_state->json["images"]; + HashSet<String> used_names; for (int i = 0; i < images.size(); i++) { const Dictionary &d = images[i]; @@ -3092,11 +3093,21 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p int data_size = 0; String image_name; + if (d.has("name")) { + image_name = d["name"]; + image_name = image_name.get_file().get_basename().validate_filename(); + } + if (image_name.is_empty()) { + image_name = itos(i); + } + while (used_names.has(image_name)) { + image_name += "_" + itos(i); + } + used_names.insert(image_name); if (d.has("uri")) { // Handles the first two bullet points from the spec (embedded data, or external file). String uri = d["uri"]; - image_name = uri; if (uri.begins_with("data:")) { // Embedded data using base64. // Validate data MIME types and throw a warning if it's one we don't know/support. @@ -3158,7 +3169,6 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i)); const GLTFBufferViewIndex bvi = d["bufferView"]; - image_name = itos(bvi); ERR_FAIL_INDEX_V(bvi, p_state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); @@ -3206,13 +3216,12 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p p_state->source_images.push_back(Ref<Image>()); continue; } + img->set_name(image_name); if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_DISCARD_TEXTURES) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); continue; } else if (GLTFState::GLTFHandleBinary(p_state->handle_binary_image) == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) { - String extracted_image_name = image_name.get_file().get_basename().validate_filename(); - img->set_name(extracted_image_name); if (p_state->base_path.is_empty()) { p_state->images.push_back(Ref<Texture2D>()); p_state->source_images.push_back(Ref<Image>()); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs index 150eb98fc7..350626389b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs @@ -6,13 +6,17 @@ using Godot.NativeInterop; namespace Godot { /// <summary> - /// The Rid type is used to access the unique integer ID of a resource. - /// They are opaque, which means they do not grant access to the associated - /// resource by themselves. They are used by and with the low-level Server - /// classes such as <see cref="RenderingServer"/>. + /// The RID type is used to access a low-level resource by its unique ID. + /// RIDs are opaque, which means they do not grant access to the resource + /// by themselves. They are used by the low-level server classes, such as + /// <see cref="DisplayServer"/>, <see cref="RenderingServer"/>, + /// <see cref="TextServer"/>, etc. + /// + /// A low-level resource may correspond to a high-level <see cref="Resource"/>, + /// such as <see cref="Texture"/> or <see cref="Mesh"/> /// </summary> [StructLayout(LayoutKind.Sequential)] - public readonly struct Rid + public readonly struct Rid : IEquatable<Rid> { private readonly ulong _id; // Default is 0 @@ -28,15 +32,73 @@ namespace Godot => _id = from is Resource res ? res.GetRid()._id : default; /// <summary> - /// Returns the ID of the referenced resource. + /// Returns the ID of the referenced low-level resource. /// </summary> /// <returns>The ID of the referenced resource.</returns> public ulong Id => _id; /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/> is not <c>0</c>. + /// </summary> + /// <returns>Whether or not the ID is valid.</returns> + public bool IsValid => _id != 0; + + /// <summary> + /// Returns <see langword="true"/> if both <see cref="Rid"/>s are equal, + /// which means they both refer to the same low-level resource. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator ==(Rid left, Rid right) + { + return left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if the <see cref="Rid"/>s are not equal. + /// </summary> + /// <param name="left">The left RID.</param> + /// <param name="right">The right RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public static bool operator !=(Rid left, Rid right) + { + return !left.Equals(right); + } + + /// <summary> + /// Returns <see langword="true"/> if this RID and <paramref name="obj"/> are equal. + /// </summary> + /// <param name="obj">The other object to compare.</param> + /// <returns>Whether or not the color and the other object are equal.</returns> + public override readonly bool Equals(object obj) + { + return obj is Rid other && Equals(other); + } + + /// <summary> + /// Returns <see langword="true"/> if the RIDs are equal. + /// </summary> + /// <param name="other">The other RID.</param> + /// <returns>Whether or not the RIDs are equal.</returns> + public readonly bool Equals(Rid other) + { + return _id == other.Id; + } + + /// <summary> + /// Serves as the hash function for <see cref="Rid"/>. + /// </summary> + /// <returns>A hash code for this RID.</returns> + public override readonly int GetHashCode() + { + return HashCode.Combine(_id); + } + + /// <summary> /// Converts this <see cref="Rid"/> to a string. /// </summary> /// <returns>A string representation of this Rid.</returns> - public override string ToString() => $"Rid({Id})"; + public override string ToString() => $"RID({Id})"; } } diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index 7251a4a9bd..f3cc469c9d 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -11,12 +11,33 @@ <link title="Setting up XR">$DOCS_URL/tutorials/xr/setting_up_xr.html</link> </tutorials> <methods> + <method name="get_action_sets" qualifiers="const"> + <return type="Array" /> + <description> + Returns a list of action sets registered with Godot (loaded from the action map at runtime). + </description> + </method> <method name="get_available_display_refresh_rates" qualifiers="const"> <return type="Array" /> <description> Returns display refresh rates supported by the current HMD. Only returned if this feature is supported by the OpenXR runtime and after the interface has been initialized. </description> </method> + <method name="is_action_set_active" qualifiers="const"> + <return type="bool" /> + <param index="0" name="name" type="String" /> + <description> + Returns [code]true[/code] if the given action set is active. + </description> + </method> + <method name="set_action_set_active"> + <return type="void" /> + <param index="0" name="name" type="String" /> + <param index="1" name="active" type="bool" /> + <description> + Sets the given action set as active or inactive. + </description> + </method> </methods> <members> <member name="display_refresh_rate" type="float" setter="set_display_refresh_rate" getter="get_display_refresh_rate" default="0.0"> diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp index f9afdf2d4a..702e56b410 100644 --- a/modules/openxr/openxr_interface.cpp +++ b/modules/openxr/openxr_interface.cpp @@ -47,6 +47,10 @@ void OpenXRInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("set_display_refresh_rate", "refresh_rate"), &OpenXRInterface::set_display_refresh_rate); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "display_refresh_rate"), "set_display_refresh_rate", "get_display_refresh_rate"); + ClassDB::bind_method(D_METHOD("is_action_set_active", "name"), &OpenXRInterface::is_action_set_active); + ClassDB::bind_method(D_METHOD("set_action_set_active", "name", "active"), &OpenXRInterface::set_action_set_active); + ClassDB::bind_method(D_METHOD("get_action_sets"), &OpenXRInterface::get_action_sets); + ClassDB::bind_method(D_METHOD("get_available_display_refresh_rates"), &OpenXRInterface::get_available_display_refresh_rates); } @@ -621,6 +625,38 @@ Array OpenXRInterface::get_available_display_refresh_rates() const { } } +bool OpenXRInterface::is_action_set_active(const String &p_action_set) const { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + return action_set->is_active; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); + return false; +} + +void OpenXRInterface::set_action_set_active(const String &p_action_set, bool p_active) { + for (ActionSet *action_set : action_sets) { + if (action_set->action_set_name == p_action_set) { + action_set->is_active = p_active; + return; + } + } + + WARN_PRINT("OpenXR: Unknown action set " + p_action_set); +} + +Array OpenXRInterface::get_action_sets() const { + Array arr; + + for (ActionSet *action_set : action_sets) { + arr.push_back(action_set->action_set_name); + } + + return arr; +} + Size2 OpenXRInterface::get_render_target_size() { if (openxr_api == nullptr) { return Size2(); diff --git a/modules/openxr/openxr_interface.h b/modules/openxr/openxr_interface.h index 2b43369523..cce329d8e6 100644 --- a/modules/openxr/openxr_interface.h +++ b/modules/openxr/openxr_interface.h @@ -123,6 +123,10 @@ public: void set_display_refresh_rate(float p_refresh_rate); Array get_available_display_refresh_rates() const; + bool is_action_set_active(const String &p_action_set) const; + void set_action_set_active(const String &p_action_set, bool p_active); + Array get_action_sets() const; + virtual Size2 get_render_target_size() override; virtual uint32_t get_view_count() override; virtual Transform3D get_camera_transform() override; diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index e07af8f169..3ec762a880 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -9,19 +9,4 @@ </description> <tutorials> </tutorials> - <methods> - <method name="get_file"> - <return type="String" /> - <description> - Returns the Ogg Theora video file handled by this [VideoStreamTheora]. - </description> - </method> - <method name="set_file"> - <return type="void" /> - <param index="0" name="file" type="String" /> - <description> - Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension. - </description> - </method> - </methods> </class> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index c600924a3e..b38f7225a2 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -574,25 +574,10 @@ bool VideoStreamPlaybackTheora::is_paused() const { return paused; } -void VideoStreamPlaybackTheora::set_loop(bool p_enable) { -} - -bool VideoStreamPlaybackTheora::has_loop() const { - return false; -} - double VideoStreamPlaybackTheora::get_length() const { return 0; } -String VideoStreamPlaybackTheora::get_stream_name() const { - return ""; -} - -int VideoStreamPlaybackTheora::get_loop_count() const { - return 0; -} - double VideoStreamPlaybackTheora::get_playback_position() const { return get_time(); } @@ -601,11 +586,6 @@ void VideoStreamPlaybackTheora::seek(double p_time) { WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDExtension-provided video streams)."); } -void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { - mix_callback = p_callback; - mix_udata = p_userdata; -} - int VideoStreamPlaybackTheora::get_channels() const { return vi.channels; } @@ -657,16 +637,9 @@ VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() { memdelete(thread_sem); #endif clear(); -} - -void VideoStreamTheora::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file); - ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file); - - ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file"); -} +}; -//////////// +void VideoStreamTheora::_bind_methods() {} Ref<Resource> ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index f047440df7..32adc28a88 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -99,8 +99,6 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback { Ref<ImageTexture> texture; - AudioMixCallback mix_callback = nullptr; - void *mix_udata = nullptr; bool paused = false; #ifdef THEORA_USE_THREAD_STREAMING @@ -133,15 +131,8 @@ public: virtual void set_paused(bool p_paused) override; virtual bool is_paused() const override; - virtual void set_loop(bool p_enable) override; - virtual bool has_loop() const override; - virtual double get_length() const override; - virtual String get_stream_name() const; - - virtual int get_loop_count() const; - virtual double get_playback_position() const override; virtual void seek(double p_time) override; @@ -150,7 +141,6 @@ public: virtual Ref<Texture2D> get_texture() const override; virtual void update(double p_delta) override; - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override; virtual int get_channels() const override; virtual int get_mix_rate() const override; @@ -163,9 +153,6 @@ public: class VideoStreamTheora : public VideoStream { GDCLASS(VideoStreamTheora, VideoStream); - String file; - int audio_track; - protected: static void _bind_methods(); @@ -177,8 +164,6 @@ public: return pb; } - void set_file(const String &p_file) { file = p_file; } - String get_file() { return file; } void set_audio_track(int p_track) override { audio_track = p_track; } VideoStreamTheora() { audio_track = 0; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index ebd0733c55..cc230575c8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -991,15 +991,6 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi wpos.y = CLAMP(wpos.y, srect.position.y, srect.position.y + srect.size.height - wsize.height / 3); window_set_position(wpos, p_window); } - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { @@ -1077,15 +1068,6 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window AdjustWindowRectEx(&rc, style, false, exStyle); MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE); - // Don't let the mouse leave the window when moved. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT rect; - GetClientRect(wd.hWnd, &rect); - ClientToScreen(wd.hWnd, (POINT *)&rect.left); - ClientToScreen(wd.hWnd, (POINT *)&rect.right); - ClipCursor(&rect); - } - wd.last_pos = p_position; _update_real_mouse_position(p_window); } @@ -1227,15 +1209,6 @@ void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_windo } MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE); - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } Size2i DisplayServerWindows::window_get_size(WindowID p_window) const { @@ -1423,15 +1396,6 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0); } } - - // Don't let the mouse leave the window when resizing to a smaller resolution. - if (mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { - RECT crect; - GetClientRect(wd.hWnd, &crect); - ClientToScreen(wd.hWnd, (POINT *)&crect.left); - ClientToScreen(wd.hWnd, (POINT *)&crect.right); - ClipCursor(&crect); - } } DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const { @@ -3381,6 +3345,15 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA Callable::CallError ce; window.rect_changed_callback.callp(args, 1, ret, ce); } + + // Update cursor clip region after window rect has changed. + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + RECT crect; + GetClientRect(window.hWnd, &crect); + ClientToScreen(window.hWnd, (POINT *)&crect.left); + ClientToScreen(window.hWnd, (POINT *)&crect.right); + ClipCursor(&crect); + } } // Return here to prevent WM_MOVE and WM_SIZE from being sent diff --git a/platform/windows/gl_manager_windows.h b/platform/windows/gl_manager_windows.h index b97d0f667c..361c559a5a 100644 --- a/platform/windows/gl_manager_windows.h +++ b/platform/windows/gl_manager_windows.h @@ -74,7 +74,6 @@ private: GLWindow *_current_window = nullptr; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr; - PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = nullptr; // funcs void _internal_set_current_window(GLWindow *p_win); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 08299d9b98..d384049fb5 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -830,7 +830,7 @@ class FallbackTextAnalysisSource : public IDWriteTextAnalysisSource { IDWriteNumberSubstitution *n_sub = nullptr; public: - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) { + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown *)this; @@ -844,11 +844,11 @@ public: return S_OK; } - ULONG STDMETHODCALLTYPE AddRef() { + ULONG STDMETHODCALLTYPE AddRef() override { return InterlockedIncrement(&_cRef); } - ULONG STDMETHODCALLTYPE Release() { + ULONG STDMETHODCALLTYPE Release() override { ULONG ulRef = InterlockedDecrement(&_cRef); if (0 == ulRef) { delete this; diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp index 2dcf7c3a11..a37fabf21f 100644 --- a/scene/2d/area_2d.cpp +++ b/scene/2d/area_2d.cpp @@ -51,13 +51,13 @@ bool Area2D::is_gravity_a_point() const { return gravity_is_point; } -void Area2D::set_gravity_point_distance_scale(real_t p_scale) { - gravity_distance_scale = p_scale; - PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale); +void Area2D::set_gravity_point_unit_distance(real_t p_scale) { + gravity_point_unit_distance = p_scale; + PhysicsServer2D::get_singleton()->area_set_param(get_rid(), PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, p_scale); } -real_t Area2D::get_gravity_point_distance_scale() const { - return gravity_distance_scale; +real_t Area2D::get_gravity_point_unit_distance() const { + return gravity_point_unit_distance; } void Area2D::set_gravity_point_center(const Vector2 &p_center) { @@ -557,8 +557,8 @@ void Area2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area2D::set_gravity_is_point); ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area2D::is_gravity_a_point); - ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area2D::set_gravity_point_distance_scale); - ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area2D::get_gravity_point_distance_scale); + ClassDB::bind_method(D_METHOD("set_gravity_point_unit_distance", "distance_scale"), &Area2D::set_gravity_point_unit_distance); + ClassDB::bind_method(D_METHOD("get_gravity_point_unit_distance"), &Area2D::get_gravity_point_unit_distance); ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area2D::set_gravity_point_center); ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area2D::get_gravity_point_center); @@ -622,7 +622,7 @@ void Area2D::_bind_methods() { ADD_GROUP("Gravity", "gravity_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_unit_distance", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp,suffix:px"), "set_gravity_point_unit_distance", "get_gravity_point_unit_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:px"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-4096,4096,0.001,or_less,or_greater,suffix:px/s\u00B2"), "set_gravity", "get_gravity"); diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h index aaf7ea28f8..8f4bbe3219 100644 --- a/scene/2d/area_2d.h +++ b/scene/2d/area_2d.h @@ -51,7 +51,7 @@ private: Vector2 gravity_vec; real_t gravity = 0.0; bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; + real_t gravity_point_unit_distance = 0.0; SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED; SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED; @@ -144,8 +144,8 @@ public: void set_gravity_is_point(bool p_enabled); bool is_gravity_a_point() const; - void set_gravity_point_distance_scale(real_t p_scale); - real_t get_gravity_point_distance_scale() const; + void set_gravity_point_unit_distance(real_t p_scale); + real_t get_gravity_point_unit_distance() const; void set_gravity_point_center(const Vector2 &p_center); const Vector2 &get_gravity_point_center() const; diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp index 71b8fdb539..49c5501e77 100644 --- a/scene/2d/camera_2d.cpp +++ b/scene/2d/camera_2d.cpp @@ -50,7 +50,7 @@ void Camera2D::_update_scroll() { return; } - if (current) { + if (is_current()) { ERR_FAIL_COND(custom_viewport && !ObjectDB::get_instance(custom_viewport_id)); Transform2D xform = get_camera_transform(); @@ -241,10 +241,6 @@ void Camera2D::_notification(int p_what) { viewport = get_viewport(); } - if (is_current()) { - viewport->_camera_2d_set(this); - } - canvas = get_canvas(); RID vp = viewport->get_viewport_rid(); @@ -254,6 +250,10 @@ void Camera2D::_notification(int p_what) { add_to_group(group_name); add_to_group(canvas_group_name); + if (enabled && !viewport->get_camera_2d()) { + make_current(); + } + _update_process_callback(); first = true; _update_scroll(); @@ -261,11 +261,7 @@ void Camera2D::_notification(int p_what) { case NOTIFICATION_EXIT_TREE: { if (is_current()) { - if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { - viewport->set_canvas_transform(Transform2D()); - clear_current(); - current = true; - } + clear_current(); } remove_from_group(group_name); remove_from_group(canvas_group_name); @@ -397,19 +393,31 @@ void Camera2D::set_process_callback(Camera2DProcessCallback p_mode) { _update_process_callback(); } +void Camera2D::set_enabled(bool p_enabled) { + enabled = p_enabled; + + if (enabled && is_inside_tree() && !viewport->get_camera_2d()) { + make_current(); + } else if (!enabled && is_current()) { + clear_current(); + } +} + +bool Camera2D::is_enabled() const { + return enabled; +} + Camera2D::Camera2DProcessCallback Camera2D::get_process_callback() const { return process_callback; } void Camera2D::_make_current(Object *p_which) { if (p_which == this) { - current = true; if (is_inside_tree()) { get_viewport()->_camera_2d_set(this); queue_redraw(); } } else { - current = false; if (is_inside_tree()) { if (get_viewport()->get_camera_2d() == this) { get_viewport()->_camera_2d_set(nullptr); @@ -419,43 +427,32 @@ void Camera2D::_make_current(Object *p_which) { } } -void Camera2D::set_current(bool p_current) { - if (p_current) { - make_current(); - } else { - if (current) { - clear_current(); - } - } -} - void Camera2D::_update_process_internal_for_smoothing() { bool is_not_in_scene_or_editor = !(is_inside_tree() && Engine::get_singleton()->is_editor_hint()); bool is_any_smoothing_valid = position_smoothing_speed > 0 || rotation_smoothing_speed > 0; - bool enabled = is_any_smoothing_valid && is_not_in_scene_or_editor; - set_process_internal(enabled); -} - -bool Camera2D::is_current() const { - return current; + bool enable = is_any_smoothing_valid && is_not_in_scene_or_editor; + set_process_internal(enable); } void Camera2D::make_current() { - if (is_inside_tree()) { - get_tree()->call_group(group_name, "_make_current", this); - } else { - current = true; - } + ERR_FAIL_COND(!enabled || !is_inside_tree()); + get_tree()->call_group(group_name, "_make_current", this); _update_scroll(); } void Camera2D::clear_current() { - if (is_inside_tree()) { - get_tree()->call_group(group_name, "_make_current", (Object *)nullptr); - } else { - current = false; + ERR_FAIL_COND(!is_current()); + if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + viewport->assign_next_enabled_camera_2d(group_name); + } +} + +bool Camera2D::is_current() const { + if (viewport && !(custom_viewport && !ObjectDB::get_instance(custom_viewport_id))) { + return viewport->get_camera_2d() == this; } + return false; } void Camera2D::set_limit(Side p_side, int p_limit) { @@ -715,7 +712,10 @@ void Camera2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_process_callback", "mode"), &Camera2D::set_process_callback); ClassDB::bind_method(D_METHOD("get_process_callback"), &Camera2D::get_process_callback); - ClassDB::bind_method(D_METHOD("set_current", "current"), &Camera2D::set_current); + ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &Camera2D::set_enabled); + ClassDB::bind_method(D_METHOD("is_enabled"), &Camera2D::is_enabled); + + ClassDB::bind_method(D_METHOD("make_current"), &Camera2D::make_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera2D::is_current); ClassDB::bind_method(D_METHOD("_make_current"), &Camera2D::_make_current); @@ -779,7 +779,7 @@ void Camera2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_mode", PROPERTY_HINT_ENUM, "Fixed TopLeft,Drag Center"), "set_anchor_mode", "get_anchor_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_rotation"), "set_ignore_rotation", "is_ignoring_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "zoom", PROPERTY_HINT_LINK), "set_zoom", "get_zoom"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback"); diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h index 304b4ceaa6..7a77266db8 100644 --- a/scene/2d/camera_2d.h +++ b/scene/2d/camera_2d.h @@ -64,7 +64,7 @@ protected: Vector2 zoom_scale = Vector2(1, 1); AnchorMode anchor_mode = ANCHOR_MODE_DRAG_CENTER; bool ignore_rotation = true; - bool current = false; + bool enabled = true; real_t position_smoothing_speed = 5.0; bool follow_smoothing_enabled = false; @@ -88,7 +88,6 @@ protected: void _update_scroll(); void _make_current(Object *p_which); - void set_current(bool p_current); void _set_old_smoothing(real_t p_enable); @@ -155,6 +154,9 @@ public: void set_process_callback(Camera2DProcessCallback p_mode); Camera2DProcessCallback get_process_callback() const; + void set_enabled(bool p_enabled); + bool is_enabled() const; + void make_current(); void clear_current(); bool is_current() const; diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index 72f186c676..5901e38bb4 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -51,13 +51,13 @@ bool Area3D::is_gravity_a_point() const { return gravity_is_point; } -void Area3D::set_gravity_point_distance_scale(real_t p_scale) { - gravity_distance_scale = p_scale; - PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE, p_scale); +void Area3D::set_gravity_point_unit_distance(real_t p_scale) { + gravity_point_unit_distance = p_scale; + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, p_scale); } -real_t Area3D::get_gravity_point_distance_scale() const { - return gravity_distance_scale; +real_t Area3D::get_gravity_point_unit_distance() const { + return gravity_point_unit_distance; } void Area3D::set_gravity_point_center(const Vector3 &p_center) { @@ -655,8 +655,8 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gravity_is_point", "enable"), &Area3D::set_gravity_is_point); ClassDB::bind_method(D_METHOD("is_gravity_a_point"), &Area3D::is_gravity_a_point); - ClassDB::bind_method(D_METHOD("set_gravity_point_distance_scale", "distance_scale"), &Area3D::set_gravity_point_distance_scale); - ClassDB::bind_method(D_METHOD("get_gravity_point_distance_scale"), &Area3D::get_gravity_point_distance_scale); + ClassDB::bind_method(D_METHOD("set_gravity_point_unit_distance", "distance_scale"), &Area3D::set_gravity_point_unit_distance); + ClassDB::bind_method(D_METHOD("get_gravity_point_unit_distance"), &Area3D::get_gravity_point_unit_distance); ClassDB::bind_method(D_METHOD("set_gravity_point_center", "center"), &Area3D::set_gravity_point_center); ClassDB::bind_method(D_METHOD("get_gravity_point_center"), &Area3D::get_gravity_point_center); @@ -741,7 +741,7 @@ void Area3D::_bind_methods() { ADD_GROUP("Gravity", "gravity_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gravity_space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_space_override_mode", "get_gravity_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_point_distance_scale", "get_gravity_point_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_point_unit_distance", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp,suffix:m"), "set_gravity_point_unit_distance", "get_gravity_point_unit_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_point_center", PROPERTY_HINT_NONE, "suffix:m"), "set_gravity_point_center", "get_gravity_point_center"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_direction"), "set_gravity_direction", "get_gravity_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, U"-32,32,0.001,or_less,or_greater,suffix:m/s\u00B2"), "set_gravity", "get_gravity"); diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 91b91f741a..607e0d2af8 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -51,7 +51,7 @@ private: Vector3 gravity_vec; real_t gravity = 0.0; bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; + real_t gravity_point_unit_distance = 0.0; SpaceOverride linear_damp_space_override = SPACE_OVERRIDE_DISABLED; SpaceOverride angular_damp_space_override = SPACE_OVERRIDE_DISABLED; @@ -155,8 +155,8 @@ public: void set_gravity_is_point(bool p_enabled); bool is_gravity_a_point() const; - void set_gravity_point_distance_scale(real_t p_scale); - real_t get_gravity_point_distance_scale() const; + void set_gravity_point_unit_distance(real_t p_scale); + real_t get_gravity_point_unit_distance() const; void set_gravity_point_center(const Vector3 &p_center); const Vector3 &get_gravity_point_center() const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index fe7f6837f0..ba5ff02862 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -81,11 +81,6 @@ bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const { } void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { - p_list->push_back(PropertyInfo(Variant::BOOL, "override_pose", PROPERTY_HINT_NONE, "")); - if (override_pose) { - p_list->push_back(PropertyInfo(Variant::INT, "override_mode", PROPERTY_HINT_ENUM, "Global Pose Override,Local Pose Override,Custom Pose")); - } - p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton", PROPERTY_HINT_NONE, "")); if (use_external_skeleton) { p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D")); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 6a9b8c9ac4..b39ca43d2e 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -112,6 +112,12 @@ void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &Label3D::set_alpha_hash_scale); ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &Label3D::get_alpha_hash_scale); + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &Label3D::set_alpha_antialiasing); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &Label3D::get_alpha_antialiasing); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &Label3D::set_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &Label3D::get_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("set_texture_filter", "mode"), &Label3D::set_texture_filter); ClassDB::bind_method(D_METHOD("get_texture_filter"), &Label3D::get_texture_filter); @@ -133,6 +139,8 @@ void Label3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash"), "set_alpha_cut_mode", "get_alpha_cut_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_outline_render_priority", "get_outline_render_priority"); @@ -356,6 +364,7 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, RS::get_singleton()->material_set_param(surf.material, "uv2_scale", Vector3(1, 1, 1)); RS::get_singleton()->material_set_param(surf.material, "alpha_scissor_threshold", alpha_scissor_threshold); RS::get_singleton()->material_set_param(surf.material, "alpha_hash_scale", alpha_hash_scale); + RS::get_singleton()->material_set_param(surf.material, "alpha_antialiasing_edge", alpha_antialiasing_edge); if (msdf) { RS::get_singleton()->material_set_param(surf.material, "msdf_pixel_range", TS->font_get_msdf_pixel_range(p_glyph.font_rid)); RS::get_singleton()->material_set_param(surf.material, "msdf_outline_size", p_outline_size); @@ -371,7 +380,7 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, } RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, msdf, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), texture_filter, alpha_antialiasing_mode, &shader_rid); RS::get_singleton()->material_set_shader(surf.material, shader_rid); RS::get_singleton()->material_set_param(surf.material, "texture_albedo", tex); @@ -966,6 +975,28 @@ float Label3D::get_alpha_scissor_threshold() const { return alpha_scissor_threshold; } +void Label3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { + if (alpha_antialiasing_mode != p_alpha_aa) { + alpha_antialiasing_mode = p_alpha_aa; + _queue_update(); + } +} + +BaseMaterial3D::AlphaAntiAliasing Label3D::get_alpha_antialiasing() const { + return alpha_antialiasing_mode; +} + +void Label3D::set_alpha_antialiasing_edge(float p_edge) { + if (alpha_antialiasing_edge != p_edge) { + alpha_antialiasing_edge = p_edge; + _queue_update(); + } +} + +float Label3D::get_alpha_antialiasing_edge() const { + return alpha_antialiasing_edge; +} + Label3D::Label3D() { for (int i = 0; i < FLAG_MAX; i++) { flags[i] = (i == FLAG_DOUBLE_SIDED); diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 576735840a..912f485354 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -62,6 +62,8 @@ private: AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; float alpha_scissor_threshold = 0.5; float alpha_hash_scale = 1.0; + StandardMaterial3D::AlphaAntiAliasing alpha_antialiasing_mode = StandardMaterial3D::ALPHA_ANTIALIASING_OFF; + float alpha_antialiasing_edge = 0.0f; AABB aabb; @@ -234,6 +236,12 @@ public: void set_alpha_hash_scale(float p_hash_scale); float get_alpha_hash_scale() const; + void set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa); + BaseMaterial3D::AlphaAntiAliasing get_alpha_antialiasing() const; + + void set_alpha_antialiasing_edge(float p_edge); + float get_alpha_antialiasing_edge() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 041ca7707b..59e4a0b718 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -247,6 +247,7 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, RS::get_singleton()->material_set_param(get_material(), "alpha_scissor_threshold", alpha_scissor_threshold); RS::get_singleton()->material_set_param(get_material(), "alpha_hash_scale", alpha_hash_scale); + RS::get_singleton()->material_set_param(get_material(), "alpha_antialiasing_edge", alpha_antialiasing_edge); BaseMaterial3D::Transparency mat_transparency = BaseMaterial3D::Transparency::TRANSPARENCY_DISABLED; if (get_draw_flag(FLAG_TRANSPARENT)) { @@ -262,7 +263,7 @@ void SpriteBase3D::draw_texture_rect(Ref<Texture2D> p_texture, Rect2 p_dst_rect, } RID shader_rid; - StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), &shader_rid); + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), mat_transparency, get_draw_flag(FLAG_DOUBLE_SIDED), get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, false, get_draw_flag(FLAG_DISABLE_DEPTH_TEST), get_draw_flag(FLAG_FIXED_SIZE), get_texture_filter(), alpha_antialiasing_mode, &shader_rid); if (last_shader != shader_rid) { RS::get_singleton()->material_set_shader(get_material(), shader_rid); @@ -481,6 +482,28 @@ float SpriteBase3D::get_alpha_scissor_threshold() const { return alpha_scissor_threshold; } +void SpriteBase3D::set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa) { + if (alpha_antialiasing_mode != p_alpha_aa) { + alpha_antialiasing_mode = p_alpha_aa; + _queue_redraw(); + } +} + +BaseMaterial3D::AlphaAntiAliasing SpriteBase3D::get_alpha_antialiasing() const { + return alpha_antialiasing_mode; +} + +void SpriteBase3D::set_alpha_antialiasing_edge(float p_edge) { + if (alpha_antialiasing_edge != p_edge) { + alpha_antialiasing_edge = p_edge; + _queue_redraw(); + } +} + +float SpriteBase3D::get_alpha_antialiasing_edge() const { + return alpha_antialiasing_edge; +} + void SpriteBase3D::set_billboard_mode(StandardMaterial3D::BillboardMode p_mode) { ERR_FAIL_INDEX(p_mode, 3); // Cannot use BILLBOARD_PARTICLES. billboard_mode = p_mode; @@ -539,6 +562,12 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alpha_hash_scale", "threshold"), &SpriteBase3D::set_alpha_hash_scale); ClassDB::bind_method(D_METHOD("get_alpha_hash_scale"), &SpriteBase3D::get_alpha_hash_scale); + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing", "alpha_aa"), &SpriteBase3D::set_alpha_antialiasing); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing"), &SpriteBase3D::get_alpha_antialiasing); + + ClassDB::bind_method(D_METHOD("set_alpha_antialiasing_edge", "edge"), &SpriteBase3D::set_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("get_alpha_antialiasing_edge"), &SpriteBase3D::get_alpha_antialiasing_edge); + ClassDB::bind_method(D_METHOD("set_billboard_mode", "mode"), &SpriteBase3D::set_billboard_mode); ClassDB::bind_method(D_METHOD("get_billboard_mode"), &SpriteBase3D::get_billboard_mode); @@ -567,6 +596,8 @@ void SpriteBase3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_cut", PROPERTY_HINT_ENUM, "Disabled,Discard,Opaque Pre-Pass,Alpha Hash"), "set_alpha_cut_mode", "get_alpha_cut_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge"); ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Anisotropic,Linear Mipmap Anisotropic"), "set_texture_filter", "get_texture_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "render_priority", PROPERTY_HINT_RANGE, itos(RS::MATERIAL_RENDER_PRIORITY_MIN) + "," + itos(RS::MATERIAL_RENDER_PRIORITY_MAX) + ",1"), "set_render_priority", "get_render_priority"); diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index 873c321878..1eb1211951 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -89,6 +89,8 @@ private: AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; float alpha_scissor_threshold = 0.5; float alpha_hash_scale = 1.0; + StandardMaterial3D::AlphaAntiAliasing alpha_antialiasing_mode = StandardMaterial3D::ALPHA_ANTIALIASING_OFF; + float alpha_antialiasing_edge = 0.0f; StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; StandardMaterial3D::TextureFilter texture_filter = StandardMaterial3D::TEXTURE_FILTER_LINEAR_WITH_MIPMAPS; bool pending_update = false; @@ -153,6 +155,12 @@ public: void set_alpha_hash_scale(float p_hash_scale); float get_alpha_hash_scale() const; + void set_alpha_antialiasing(BaseMaterial3D::AlphaAntiAliasing p_alpha_aa); + BaseMaterial3D::AlphaAntiAliasing get_alpha_antialiasing() const; + + void set_alpha_antialiasing_edge(float p_edge); + float get_alpha_antialiasing_edge() const; + void set_billboard_mode(StandardMaterial3D::BillboardMode p_mode); StandardMaterial3D::BillboardMode get_billboard_mode() const; diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index a2028b8de8..d28a6fcc04 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -30,12 +30,20 @@ #include "animation_blend_space_1d.h" +#include "animation_blend_tree.h" + void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { r_list->push_back(PropertyInfo(Variant::FLOAT, blend_position)); + r_list->push_back(PropertyInfo(Variant::INT, closest, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); + r_list->push_back(PropertyInfo(Variant::FLOAT, length_internal, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE)); } Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { - return 0; + if (p_parameter == closest) { + return -1; + } else { + return 0; + } } Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { @@ -77,6 +85,9 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); + ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &AnimationNodeBlendSpace1D::set_blend_mode); + ClassDB::bind_method(D_METHOD("get_blend_mode"), &AnimationNodeBlendSpace1D::get_blend_mode); + ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlendSpace1D::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlendSpace1D::is_using_sync); @@ -91,7 +102,12 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_snap", "get_snap"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_value_label", "get_value_label"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Interpolated,Discrete,Carry", PROPERTY_USAGE_NO_EDITOR), "set_blend_mode", "get_blend_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_use_sync", "is_using_sync"); + + BIND_ENUM_CONSTANT(BLEND_MODE_INTERPOLATED); + BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE); + BIND_ENUM_CONSTANT(BLEND_MODE_DISCRETE_CARRY); } void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { @@ -214,6 +230,14 @@ String AnimationNodeBlendSpace1D::get_value_label() const { return value_label; } +void AnimationNodeBlendSpace1D::set_blend_mode(BlendMode p_blend_mode) { + blend_mode = p_blend_mode; +} + +AnimationNodeBlendSpace1D::BlendMode AnimationNodeBlendSpace1D::get_blend_mode() const { + return blend_mode; +} + void AnimationNodeBlendSpace1D::set_use_sync(bool p_sync) { sync = p_sync; } @@ -241,79 +265,125 @@ double AnimationNodeBlendSpace1D::process(double p_time, bool p_seek, bool p_is_ } double blend_pos = get_parameter(blend_position); + int cur_closest = get_parameter(closest); + double cur_length_internal = get_parameter(length_internal); + double max_time_remaining = 0.0; - float weights[MAX_BLEND_POINTS] = {}; + if (blend_mode == BLEND_MODE_INTERPOLATED) { + float weights[MAX_BLEND_POINTS] = {}; + + int point_lower = -1; + float pos_lower = 0.0; + int point_higher = -1; + float pos_higher = 0.0; + + // find the closest two points to blend between + for (int i = 0; i < blend_points_used; i++) { + float pos = blend_points[i].position; + + if (pos <= blend_pos) { + if (point_lower == -1) { + point_lower = i; + pos_lower = pos; + } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { + point_lower = i; + pos_lower = pos; + } + } else { + if (point_higher == -1) { + point_higher = i; + pos_higher = pos; + } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { + point_higher = i; + pos_higher = pos; + } + } + } - int point_lower = -1; - float pos_lower = 0.0; - int point_higher = -1; - float pos_higher = 0.0; + // fill in weights - // find the closest two points to blend between - for (int i = 0; i < blend_points_used; i++) { - float pos = blend_points[i].position; - - if (pos <= blend_pos) { - if (point_lower == -1) { - point_lower = i; - pos_lower = pos; - } else if ((blend_pos - pos) < (blend_pos - pos_lower)) { - point_lower = i; - pos_lower = pos; - } + if (point_lower == -1 && point_higher != -1) { + // we are on the left side, no other point to the left + // we just play the next point. + + weights[point_higher] = 1.0; + } else if (point_higher == -1) { + // we are on the right side, no other point to the right + // we just play the previous point + + weights[point_lower] = 1.0; } else { - if (point_higher == -1) { - point_higher = i; - pos_higher = pos; - } else if ((pos - blend_pos) < (pos_higher - blend_pos)) { - point_higher = i; - pos_higher = pos; - } - } - } + // we are between two points. + // figure out weights, then blend the animations - // fill in weights + float distance_between_points = pos_higher - pos_lower; - if (point_lower == -1 && point_higher != -1) { - // we are on the left side, no other point to the left - // we just play the next point. + float current_pos_inbetween = blend_pos - pos_lower; - weights[point_higher] = 1.0; - } else if (point_higher == -1) { - // we are on the right side, no other point to the right - // we just play the previous point + float blend_percentage = current_pos_inbetween / distance_between_points; - weights[point_lower] = 1.0; - } else { - // we are between two points. - // figure out weights, then blend the animations + float blend_lower = 1.0 - blend_percentage; + float blend_higher = blend_percentage; - float distance_between_points = pos_higher - pos_lower; + weights[point_lower] = blend_lower; + weights[point_higher] = blend_higher; + } - float current_pos_inbetween = blend_pos - pos_lower; + // actually blend the animations now - float blend_percentage = current_pos_inbetween / distance_between_points; + for (int i = 0; i < blend_points_used; i++) { + if (i == point_lower || i == point_higher) { + double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); + max_time_remaining = MAX(max_time_remaining, remaining); + } else if (sync) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } + } + } else { + int new_closest = -1; + double new_closest_dist = 1e20; + + for (int i = 0; i < blend_points_used; i++) { + double d = abs(blend_points[i].position - blend_pos); + if (d < new_closest_dist) { + new_closest = i; + new_closest_dist = d; + } + } - float blend_lower = 1.0 - blend_percentage; - float blend_higher = blend_percentage; + if (new_closest != cur_closest && new_closest != -1) { + double from = 0.0; + if (blend_mode == BLEND_MODE_DISCRETE_CARRY && cur_closest != -1) { + //for ping-pong loop + Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node); + Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node); + if (!na_c.is_null() && !na_n.is_null()) { + na_n->set_backward(na_c->is_backward()); + } + //see how much animation remains + from = cur_length_internal - blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, false, p_is_external_seeking, 0.0, FILTER_IGNORE, true); + } - weights[point_lower] = blend_lower; - weights[point_higher] = blend_higher; - } + max_time_remaining = blend_node(blend_points[new_closest].name, blend_points[new_closest].node, from, true, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + cur_length_internal = from + max_time_remaining; - // actually blend the animations now + cur_closest = new_closest; - double max_time_remaining = 0.0; + } else { + max_time_remaining = blend_node(blend_points[cur_closest].name, blend_points[cur_closest].node, p_time, p_seek, p_is_external_seeking, 1.0, FILTER_IGNORE, true); + } - for (int i = 0; i < blend_points_used; i++) { - if (i == point_lower || i == point_higher) { - double remaining = blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, weights[i], FILTER_IGNORE, true); - max_time_remaining = MAX(max_time_remaining, remaining); - } else if (sync) { - blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + if (sync) { + for (int i = 0; i < blend_points_used; i++) { + if (i != cur_closest) { + blend_node(blend_points[i].name, blend_points[i].node, p_time, p_seek, p_is_external_seeking, 0, FILTER_IGNORE, true); + } + } } } + set_parameter(this->closest, cur_closest); + set_parameter(this->length_internal, cur_length_internal); return max_time_remaining; } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index af93783c0d..a1e9a7a764 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -36,6 +36,14 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { GDCLASS(AnimationNodeBlendSpace1D, AnimationRootNode); +public: + enum BlendMode { + BLEND_MODE_INTERPOLATED, + BLEND_MODE_DISCRETE, + BLEND_MODE_DISCRETE_CARRY, + }; + +protected: enum { MAX_BLEND_POINTS = 64 }; @@ -61,6 +69,10 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void _tree_changed(); StringName blend_position = "blend_position"; + StringName closest = "closest"; + StringName length_internal = "length_internal"; + + BlendMode blend_mode = BLEND_MODE_INTERPOLATED; protected: bool sync = false; @@ -95,6 +107,9 @@ public: void set_value_label(const String &p_label); String get_value_label() const; + void set_blend_mode(BlendMode p_blend_mode); + BlendMode get_blend_mode() const; + void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -107,4 +122,6 @@ public: ~AnimationNodeBlendSpace1D(); }; +VARIANT_ENUM_CAST(AnimationNodeBlendSpace1D::BlendMode) + #endif // ANIMATION_BLEND_SPACE_1D_H diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 1c1f94c986..fa72bbc593 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -1861,6 +1861,8 @@ void AnimationTree::_setup_animation_player() { return; } + cache_valid = false; + AnimationPlayer *new_player = nullptr; if (!animation_player.is_empty()) { new_player = Object::cast_to<AnimationPlayer>(get_node_or_null(animation_player)); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index f7c056316d..6f5e2cf058 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -692,6 +692,12 @@ Transform2D Control::get_transform() const { return xform; } +void Control::_toplevel_changed_on_parent() { + // Update root control status. + _notification(NOTIFICATION_EXIT_CANVAS); + _notification(NOTIFICATION_ENTER_CANVAS); +} + /// Anchors and offsets. void Control::_set_anchor(Side p_side, real_t p_anchor) { diff --git a/scene/gui/control.h b/scene/gui/control.h index a93a88e5b4..5977f4dbea 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -292,6 +292,9 @@ private: void _update_minimum_size(); void _size_changed(); + void _toplevel_changed() override{}; // Controls don't need to do anything, only other CanvasItems. + void _toplevel_changed_on_parent() override; + void _clear_size_warning(); // Input events. diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 6eb25bf852..1f3bbff779 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -236,7 +236,6 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { AudioServer::get_singleton()->unlock(); if (!playback.is_null()) { - playback->set_loop(loops); playback->set_paused(paused); texture = playback->get_texture(); @@ -344,6 +343,12 @@ int VideoStreamPlayer::get_buffering_msec() const { void VideoStreamPlayer::set_audio_track(int p_track) { audio_track = p_track; + if (stream.is_valid()) { + stream->set_audio_track(audio_track); + } + if (playback.is_valid()) { + playback->set_audio_track(audio_track); + } } int VideoStreamPlayer::get_audio_track() const { diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h index 09ef272a9a..1fd599a9e1 100644 --- a/scene/gui/video_stream_player.h +++ b/scene/gui/video_stream_player.h @@ -65,7 +65,6 @@ class VideoStreamPlayer : public Control { float volume = 1.0; double last_audio_time = 0.0; bool expand = false; - bool loops = false; int buffering_ms = 500; int audio_track = 0; int bus_index = 0; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 35176f0edd..cde3503bdf 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -400,11 +400,28 @@ void CanvasItem::set_as_top_level(bool p_top_level) { _exit_canvas(); top_level = p_top_level; + _toplevel_changed(); _enter_canvas(); _notify_transform(); } +void CanvasItem::_toplevel_changed() { + // Inform children that toplevel status has changed on a parent. + int childs = get_child_count(); + for (int i = 0; i < childs; i++) { + CanvasItem *child = Object::cast_to<CanvasItem>(get_child(i)); + if (child) { + child->_toplevel_changed_on_parent(); + } + } +} + +void CanvasItem::_toplevel_changed_on_parent() { + // Inform children that toplevel status has changed on a parent. + _toplevel_changed(); +} + bool CanvasItem::is_set_as_top_level() const { return top_level; } diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index 1c84ea338a..1ddfaa288c 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -125,6 +125,9 @@ private: void _propagate_visibility_changed(bool p_parent_visible_in_tree); void _handle_visibility_change(bool p_visible); + virtual void _toplevel_changed(); + virtual void _toplevel_changed_on_parent(); + void _redraw_callback(); void _enter_canvas(); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 48cff5aa8e..b6bcbe04a3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1045,6 +1045,25 @@ Transform2D Viewport::get_final_transform() const { return _get_input_pre_xform().affine_inverse() * stretch_transform * global_canvas_transform; } +void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) { + List<Node *> camera_list; + get_tree()->get_nodes_in_group(p_camera_group, &camera_list); + + Camera2D *new_camera = nullptr; + for (const Node *E : camera_list) { + const Camera2D *cam = Object::cast_to<Camera2D>(E); + if (cam->is_enabled()) { + new_camera = const_cast<Camera2D *>(cam); + break; + } + } + + _camera_2d_set(new_camera); + if (!camera_2d) { + set_canvas_transform(Transform2D()); + } +} + void Viewport::_update_canvas_items(Node *p_node) { if (p_node != this) { Window *w = Object::cast_to<Window>(p_node); @@ -1110,14 +1129,11 @@ Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_at } Transform2D Viewport::_get_input_pre_xform() const { - Transform2D pre_xf; - - if (to_screen_rect.size.x != 0 && to_screen_rect.size.y != 0) { - pre_xf.columns[2] = -to_screen_rect.position; - pre_xf.scale(Vector2(size) / to_screen_rect.size); + const Window *this_window = Object::cast_to<Window>(this); + if (this_window) { + return this_window->window_transform.affine_inverse(); } - - return pre_xf; + return Transform2D(); } Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) { diff --git a/scene/main/viewport.h b/scene/main/viewport.h index d5d5201e9a..e081013fde 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -511,6 +511,7 @@ public: Transform2D get_global_canvas_transform() const; Transform2D get_final_transform() const; + void assign_next_enabled_camera_2d(const StringName &p_camera_group); void gui_set_root_order_dirty(); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 869d12b4df..b1f2bc65dc 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -907,6 +907,7 @@ void Window::_update_viewport_size() { Rect2i attach_to_screen_rect(Point2i(), size); Transform2D stretch_transform_new; float font_oversampling = 1.0; + window_transform = Transform2D(); if (content_scale_mode == CONTENT_SCALE_MODE_DISABLED || content_scale_size.x == 0 || content_scale_size.y == 0) { font_oversampling = content_scale_factor; @@ -993,11 +994,18 @@ void Window::_update_viewport_size() { Size2 scale = Vector2(screen_size) / Vector2(final_size_override); stretch_transform_new.scale(scale); + window_transform.translate_local(margin); } break; case CONTENT_SCALE_MODE_VIEWPORT: { final_size = (viewport_size / content_scale_factor).floor(); attach_to_screen_rect = Rect2(margin, screen_size); + window_transform.translate_local(margin); + if (final_size.x != 0 && final_size.y != 0) { + Transform2D scale_transform; + scale_transform.scale(Vector2(attach_to_screen_rect.size) / Vector2(final_size)); + window_transform *= scale_transform; + } } break; } } @@ -2127,13 +2135,13 @@ Transform2D Window::get_popup_base_transform() const { if (is_embedding_subwindows()) { return Transform2D(); } - Transform2D window_transform; - window_transform.set_origin(get_position()); - window_transform *= Viewport::get_screen_transform(); + Transform2D popup_base_transform; + popup_base_transform.set_origin(get_position()); + popup_base_transform *= Viewport::get_screen_transform(); if (_get_embedder()) { - return _get_embedder()->get_popup_base_transform() * window_transform; + return _get_embedder()->get_popup_base_transform() * popup_base_transform; } - return window_transform; + return popup_base_transform; } void Window::_bind_methods() { diff --git a/scene/main/window.h b/scene/main/window.h index 182caf5f0c..1730de0b33 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -173,6 +173,8 @@ private: Viewport *embedder = nullptr; + Transform2D window_transform; + friend class Viewport; //friend back, can call the methods below void _window_input(const Ref<InputEvent> &p_ev); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 7bebf1cfd3..39fc03f9f1 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -393,6 +393,8 @@ void register_scene_types() { GDREGISTER_CLASS(LineEdit); GDREGISTER_CLASS(VideoStreamPlayer); + GDREGISTER_VIRTUAL_CLASS(VideoStreamPlayback); + GDREGISTER_VIRTUAL_CLASS(VideoStream); #ifndef ADVANCED_GUI_DISABLED GDREGISTER_CLASS(FileDialog); @@ -906,7 +908,6 @@ void register_scene_types() { #ifndef _3D_DISABLED GDREGISTER_CLASS(AudioStreamPlayer3D); #endif - GDREGISTER_ABSTRACT_CLASS(VideoStream); GDREGISTER_CLASS(AudioStreamWAV); GDREGISTER_CLASS(AudioStreamPolyphonic); GDREGISTER_ABSTRACT_CLASS(AudioStreamPlaybackPolyphonic); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 55b633a40c..742ac2bbd9 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -1058,6 +1058,8 @@ struct EditorSceneFormatImporterMeshLightmapSurface { String name; }; +static const uint32_t custom_shift[RS::ARRAY_CUSTOM_COUNT] = { Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM1_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM2_SHIFT, Mesh::ARRAY_FORMAT_CUSTOM3_SHIFT }; + Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) { ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED); ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes."); @@ -1178,9 +1180,6 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, return ERR_CANT_CREATE; } - //remove surfaces - clear(); - //create surfacetools for each surface.. LocalVector<Ref<SurfaceTool>> surfaces_tools; @@ -1190,9 +1189,16 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, st->begin(Mesh::PRIMITIVE_TRIANGLES); st->set_material(lightmap_surfaces[i].material); st->set_meta("name", lightmap_surfaces[i].name); + + for (int custom_i = 0; custom_i < RS::ARRAY_CUSTOM_COUNT; custom_i++) { + st->set_custom_format(custom_i, (SurfaceTool::CustomFormat)((lightmap_surfaces[i].format >> custom_shift[custom_i]) & RS::ARRAY_FORMAT_CUSTOM_MASK)); + } surfaces_tools.push_back(st); //stay there } + //remove surfaces + clear(); + print_verbose("Mesh: Gen indices: " + itos(gen_index_count)); //go through all indices @@ -1229,6 +1235,11 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) { surfaces_tools[surface]->set_weights(v.weights); } + for (int custom_i = 0; custom_i < RS::ARRAY_CUSTOM_COUNT; custom_i++) { + if ((lightmap_surfaces[surface].format >> custom_shift[custom_i]) & RS::ARRAY_FORMAT_CUSTOM_MASK) { + surfaces_tools[surface]->set_custom(custom_i, v.custom[custom_i]); + } + } Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]); surfaces_tools[surface]->set_uv2(uv2); @@ -1238,10 +1249,11 @@ Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, } //generate surfaces - for (Ref<SurfaceTool> &tool : surfaces_tools) { + for (int i = 0; i < lightmap_surfaces.size(); i++) { + Ref<SurfaceTool> &tool = surfaces_tools[i]; tool->index(); Array arrays = tool->commit_to_arrays(); - add_surface(tool->get_primitive_type(), arrays, Array(), Dictionary(), tool->get_material(), tool->get_meta("name")); + add_surface(tool->get_primitive_type(), arrays, Array(), Dictionary(), tool->get_material(), tool->get_meta("name"), lightmap_surfaces[i].format); } set_lightmap_size_hint(Size2(size_x, size_y)); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index d6393966b1..7e84814ab3 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -2316,7 +2316,7 @@ BaseMaterial3D::TextureChannel BaseMaterial3D::get_refraction_texture_channel() return refraction_texture_channel; } -Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, RID *r_shader_rid) { +Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard, bool p_billboard_y, bool p_msdf, bool p_no_depth, bool p_fixed_size, TextureFilter p_filter, AlphaAntiAliasing p_alpha_antialiasing_mode, RID *r_shader_rid) { uint64_t key = 0; key |= ((int8_t)p_shaded & 0x01) << 0; key |= ((int8_t)p_transparency & 0x07) << 1; // Bits 1-3. @@ -2326,7 +2326,8 @@ Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, Transparency p_ key |= ((int8_t)p_msdf & 0x01) << 7; key |= ((int8_t)p_no_depth & 0x01) << 8; key |= ((int8_t)p_fixed_size & 0x01) << 9; - key |= ((int8_t)p_filter & 0x07) << 10; // Bits 10-13. + key |= ((int8_t)p_filter & 0x07) << 10; // Bits 10-12. + key |= ((int8_t)p_alpha_antialiasing_mode & 0x07) << 13; // Bits 13-15. if (materials_for_2d.has(key)) { if (r_shader_rid) { @@ -2346,6 +2347,7 @@ Ref<Material> BaseMaterial3D::get_material_for_2d(bool p_shaded, Transparency p_ material->set_flag(FLAG_ALBEDO_TEXTURE_MSDF, p_msdf); material->set_flag(FLAG_DISABLE_DEPTH_TEST, p_no_depth); material->set_flag(FLAG_FIXED_SIZE, p_fixed_size); + material->set_alpha_antialiasing(p_alpha_antialiasing_mode); material->set_texture_filter(p_filter); if (p_billboard || p_billboard_y) { material->set_flag(FLAG_BILLBOARD_KEEP_SCALE, true); diff --git a/scene/resources/material.h b/scene/resources/material.h index 23f3a8824d..5ea9a807d4 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -760,7 +760,7 @@ public: static void finish_shaders(); static void flush_changes(); - static Ref<Material> get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RID *r_shader_rid = nullptr); + static Ref<Material> get_material_for_2d(bool p_shaded, Transparency p_transparency, bool p_double_sided, bool p_billboard = false, bool p_billboard_y = false, bool p_msdf = false, bool p_no_depth = false, bool p_fixed_size = false, TextureFilter p_filter = TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, AlphaAntiAliasing p_alpha_antialiasing_mode = ALPHA_ANTIALIASING_OFF, RID *r_shader_rid = nullptr); virtual RID get_shader_rid() const override; diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index e497a628aa..7984ec735e 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -314,7 +314,19 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { //must make a copy, because this res is local to scene } } - } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { + } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = node->get(snames[nprops[j].name], &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } + } + } + if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) { value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 0ba177f882..608d15019e 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -636,6 +636,18 @@ Error ResourceLoaderText::load() { } } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = res->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } + } + } + if (set_valid) { res->set(assign, value); } @@ -746,6 +758,18 @@ Error ResourceLoaderText::load() { } } + if (value.get_type() == Variant::ARRAY) { + Array set_array = value; + bool is_get_valid = false; + Variant get_value = resource->get(assign, &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { + Array get_array = get_value; + if (!set_array.is_same_typed(get_array)) { + value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script()); + } + } + } + if (set_valid) { resource->set(assign, value); } diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp new file mode 100644 index 0000000000..ee1a47c338 --- /dev/null +++ b/scene/resources/video_stream.cpp @@ -0,0 +1,198 @@ +/**************************************************************************/ +/* video_stream.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "video_stream.h" + +#include "core/config/project_settings.h" +#include "servers/audio_server.h" + +// VideoStreamPlayback starts here. + +void VideoStreamPlayback::_bind_methods() { + ClassDB::bind_method(D_METHOD("mix_audio", "num_frames", "buffer", "offset"), &VideoStreamPlayback::mix_audio, DEFVAL(PackedFloat32Array()), DEFVAL(0)); + GDVIRTUAL_BIND(_stop); + GDVIRTUAL_BIND(_play); + GDVIRTUAL_BIND(_is_playing); + GDVIRTUAL_BIND(_set_paused, "paused"); + GDVIRTUAL_BIND(_is_paused); + GDVIRTUAL_BIND(_get_length); + GDVIRTUAL_BIND(_get_playback_position); + GDVIRTUAL_BIND(_seek, "time"); + GDVIRTUAL_BIND(_set_audio_track, "idx"); + GDVIRTUAL_BIND(_get_texture); + GDVIRTUAL_BIND(_update, "delta"); + GDVIRTUAL_BIND(_get_channels); + GDVIRTUAL_BIND(_get_mix_rate); +} + +VideoStreamPlayback::VideoStreamPlayback() { +} + +VideoStreamPlayback::~VideoStreamPlayback() { +} + +void VideoStreamPlayback::stop() { + GDVIRTUAL_CALL(_stop); +} + +void VideoStreamPlayback::play() { + GDVIRTUAL_CALL(_play); +} + +bool VideoStreamPlayback::is_playing() const { + bool ret; + if (GDVIRTUAL_CALL(_is_playing, ret)) { + return ret; + } + return false; +} + +void VideoStreamPlayback::set_paused(bool p_paused) { + GDVIRTUAL_CALL(_is_playing, p_paused); +} + +bool VideoStreamPlayback::is_paused() const { + bool ret; + if (GDVIRTUAL_CALL(_is_paused, ret)) { + return ret; + } + return false; +} + +double VideoStreamPlayback::get_length() const { + double ret; + if (GDVIRTUAL_CALL(_get_length, ret)) { + return ret; + } + return 0; +} + +double VideoStreamPlayback::get_playback_position() const { + double ret; + if (GDVIRTUAL_CALL(_get_playback_position, ret)) { + return ret; + } + return 0; +} + +void VideoStreamPlayback::seek(double p_time) { + GDVIRTUAL_CALL(_seek, p_time); +} + +void VideoStreamPlayback::set_audio_track(int p_idx) { + GDVIRTUAL_CALL(_set_audio_track, p_idx); +} + +Ref<Texture2D> VideoStreamPlayback::get_texture() const { + Ref<Texture2D> ret; + if (GDVIRTUAL_CALL(_get_texture, ret)) { + return ret; + } + return nullptr; +} + +void VideoStreamPlayback::update(double p_delta) { + if (!GDVIRTUAL_CALL(_update, p_delta)) { + ERR_FAIL_MSG("VideoStreamPlayback::update unimplemented"); + } +} + +void VideoStreamPlayback::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) { + mix_callback = p_callback; + mix_udata = p_userdata; +} + +int VideoStreamPlayback::get_channels() const { + int ret; + if (GDVIRTUAL_CALL(_get_channels, ret)) { + _channel_count = ret; + return ret; + } + return 0; +} + +int VideoStreamPlayback::get_mix_rate() const { + int ret; + if (GDVIRTUAL_CALL(_get_mix_rate, ret)) { + return ret; + } + return 0; +} + +int VideoStreamPlayback::mix_audio(int num_frames, PackedFloat32Array buffer, int offset) { + if (num_frames <= 0) { + return 0; + } + if (!mix_callback) { + return -1; + } + ERR_FAIL_INDEX_V(offset, buffer.size(), -1); + ERR_FAIL_INDEX_V((_channel_count < 1 ? 1 : _channel_count) * num_frames - 1, buffer.size() - offset, -1); + return mix_callback(mix_udata, buffer.ptr() + offset, num_frames); +} + +/* --- NOTE VideoStream starts here. ----- */ + +Ref<VideoStreamPlayback> VideoStream::instantiate_playback() { + Ref<VideoStreamPlayback> ret; + if (GDVIRTUAL_CALL(_instantiate_playback, ret)) { + ERR_FAIL_COND_V_MSG(ret.is_null(), nullptr, "Plugin returned null playback"); + ret->set_audio_track(audio_track); + return ret; + } + return nullptr; +} + +void VideoStream::set_file(const String &p_file) { + file = p_file; +} + +String VideoStream::get_file() { + return file; +} + +void VideoStream::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStream::set_file); + ClassDB::bind_method(D_METHOD("get_file"), &VideoStream::get_file); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "file"), "set_file", "get_file"); + + GDVIRTUAL_BIND(_instantiate_playback); +} + +VideoStream::VideoStream() { +} + +VideoStream::~VideoStream() { +} + +void VideoStream::set_audio_track(int p_track) { + audio_track = p_track; +} diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h index f83c621d0a..b91a7acf35 100644 --- a/scene/resources/video_stream.h +++ b/scene/resources/video_stream.h @@ -31,6 +31,7 @@ #ifndef VIDEO_STREAM_H #define VIDEO_STREAM_H +#include "core/io/file_access.h" #include "scene/resources/texture.h" class VideoStreamPlayback : public Resource { @@ -39,40 +40,77 @@ class VideoStreamPlayback : public Resource { public: typedef int (*AudioMixCallback)(void *p_udata, const float *p_data, int p_frames); - virtual void stop() = 0; - virtual void play() = 0; +protected: + AudioMixCallback mix_callback = nullptr; + void *mix_udata = nullptr; + mutable int _channel_count = 0; // Used only to assist with bounds checking in mix_audio. + + static void _bind_methods(); + GDVIRTUAL0(_stop); + GDVIRTUAL0(_play); + GDVIRTUAL0RC(bool, _is_playing); + GDVIRTUAL1(_set_paused, bool); + GDVIRTUAL0RC(bool, _is_paused); + GDVIRTUAL0RC(double, _get_length); + GDVIRTUAL0RC(double, _get_playback_position); + GDVIRTUAL1(_seek, double); + GDVIRTUAL1(_set_audio_track, int); + GDVIRTUAL0RC(Ref<Texture2D>, _get_texture); + GDVIRTUAL1(_update, double); + GDVIRTUAL0RC(int, _get_channels); + GDVIRTUAL0RC(int, _get_mix_rate); + + int mix_audio(int num_frames, PackedFloat32Array buffer = {}, int offset = 0); - virtual bool is_playing() const = 0; +public: + VideoStreamPlayback(); + virtual ~VideoStreamPlayback(); - virtual void set_paused(bool p_paused) = 0; - virtual bool is_paused() const = 0; + virtual void stop(); + virtual void play(); - virtual void set_loop(bool p_enable) = 0; - virtual bool has_loop() const = 0; + virtual bool is_playing() const; - virtual double get_length() const = 0; + virtual void set_paused(bool p_paused); + virtual bool is_paused() const; - virtual double get_playback_position() const = 0; - virtual void seek(double p_time) = 0; + virtual double get_length() const; - virtual void set_audio_track(int p_idx) = 0; + virtual double get_playback_position() const; + virtual void seek(double p_time); - virtual Ref<Texture2D> get_texture() const = 0; + virtual void set_audio_track(int p_idx); - virtual void update(double p_delta) = 0; + virtual Ref<Texture2D> get_texture() const; + virtual void update(double p_delta); - virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) = 0; - virtual int get_channels() const = 0; - virtual int get_mix_rate() const = 0; + virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata); + virtual int get_channels() const; + virtual int get_mix_rate() const; }; class VideoStream : public Resource { GDCLASS(VideoStream, Resource); - OBJ_SAVE_TYPE(VideoStream); // Saves derived classes with common type so they can be interchanged. + OBJ_SAVE_TYPE(VideoStream); + +protected: + static void + _bind_methods(); + + GDVIRTUAL0R(Ref<VideoStreamPlayback>, _instantiate_playback); + + String file; + int audio_track = 0; public: - virtual void set_audio_track(int p_track) = 0; - virtual Ref<VideoStreamPlayback> instantiate_playback() = 0; + void set_file(const String &p_file); + String get_file(); + + virtual void set_audio_track(int p_track); + virtual Ref<VideoStreamPlayback> instantiate_playback(); + + VideoStream(); + ~VideoStream(); }; #endif // VIDEO_STREAM_H diff --git a/scene/resources/world_2d.cpp b/scene/resources/world_2d.cpp index 2a70139bcb..c7304da358 100644 --- a/scene/resources/world_2d.cpp +++ b/scene/resources/world_2d.cpp @@ -43,6 +43,14 @@ RID World2D::get_canvas() const { } RID World2D::get_space() const { + if (space.is_null()) { + space = PhysicsServer2D::get_singleton()->space_create(); + PhysicsServer2D::get_singleton()->space_set_active(space, true); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/2d/default_gravity")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/2d/default_gravity_vector")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/2d/default_linear_damp")); + PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/2d/default_angular_damp")); + } return space; } @@ -71,19 +79,11 @@ void World2D::_bind_methods() { } PhysicsDirectSpaceState2D *World2D::get_direct_space_state() { - return PhysicsServer2D::get_singleton()->space_get_direct_state(space); + return PhysicsServer2D::get_singleton()->space_get_direct_state(get_space()); } World2D::World2D() { canvas = RenderingServer::get_singleton()->canvas_create(); - - // Create and configure space2D to be more friendly with pixels than meters - space = PhysicsServer2D::get_singleton()->space_create(); - PhysicsServer2D::get_singleton()->space_set_active(space, true); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0)); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1))); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1)); - PhysicsServer2D::get_singleton()->area_set_param(space, PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0)); } World2D::~World2D() { @@ -91,7 +91,9 @@ World2D::~World2D() { ERR_FAIL_NULL(PhysicsServer2D::get_singleton()); ERR_FAIL_NULL(NavigationServer2D::get_singleton()); RenderingServer::get_singleton()->free(canvas); - PhysicsServer2D::get_singleton()->free(space); + if (space.is_valid()) { + PhysicsServer2D::get_singleton()->free(space); + } if (navigation_map.is_valid()) { NavigationServer2D::get_singleton()->free(navigation_map); } diff --git a/scene/resources/world_2d.h b/scene/resources/world_2d.h index f02dddd2fe..0b3b9df7dc 100644 --- a/scene/resources/world_2d.h +++ b/scene/resources/world_2d.h @@ -43,7 +43,7 @@ class World2D : public Resource { GDCLASS(World2D, Resource); RID canvas; - RID space; + mutable RID space; mutable RID navigation_map; HashSet<Viewport *> viewports; diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index cc4d261c0d..82c056d5ee 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -51,6 +51,14 @@ void World3D::_remove_camera(Camera3D *p_camera) { } RID World3D::get_space() const { + if (space.is_null()) { + space = PhysicsServer3D::get_singleton()->space_create(); + PhysicsServer3D::get_singleton()->space_set_active(space, true); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_GET("physics/3d/default_gravity")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_GET("physics/3d/default_gravity_vector")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_GET("physics/3d/default_linear_damp")); + PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_GET("physics/3d/default_angular_damp")); + } return space; } @@ -121,7 +129,7 @@ Ref<CameraAttributes> World3D::get_camera_attributes() const { } PhysicsDirectSpaceState3D *World3D::get_direct_space_state() { - return PhysicsServer3D::get_singleton()->space_get_direct_state(space); + return PhysicsServer3D::get_singleton()->space_get_direct_state(get_space()); } void World3D::_bind_methods() { @@ -145,22 +153,18 @@ void World3D::_bind_methods() { } World3D::World3D() { - space = PhysicsServer3D::get_singleton()->space_create(); scenario = RenderingServer::get_singleton()->scenario_create(); - - PhysicsServer3D::get_singleton()->space_set_active(space, true); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY, GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8)); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_GRAVITY_VECTOR, GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0))); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_LINEAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); - PhysicsServer3D::get_singleton()->area_set_param(space, PhysicsServer3D::AREA_PARAM_ANGULAR_DAMP, GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1)); } World3D::~World3D() { - ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); ERR_FAIL_NULL(RenderingServer::get_singleton()); + ERR_FAIL_NULL(PhysicsServer3D::get_singleton()); ERR_FAIL_NULL(NavigationServer3D::get_singleton()); - PhysicsServer3D::get_singleton()->free(space); + RenderingServer::get_singleton()->free(scenario); + if (space.is_valid()) { + PhysicsServer3D::get_singleton()->free(space); + } if (navigation_map.is_valid()) { NavigationServer3D::get_singleton()->free(navigation_map); } diff --git a/scene/resources/world_3d.h b/scene/resources/world_3d.h index ad17daf466..518fff64e2 100644 --- a/scene/resources/world_3d.h +++ b/scene/resources/world_3d.h @@ -45,8 +45,8 @@ class World3D : public Resource { GDCLASS(World3D, Resource); private: - RID space; RID scenario; + mutable RID space; mutable RID navigation_map; Ref<Environment> environment; diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 6b4435a991..8c877e4eed 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -144,7 +144,7 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m ERR_FAIL_V(2); } -PackedStringArray AudioDriver::get_device_list() { +PackedStringArray AudioDriver::get_output_device_list() { PackedStringArray list; list.push_back("Default"); @@ -152,11 +152,11 @@ PackedStringArray AudioDriver::get_device_list() { return list; } -String AudioDriver::get_device() { +String AudioDriver::get_output_device() { return "Default"; } -PackedStringArray AudioDriver::capture_get_device_list() { +PackedStringArray AudioDriver::get_input_device_list() { PackedStringArray list; list.push_back("Default"); @@ -238,7 +238,7 @@ void AudioServer::_driver_process(int p_frames, int32_t *p_buffer) { #endif if (channel_count != get_channel_count()) { - // Amount of channels changed due to a device change + // Amount of channels changed due to a output_device change // reinitialize the buses channels and buffers init_channels_and_buffers(); } @@ -1632,28 +1632,28 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const { return state; } -PackedStringArray AudioServer::get_device_list() { - return AudioDriver::get_singleton()->get_device_list(); +PackedStringArray AudioServer::get_output_device_list() { + return AudioDriver::get_singleton()->get_output_device_list(); } -String AudioServer::get_device() { - return AudioDriver::get_singleton()->get_device(); +String AudioServer::get_output_device() { + return AudioDriver::get_singleton()->get_output_device(); } -void AudioServer::set_device(String device) { - AudioDriver::get_singleton()->set_device(device); +void AudioServer::set_output_device(String output_device) { + AudioDriver::get_singleton()->set_output_device(output_device); } -PackedStringArray AudioServer::capture_get_device_list() { - return AudioDriver::get_singleton()->capture_get_device_list(); +PackedStringArray AudioServer::get_input_device_list() { + return AudioDriver::get_singleton()->get_input_device_list(); } -String AudioServer::capture_get_device() { - return AudioDriver::get_singleton()->capture_get_device(); +String AudioServer::get_input_device() { + return AudioDriver::get_singleton()->get_input_device(); } -void AudioServer::capture_set_device(const String &p_name) { - AudioDriver::get_singleton()->capture_set_device(p_name); +void AudioServer::set_input_device(const String &p_name) { + AudioDriver::get_singleton()->set_input_device(p_name); } void AudioServer::set_enable_tagging_used_audio_streams(bool p_enable) { @@ -1711,17 +1711,17 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode); ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate); - ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list); - ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device); - ClassDB::bind_method(D_METHOD("set_device", "device"), &AudioServer::set_device); + ClassDB::bind_method(D_METHOD("get_output_device_list"), &AudioServer::get_output_device_list); + ClassDB::bind_method(D_METHOD("get_output_device"), &AudioServer::get_output_device); + ClassDB::bind_method(D_METHOD("set_output_device", "output_device"), &AudioServer::set_output_device); ClassDB::bind_method(D_METHOD("get_time_to_next_mix"), &AudioServer::get_time_to_next_mix); ClassDB::bind_method(D_METHOD("get_time_since_last_mix"), &AudioServer::get_time_since_last_mix); ClassDB::bind_method(D_METHOD("get_output_latency"), &AudioServer::get_output_latency); - ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list); - ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device); - ClassDB::bind_method(D_METHOD("capture_set_device", "name"), &AudioServer::capture_set_device); + ClassDB::bind_method(D_METHOD("get_input_device_list"), &AudioServer::get_input_device_list); + ClassDB::bind_method(D_METHOD("get_input_device"), &AudioServer::get_input_device); + ClassDB::bind_method(D_METHOD("set_input_device", "name"), &AudioServer::set_input_device); ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout); ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout); @@ -1729,11 +1729,11 @@ void AudioServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enable_tagging_used_audio_streams", "enable"), &AudioServer::set_enable_tagging_used_audio_streams); ADD_PROPERTY(PropertyInfo(Variant::INT, "bus_count"), "set_bus_count", "get_bus_count"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "device"), "set_device", "get_device"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "capture_device"), "capture_set_device", "capture_get_device"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "output_device"), "set_output_device", "get_output_device"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "input_device"), "set_input_device", "get_input_device"); // The default value may be set to an empty string by the platform-specific audio driver. // Override for class reference generation purposes. - ADD_PROPERTY_DEFAULT("capture_device", "Default"); + ADD_PROPERTY_DEFAULT("input_device", "Default"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed_scale"), "set_playback_speed_scale", "get_playback_speed_scale"); ADD_SIGNAL(MethodInfo("bus_layout_changed")); diff --git a/servers/audio_server.h b/servers/audio_server.h index a369efedf7..d3d87a8400 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -94,18 +94,18 @@ public: virtual void start() = 0; virtual int get_mix_rate() const = 0; virtual SpeakerMode get_speaker_mode() const = 0; - virtual PackedStringArray get_device_list(); - virtual String get_device(); - virtual void set_device(String device) {} + virtual PackedStringArray get_output_device_list(); + virtual String get_output_device(); + virtual void set_output_device(String output_device) {} virtual void lock() = 0; virtual void unlock() = 0; virtual void finish() = 0; virtual Error capture_start() { return FAILED; } virtual Error capture_stop() { return FAILED; } - virtual void capture_set_device(const String &p_name) {} - virtual String capture_get_device() { return "Default"; } - virtual PackedStringArray capture_get_device_list(); + virtual void set_input_device(const String &p_name) {} + virtual String get_input_device() { return "Default"; } + virtual PackedStringArray get_input_device_list(); virtual float get_latency() { return 0; } @@ -419,13 +419,13 @@ public: void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout); Ref<AudioBusLayout> generate_bus_layout() const; - PackedStringArray get_device_list(); - String get_device(); - void set_device(String device); + PackedStringArray get_output_device_list(); + String get_output_device(); + void set_output_device(String output_device); - PackedStringArray capture_get_device_list(); - String capture_get_device(); - void capture_set_device(const String &p_name); + PackedStringArray get_input_device_list(); + String get_input_device(); + void set_input_device(const String &p_name); void set_enable_tagging_used_audio_streams(bool p_enable); diff --git a/servers/physics_2d/godot_area_2d.cpp b/servers/physics_2d/godot_area_2d.cpp index 4d93bbfa61..4d2148aa07 100644 --- a/servers/physics_2d/godot_area_2d.cpp +++ b/servers/physics_2d/godot_area_2d.cpp @@ -145,11 +145,8 @@ void GodotArea2D::set_param(PhysicsServer2D::AreaParameter p_param, const Varian case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point = p_value; break; - case PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - gravity_distance_scale = p_value; - break; - case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - point_attenuation = p_value; + case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + gravity_point_unit_distance = p_value; break; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: _set_space_override_mode(linear_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value); @@ -179,10 +176,8 @@ Variant GodotArea2D::get_param(PhysicsServer2D::AreaParameter p_param) const { return gravity_vector; case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point; - case PhysicsServer2D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - return gravity_distance_scale; - case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - return point_attenuation; + case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + return gravity_point_unit_distance; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: return linear_damping_override_mode; case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP: @@ -304,13 +299,13 @@ void GodotArea2D::call_queries() { void GodotArea2D::compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const { if (is_gravity_point()) { - const real_t gr_distance_scale = get_gravity_distance_scale(); + const real_t gr_unit_dist = get_gravity_point_unit_distance(); Vector2 v = get_transform().xform(get_gravity_vector()) - p_position; - if (gr_distance_scale > 0) { - const real_t v_length = v.length(); - if (v_length > 0) { - const real_t v_scaled = v_length * gr_distance_scale; - r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + if (gr_unit_dist > 0) { + const real_t v_length_sq = v.length_squared(); + if (v_length_sq > 0) { + const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq; + r_gravity = v.normalized() * gravity_strength; } else { r_gravity = Vector2(); } diff --git a/servers/physics_2d/godot_area_2d.h b/servers/physics_2d/godot_area_2d.h index 07c89ecb88..234e4eb9a9 100644 --- a/servers/physics_2d/godot_area_2d.h +++ b/servers/physics_2d/godot_area_2d.h @@ -48,8 +48,7 @@ class GodotArea2D : public GodotCollisionObject2D { real_t gravity = 9.80665; Vector2 gravity_vector = Vector2(0, -1); bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; - real_t point_attenuation = 1.0; + real_t gravity_point_unit_distance = 0.0; real_t linear_damp = 0.1; real_t angular_damp = 1.0; int priority = 0; @@ -125,11 +124,8 @@ public: _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; } _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; } - _FORCE_INLINE_ void set_gravity_distance_scale(real_t scale) { gravity_distance_scale = scale; } - _FORCE_INLINE_ real_t get_gravity_distance_scale() const { return gravity_distance_scale; } - - _FORCE_INLINE_ void set_point_attenuation(real_t p_point_attenuation) { point_attenuation = p_point_attenuation; } - _FORCE_INLINE_ real_t get_point_attenuation() const { return point_attenuation; } + _FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; } + _FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; } _FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; } _FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; } diff --git a/servers/physics_2d/godot_space_2d.cpp b/servers/physics_2d/godot_space_2d.cpp index 0a5a7c93cc..1d1f1ba054 100644 --- a/servers/physics_2d/godot_space_2d.cpp +++ b/servers/physics_2d/godot_space_2d.cpp @@ -1216,15 +1216,15 @@ GodotPhysicsDirectSpaceState2D *GodotSpace2D::get_direct_state() { } GodotSpace2D::GodotSpace2D() { - body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); - body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0)); - body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); - solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); - contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0); - contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5); - contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3); - contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); - constraint_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2); + body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_linear"); + body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/2d/sleep_threshold_angular"); + body_time_to_sleep = GLOBAL_GET("physics/2d/time_before_sleep"); + solver_iterations = GLOBAL_GET("physics/2d/solver/solver_iterations"); + contact_recycle_radius = GLOBAL_GET("physics/2d/solver/contact_recycle_radius"); + contact_max_separation = GLOBAL_GET("physics/2d/solver/contact_max_separation"); + contact_max_allowed_penetration = GLOBAL_GET("physics/2d/solver/contact_max_allowed_penetration"); + contact_bias = GLOBAL_GET("physics/2d/solver/default_contact_bias"); + constraint_bias = GLOBAL_GET("physics/2d/solver/default_constraint_bias"); broadphase = GodotBroadPhase2D::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/physics_3d/godot_area_3d.cpp b/servers/physics_3d/godot_area_3d.cpp index cd1f3b52b9..5bf16aa688 100644 --- a/servers/physics_3d/godot_area_3d.cpp +++ b/servers/physics_3d/godot_area_3d.cpp @@ -152,11 +152,8 @@ void GodotArea3D::set_param(PhysicsServer3D::AreaParameter p_param, const Varian case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: gravity_is_point = p_value; break; - case PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - gravity_distance_scale = p_value; - break; - case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - point_attenuation = p_value; + case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + gravity_point_unit_distance = p_value; break; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: _set_space_override_mode(linear_damping_override_mode, (PhysicsServer3D::AreaSpaceOverrideMode)(int)p_value); @@ -200,10 +197,8 @@ Variant GodotArea3D::get_param(PhysicsServer3D::AreaParameter p_param) const { return gravity_vector; case PhysicsServer3D::AREA_PARAM_GRAVITY_IS_POINT: return gravity_is_point; - case PhysicsServer3D::AREA_PARAM_GRAVITY_DISTANCE_SCALE: - return gravity_distance_scale; - case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_ATTENUATION: - return point_attenuation; + case PhysicsServer3D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE: + return gravity_point_unit_distance; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE: return linear_damping_override_mode; case PhysicsServer3D::AREA_PARAM_LINEAR_DAMP: @@ -333,13 +328,13 @@ void GodotArea3D::call_queries() { void GodotArea3D::compute_gravity(const Vector3 &p_position, Vector3 &r_gravity) const { if (is_gravity_point()) { - const real_t gr_distance_scale = get_gravity_distance_scale(); + const real_t gr_unit_dist = get_gravity_point_unit_distance(); Vector3 v = get_transform().xform(get_gravity_vector()) - p_position; - if (gr_distance_scale > 0) { - const real_t v_length = v.length(); - if (v_length > 0) { - const real_t v_scaled = v_length * gr_distance_scale; - r_gravity = (v.normalized() * (get_gravity() / (v_scaled * v_scaled))); + if (gr_unit_dist > 0) { + const real_t v_length_sq = v.length_squared(); + if (v_length_sq > 0) { + const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq; + r_gravity = v.normalized() * gravity_strength; } else { r_gravity = Vector3(); } diff --git a/servers/physics_3d/godot_area_3d.h b/servers/physics_3d/godot_area_3d.h index 0961da5b7d..f05d0f9019 100644 --- a/servers/physics_3d/godot_area_3d.h +++ b/servers/physics_3d/godot_area_3d.h @@ -49,8 +49,7 @@ class GodotArea3D : public GodotCollisionObject3D { real_t gravity = 9.80665; Vector3 gravity_vector = Vector3(0, -1, 0); bool gravity_is_point = false; - real_t gravity_distance_scale = 0.0; - real_t point_attenuation = 1.0; + real_t gravity_point_unit_distance = 0.0; real_t linear_damp = 0.1; real_t angular_damp = 0.1; real_t wind_force_magnitude = 0.0; @@ -134,11 +133,8 @@ public: _FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; } _FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; } - _FORCE_INLINE_ void set_gravity_distance_scale(real_t scale) { gravity_distance_scale = scale; } - _FORCE_INLINE_ real_t get_gravity_distance_scale() const { return gravity_distance_scale; } - - _FORCE_INLINE_ void set_point_attenuation(real_t p_point_attenuation) { point_attenuation = p_point_attenuation; } - _FORCE_INLINE_ real_t get_point_attenuation() const { return point_attenuation; } + _FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; } + _FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; } _FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; } _FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; } diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp index fb5a67c008..2de1d86de1 100644 --- a/servers/physics_3d/godot_collision_solver_3d.cpp +++ b/servers/physics_3d/godot_collision_solver_3d.cpp @@ -127,11 +127,10 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A, } if (p_result_callback) { + Vector3 normal = (support_B - support_A).normalized(); if (p_swap_result) { - Vector3 normal = (support_B - support_A).normalized(); - p_result_callback(support_B, 0, support_A, 0, normal, p_userdata); + p_result_callback(support_B, 0, support_A, 0, -normal, p_userdata); } else { - Vector3 normal = (support_A - support_B).normalized(); p_result_callback(support_A, 0, support_B, 0, normal, p_userdata); } } diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp index 93572965d2..35f6fa023d 100644 --- a/servers/physics_3d/godot_space_3d.cpp +++ b/servers/physics_3d/godot_space_3d.cpp @@ -1250,14 +1250,14 @@ GodotPhysicsDirectSpaceState3D *GodotSpace3D::get_direct_state() { } GodotSpace3D::GodotSpace3D() { - body_linear_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); - body_angular_velocity_sleep_threshold = GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0)); - body_time_to_sleep = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); - solver_iterations = GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); - contact_recycle_radius = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); - contact_max_separation = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05); - contact_max_allowed_penetration = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); - contact_bias = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); + body_linear_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_linear"); + body_angular_velocity_sleep_threshold = GLOBAL_GET("physics/3d/sleep_threshold_angular"); + body_time_to_sleep = GLOBAL_GET("physics/3d/time_before_sleep"); + solver_iterations = GLOBAL_GET("physics/3d/solver/solver_iterations"); + contact_recycle_radius = GLOBAL_GET("physics/3d/solver/contact_recycle_radius"); + contact_max_separation = GLOBAL_GET("physics/3d/solver/contact_max_separation"); + contact_max_allowed_penetration = GLOBAL_GET("physics/3d/solver/contact_max_allowed_penetration"); + contact_bias = GLOBAL_GET("physics/3d/solver/default_contact_bias"); broadphase = GodotBroadPhase3D::create_func(); broadphase->set_pair_callback(_broadphase_pair, this); diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 214de27b35..f9a8d5f156 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -811,8 +811,7 @@ void PhysicsServer2D::_bind_methods() { BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_VECTOR); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_IS_POINT); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_DISTANCE_SCALE); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_ATTENUATION); + BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE); @@ -880,6 +879,23 @@ void PhysicsServer2D::_bind_methods() { PhysicsServer2D::PhysicsServer2D() { singleton = this; + + // World2D physics space + GLOBAL_DEF_BASIC("physics/2d/default_gravity", 980.0); + GLOBAL_DEF_BASIC("physics/2d/default_gravity_vector", Vector2(0, 1)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 0.1); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/default_angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), 1.0); + + // PhysicsServer2D + GLOBAL_DEF("physics/2d/sleep_threshold_linear", 2.0); + GLOBAL_DEF("physics/2d/sleep_threshold_angular", Math::deg_to_rad(8.0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); + GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/2d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.0); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 1.5); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,10,0.01,or_greater"), 0.3); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/2d/solver/default_constraint_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.2); } PhysicsServer2D::~PhysicsServer2D() { diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 836ab5bd76..3e254e610e 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -286,8 +286,7 @@ public: AREA_PARAM_GRAVITY, AREA_PARAM_GRAVITY_VECTOR, AREA_PARAM_GRAVITY_IS_POINT, - AREA_PARAM_GRAVITY_DISTANCE_SCALE, - AREA_PARAM_GRAVITY_POINT_ATTENUATION, + AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, AREA_PARAM_LINEAR_DAMP, AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp index f1272a985d..6a163c86d2 100644 --- a/servers/physics_server_3d.cpp +++ b/servers/physics_server_3d.cpp @@ -976,8 +976,7 @@ void PhysicsServer3D::_bind_methods() { BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_VECTOR); BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_IS_POINT); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_DISTANCE_SCALE); - BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_ATTENUATION); + BIND_ENUM_CONSTANT(AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE); BIND_ENUM_CONSTANT(AREA_PARAM_LINEAR_DAMP); BIND_ENUM_CONSTANT(AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE); @@ -1048,6 +1047,22 @@ void PhysicsServer3D::_bind_methods() { PhysicsServer3D::PhysicsServer3D() { singleton = this; + + // World3D physics space + GLOBAL_DEF_BASIC("physics/3d/default_gravity", 9.8); + GLOBAL_DEF_BASIC("physics/3d/default_gravity_vector", Vector3(0, -1, 0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/default_angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), 0.1); + + // PhysicsServer3D + GLOBAL_DEF("physics/3d/sleep_threshold_linear", 0.1); + GLOBAL_DEF("physics/3d/sleep_threshold_angular", Math::deg_to_rad(8.0)); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/time_before_sleep", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5); + GLOBAL_DEF(PropertyInfo(Variant::INT, "physics/3d/solver/solver_iterations", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), 16); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_recycle_radius", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_separation", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.05); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/contact_max_allowed_penetration", PROPERTY_HINT_RANGE, "0,0.1,0.01,or_greater"), 0.01); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "physics/3d/solver/default_contact_bias", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.8); } PhysicsServer3D::~PhysicsServer3D() { diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index d1c644d51a..abf22e68a4 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -316,8 +316,7 @@ public: AREA_PARAM_GRAVITY, AREA_PARAM_GRAVITY_VECTOR, AREA_PARAM_GRAVITY_IS_POINT, - AREA_PARAM_GRAVITY_DISTANCE_SCALE, - AREA_PARAM_GRAVITY_POINT_ATTENUATION, + AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE, AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE, AREA_PARAM_LINEAR_DAMP, AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE, diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 814d145b66..7fff349b3c 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -574,7 +574,7 @@ RID SkyRD::Sky::get_textures(SkyTextureSetVersion p_version, RID p_default_shade u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 1; // half res if (p_version >= SKY_TEXTURE_SET_CUBEMAP) { - if (reflection.layers[0].views[1].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) { + if (reflection.layers.size() && reflection.layers[0].views.size() >= 2 && reflection.layers[0].views[1].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_HALF_RES) { u.append_id(reflection.layers[0].views[1]); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK)); @@ -594,7 +594,7 @@ RID SkyRD::Sky::get_textures(SkyTextureSetVersion p_version, RID p_default_shade u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.binding = 2; // quarter res if (p_version >= SKY_TEXTURE_SET_CUBEMAP) { - if (reflection.layers[0].views[2].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) { + if (reflection.layers.size() && reflection.layers[0].views.size() >= 3 && reflection.layers[0].views[2].is_valid() && p_version != SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES) { u.append_id(reflection.layers[0].views[2]); } else { u.append_id(texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK)); @@ -1323,7 +1323,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, // Note, we ignore environment_get_sky_orientation here as this is applied when we do our lookup in our scene shader. - if (shader_data->uses_quarter_res) { + if (shader_data->uses_quarter_res && roughness_layers >= 3) { RD::get_singleton()->draw_command_begin_label("Render Sky to Quarter Res Cubemap"); PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_QUARTER_RES]; @@ -1340,9 +1340,11 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); + } else if (shader_data->uses_quarter_res && roughness_layers < 3) { + ERR_PRINT_ED("Cannot use quarter res buffer in sky shader when roughness layers is less than 3. Please increase rendering/reflections/sky_reflections/roughness_layers."); } - if (shader_data->uses_half_res) { + if (shader_data->uses_half_res && roughness_layers >= 2) { RD::get_singleton()->draw_command_begin_label("Render Sky to Half Res Cubemap"); PipelineCacheRD *pipeline = &shader_data->pipelines[SKY_VERSION_CUBEMAP_HALF_RES]; @@ -1359,6 +1361,8 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RD::get_singleton()->draw_list_end(); } RD::get_singleton()->draw_command_end_label(); + } else if (shader_data->uses_half_res && roughness_layers < 2) { + ERR_PRINT_ED("Cannot use half res buffer in sky shader when roughness layers is less than 2. Please increase rendering/reflections/sky_reflections/roughness_layers."); } RD::DrawListID cubemap_draw_list; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 0f4fa1b9c4..bd8c11186e 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1550,6 +1550,9 @@ void RendererCanvasRenderRD::light_set_texture(RID p_rid, RID p_texture) { if (cl->texture == p_texture) { return; } + + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (cl->texture.is_valid()) { texture_storage->texture_remove_from_decal_atlas(cl->texture); } diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index cdecb3828b..f56cee9e12 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -232,6 +232,8 @@ void LightStorage::light_set_projector(RID p_light, RID p_texture) { return; } + ERR_FAIL_COND(p_texture.is_valid() && !texture_storage->owns_texture(p_texture)); + if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) { texture_storage->texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI); } @@ -1552,6 +1554,11 @@ bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) { if (rpi->processing_side == 6) { rpi->processing_side = 0; rpi->processing_layer++; + if (rpi->processing_layer == atlas->reflections[rpi->atlas_index].data.layers[0].mipmaps.size()) { + rpi->rendering = false; + rpi->processing_layer = 1; + return true; + } } return false; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 3886f5b379..aecd0593bc 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -236,7 +236,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) { } } - if (!p_viewport->disable_2d && !p_viewport->disable_environment && RSG::scene->is_scenario(p_viewport->scenario)) { + if (!p_viewport->disable_2d && !viewport_is_environment_disabled(p_viewport) && RSG::scene->is_scenario(p_viewport->scenario)) { RID environment = RSG::scene->scenario_get_environment(p_viewport->scenario); if (RSG::scene->is_environment(environment)) { scenario_draw_canvas_bg = RSG::scene->environment_get_background(environment) == RS::ENV_BG_CANVAS; @@ -992,11 +992,21 @@ void RendererViewport::viewport_set_disable_2d(RID p_viewport, bool p_disable) { viewport->disable_2d = p_disable; } -void RendererViewport::viewport_set_disable_environment(RID p_viewport, bool p_disable) { +void RendererViewport::viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode) { Viewport *viewport = viewport_owner.get_or_null(p_viewport); ERR_FAIL_COND(!viewport); - viewport->disable_environment = p_disable; + viewport->disable_environment = p_mode; +} + +bool RendererViewport::viewport_is_environment_disabled(Viewport *viewport) { + ERR_FAIL_COND_V(!viewport, false); + + if (viewport->parent.is_valid() && viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_INHERIT) { + Viewport *parent = viewport_owner.get_or_null(viewport->parent); + return viewport_is_environment_disabled(parent); + } + return viewport->disable_environment == RS::VIEWPORT_ENVIRONMENT_DISABLED; } void RendererViewport::viewport_set_disable_3d(RID p_viewport, bool p_disable) { diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h index 9b32cc3774..c24275de6e 100644 --- a/servers/rendering/renderer_viewport.h +++ b/servers/rendering/renderer_viewport.h @@ -85,7 +85,7 @@ public: bool viewport_render_direct_to_screen; bool disable_2d = false; - bool disable_environment = false; + RS::ViewportEnvironmentMode disable_environment = RS::VIEWPORT_ENVIRONMENT_INHERIT; bool disable_3d = false; bool measure_render_time = false; @@ -238,9 +238,11 @@ public: const RendererSceneRender::CameraData *viewport_get_prev_camera_data(RID p_viewport); void viewport_set_disable_2d(RID p_viewport, bool p_disable); - void viewport_set_disable_environment(RID p_viewport, bool p_disable); + void viewport_set_environment_mode(RID p_viewport, RS::ViewportEnvironmentMode p_mode); void viewport_set_disable_3d(RID p_viewport, bool p_disable); + bool viewport_is_environment_disabled(Viewport *viewport); + void viewport_attach_camera(RID p_viewport, RID p_camera); void viewport_set_scenario(RID p_viewport, RID p_scenario); void viewport_attach_canvas(RID p_viewport, RID p_canvas); diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 8ac522bafe..bcaaba8d65 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -608,7 +608,7 @@ public: FUNC1RC(RID, viewport_get_texture, RID) FUNC2(viewport_set_disable_2d, RID, bool) - FUNC2(viewport_set_disable_environment, RID, bool) + FUNC2(viewport_set_environment_mode, RID, ViewportEnvironmentMode) FUNC2(viewport_set_disable_3d, RID, bool) FUNC2(viewport_set_canvas_cull_mask, RID, uint32_t) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 0c06ba7c26..cea7912574 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2200,7 +2200,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_get_texture", "viewport"), &RenderingServer::viewport_get_texture); ClassDB::bind_method(D_METHOD("viewport_set_disable_3d", "viewport", "disable"), &RenderingServer::viewport_set_disable_3d); ClassDB::bind_method(D_METHOD("viewport_set_disable_2d", "viewport", "disable"), &RenderingServer::viewport_set_disable_2d); - ClassDB::bind_method(D_METHOD("viewport_set_disable_environment", "viewport", "disabled"), &RenderingServer::viewport_set_disable_environment); + ClassDB::bind_method(D_METHOD("viewport_set_environment_mode", "viewport", "mode"), &RenderingServer::viewport_set_environment_mode); ClassDB::bind_method(D_METHOD("viewport_attach_camera", "viewport", "camera"), &RenderingServer::viewport_attach_camera); ClassDB::bind_method(D_METHOD("viewport_set_scenario", "viewport", "scenario"), &RenderingServer::viewport_set_scenario); ClassDB::bind_method(D_METHOD("viewport_attach_canvas", "viewport", "canvas"), &RenderingServer::viewport_attach_canvas); @@ -2255,6 +2255,11 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(VIEWPORT_CLEAR_NEVER); BIND_ENUM_CONSTANT(VIEWPORT_CLEAR_ONLY_NEXT_FRAME); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_DISABLED); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_ENABLED); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_INHERIT); + BIND_ENUM_CONSTANT(VIEWPORT_ENVIRONMENT_MAX); + BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_100_PERCENT); BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_120_PERCENT); BIND_ENUM_CONSTANT(VIEWPORT_SDF_OVERSIZE_150_PERCENT); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 231139e9df..b416ed30e2 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -840,7 +840,14 @@ public: virtual RID viewport_get_texture(RID p_viewport) const = 0; - virtual void viewport_set_disable_environment(RID p_viewport, bool p_disable) = 0; + enum ViewportEnvironmentMode { + VIEWPORT_ENVIRONMENT_DISABLED, + VIEWPORT_ENVIRONMENT_ENABLED, + VIEWPORT_ENVIRONMENT_INHERIT, + VIEWPORT_ENVIRONMENT_MAX, + }; + + virtual void viewport_set_environment_mode(RID p_viewport, ViewportEnvironmentMode p_mode) = 0; virtual void viewport_set_disable_3d(RID p_viewport, bool p_disable) = 0; virtual void viewport_set_disable_2d(RID p_viewport, bool p_disable) = 0; @@ -1639,6 +1646,7 @@ VARIANT_ENUM_CAST(RenderingServer::FogVolumeShape); VARIANT_ENUM_CAST(RenderingServer::ViewportScaling3DMode); VARIANT_ENUM_CAST(RenderingServer::ViewportUpdateMode); VARIANT_ENUM_CAST(RenderingServer::ViewportClearMode); +VARIANT_ENUM_CAST(RenderingServer::ViewportEnvironmentMode); VARIANT_ENUM_CAST(RenderingServer::ViewportMSAA); VARIANT_ENUM_CAST(RenderingServer::ViewportScreenSpaceAA); VARIANT_ENUM_CAST(RenderingServer::ViewportRenderInfo); diff --git a/tests/core/io/test_file_access.h b/tests/core/io/test_file_access.h index 7117cb137d..243b75626f 100644 --- a/tests/core/io/test_file_access.h +++ b/tests/core/io/test_file_access.h @@ -39,6 +39,7 @@ namespace TestFileAccess { TEST_CASE("[FileAccess] CSV read") { Ref<FileAccess> f = FileAccess::open(TestUtils::get_data_path("translations.csv"), FileAccess::READ); + REQUIRE(!f.is_null()); Vector<String> header = f->get_csv_line(); // Default delimiter: ",". REQUIRE(header.size() == 3); @@ -81,6 +82,7 @@ TEST_CASE("[FileAccess] CSV read") { TEST_CASE("[FileAccess] Get as UTF-8 String") { Ref<FileAccess> f_lf = FileAccess::open(TestUtils::get_data_path("line_endings_lf.test.txt"), FileAccess::READ); + REQUIRE(!f_lf.is_null()); String s_lf = f_lf->get_as_utf8_string(); f_lf->seek(0); String s_lf_nocr = f_lf->get_as_utf8_string(true); @@ -88,6 +90,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_lf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_crlf = FileAccess::open(TestUtils::get_data_path("line_endings_crlf.test.txt"), FileAccess::READ); + REQUIRE(!f_crlf.is_null()); String s_crlf = f_crlf->get_as_utf8_string(); f_crlf->seek(0); String s_crlf_nocr = f_crlf->get_as_utf8_string(true); @@ -95,6 +98,7 @@ TEST_CASE("[FileAccess] Get as UTF-8 String") { CHECK(s_crlf_nocr == "Hello darkness\nMy old friend\nI've come to talk\nWith you again\n"); Ref<FileAccess> f_cr = FileAccess::open(TestUtils::get_data_path("line_endings_cr.test.txt"), FileAccess::READ); + REQUIRE(!f_cr.is_null()); String s_cr = f_cr->get_as_utf8_string(); f_cr->seek(0); String s_cr_nocr = f_cr->get_as_utf8_string(true); diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 3d52bc96d1..763eaed2f8 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -107,6 +107,7 @@ TEST_CASE("[Image] Saving and loading") { // Load BMP Ref<Image> image_bmp = memnew(Image()); Ref<FileAccess> f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err); + REQUIRE(!f_bmp.is_null()); PackedByteArray data_bmp; data_bmp.resize(f_bmp->get_length() + 1); f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length()); @@ -117,6 +118,7 @@ TEST_CASE("[Image] Saving and loading") { // Load JPG Ref<Image> image_jpg = memnew(Image()); Ref<FileAccess> f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err); + REQUIRE(!f_jpg.is_null()); PackedByteArray data_jpg; data_jpg.resize(f_jpg->get_length() + 1); f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length()); @@ -127,6 +129,7 @@ TEST_CASE("[Image] Saving and loading") { // Load WebP Ref<Image> image_webp = memnew(Image()); Ref<FileAccess> f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err); + REQUIRE(!f_webp.is_null()); PackedByteArray data_webp; data_webp.resize(f_webp->get_length() + 1); f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length()); @@ -137,6 +140,7 @@ TEST_CASE("[Image] Saving and loading") { // Load PNG Ref<Image> image_png = memnew(Image()); Ref<FileAccess> f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err); + REQUIRE(!f_png.is_null()); PackedByteArray data_png; data_png.resize(f_png->get_length() + 1); f_png->get_buffer(data_png.ptrw(), f_png->get_length()); @@ -147,6 +151,7 @@ TEST_CASE("[Image] Saving and loading") { // Load TGA Ref<Image> image_tga = memnew(Image()); Ref<FileAccess> f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err); + REQUIRE(!f_tga.is_null()); PackedByteArray data_tga; data_tga.resize(f_tga->get_length() + 1); f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length()); diff --git a/tests/display_server_mock.h b/tests/display_server_mock.h new file mode 100644 index 0000000000..1736f2c452 --- /dev/null +++ b/tests/display_server_mock.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* display_server_mock.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 DISPLAY_SERVER_MOCK_H +#define DISPLAY_SERVER_MOCK_H + +#include "servers/display_server_headless.h" + +#include "servers/rendering/dummy/rasterizer_dummy.h" + +// Specialized DisplayServer for unittests based on DisplayServerHeadless, that +// additionally supports rudimentary InputEvent handling and mouse position. +class DisplayServerMock : public DisplayServerHeadless { +private: + friend class DisplayServer; + + Point2i mouse_position = Point2i(-1, -1); // Outside of Window. + bool window_over = false; + Callable event_callback; + Callable input_event_callback; + + static Vector<String> get_rendering_drivers_func() { + Vector<String> drivers; + drivers.push_back("dummy"); + return drivers; + } + + static DisplayServer *create_func(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, DisplayServer::VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Error &r_error) { + r_error = OK; + RasterizerDummy::make_current(); + return memnew(DisplayServerMock()); + } + + static void _dispatch_input_events(const Ref<InputEvent> &p_event) { + static_cast<DisplayServerMock *>(get_singleton())->_dispatch_input_event(p_event); + } + + void _dispatch_input_event(const Ref<InputEvent> &p_event) { + Variant ev = p_event; + Variant *evp = &ev; + Variant ret; + Callable::CallError ce; + + if (input_event_callback.is_valid()) { + input_event_callback.callp((const Variant **)&evp, 1, ret, ce); + } + } + + void _set_mouse_position(const Point2i &p_position) { + if (mouse_position == p_position) { + return; + } + mouse_position = p_position; + _set_window_over(Rect2i(Point2i(0, 0), window_get_size()).has_point(p_position)); + } + + void _set_window_over(bool p_over) { + if (p_over == window_over) { + return; + } + window_over = p_over; + _send_window_event(p_over ? WINDOW_EVENT_MOUSE_ENTER : WINDOW_EVENT_MOUSE_EXIT); + } + + void _send_window_event(WindowEvent p_event) { + if (!event_callback.is_null()) { + Variant event = int(p_event); + Variant *eventp = &event; + Variant ret; + Callable::CallError ce; + event_callback.callp((const Variant **)&eventp, 1, ret, ce); + } + } + +public: + bool has_feature(Feature p_feature) const override { + switch (p_feature) { + case FEATURE_MOUSE: + return true; + default: { + } + } + return false; + } + + String get_name() const override { return "mock"; } + + // You can simulate DisplayServer-events by calling this function. + // The events will be deliverd to Godot's Input-system. + // Mouse-events (Button & Motion) will additionally update the DisplayServer's mouse position. + void simulate_event(Ref<InputEvent> p_event) { + Ref<InputEventMouse> me = p_event; + if (me.is_valid()) { + _set_mouse_position(me->get_position()); + } + Input::get_singleton()->parse_input_event(p_event); + } + + virtual Point2i mouse_get_position() const override { return mouse_position; } + + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override { + return Size2i(1920, 1080); + } + + virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + event_callback = p_callable; + } + + virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override { + input_event_callback = p_callable; + } + + static void register_mock_driver() { + register_create_function("mock", create_func, get_rendering_drivers_func); + } + + DisplayServerMock() { + Input::get_singleton()->set_event_dispatch_function(_dispatch_input_events); + } + ~DisplayServerMock() {} +}; + +#endif // DISPLAY_SERVER_MOCK_H diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 944f9cb9f6..f2663b037d 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3271,6 +3271,7 @@ TEST_CASE("[SceneTree][TextEdit] mouse") { TEST_CASE("[SceneTree][TextEdit] caret") { TextEdit *text_edit = memnew(TextEdit); + text_edit->set_context_menu_enabled(false); // Prohibit sending InputEvents to the context menu. SceneTree::get_singleton()->get_root()->add_child(text_edit); text_edit->set_size(Size2(800, 200)); diff --git a/tests/test_macros.h b/tests/test_macros.h index 3074c1abf5..80a93c8327 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -31,6 +31,8 @@ #ifndef TEST_MACROS_H #define TEST_MACROS_H +#include "display_server_mock.h" + #include "core/core_globals.h" #include "core/input/input_map.h" #include "core/object/message_queue.h" @@ -139,13 +141,15 @@ int register_test_command(String p_command, TestFunc p_function); // SEND_GUI_MOUSE_MOTION_EVENT - takes an object, position, mouse mask and modifiers e.g SEND_GUI_MOUSE_MOTION_EVENT(code_edit, Vector2(50, 50), MouseButtonMask::LEFT, KeyModifierMask::META); // SEND_GUI_DOUBLE_CLICK - takes an object, position and modifiers. e.g SEND_GUI_DOUBLE_CLICK(code_edit, Vector2(50, 50), KeyModifierMask::META); +#define _SEND_DISPLAYSERVER_EVENT(m_event) ((DisplayServerMock *)(DisplayServer::get_singleton()))->simulate_event(m_event); + #define SEND_GUI_ACTION(m_object, m_action) \ { \ const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(m_action); \ const List<Ref<InputEvent>>::Element *first_event = events->front(); \ Ref<InputEventKey> event = first_event->get(); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -153,7 +157,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ Ref<InputEventKey> event = InputEventKey::create_reference(m_input); \ event->set_pressed(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -176,7 +180,7 @@ int register_test_command(String p_command, TestFunc p_function); #define SEND_GUI_MOUSE_BUTTON_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers) \ { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -184,7 +188,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, m_input, m_mask, m_modifers); \ event->set_pressed(false); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -192,7 +196,7 @@ int register_test_command(String p_command, TestFunc p_function); { \ _CREATE_GUI_MOUSE_EVENT(m_object, m_local_pos, MouseButton::LEFT, 0, m_modifers); \ event->set_double_click(true); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ } @@ -207,7 +211,7 @@ int register_test_command(String p_command, TestFunc p_function); event->set_button_mask(m_mask); \ event->set_relative(Vector2(10, 10)); \ _UPDATE_EVENT_MODIFERS(event, m_modifers); \ - m_object->get_viewport()->push_input(event); \ + _SEND_DISPLAYSERVER_EVENT(event); \ MessageQueue::get_singleton()->flush(); \ CoreGlobals::print_error_enabled = errors_enabled; \ } diff --git a/tests/test_main.cpp b/tests/test_main.cpp index c7f2d4cbfb..ea6058f707 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -107,6 +107,7 @@ #include "modules/modules_tests.gen.h" +#include "tests/display_server_mock.h" #include "tests/test_macros.h" #include "scene/theme/theme_db.h" @@ -126,6 +127,7 @@ int test_main(int argc, char *argv[]) { args.push_back(String::utf8(argv[i])); } OS::get_singleton()->set_cmdline("", args, List<String>()); + DisplayServerMock::register_mock_driver(); // Run custom test tools. if (test_commands) { @@ -200,11 +202,12 @@ struct GodotTestCaseListener : public doctest::IReporter { memnew(MessageQueue); memnew(Input); + Input::get_singleton()->set_use_accumulated_input(false); Error err = OK; OS::get_singleton()->set_has_server_feature_callback(nullptr); for (int i = 0; i < DisplayServer::get_create_function_count(); i++) { - if (String("headless") == DisplayServer::get_create_function_name(i)) { + if (String("mock") == DisplayServer::get_create_function_name(i)) { DisplayServer::create(i, "", DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED, DisplayServer::VSyncMode::VSYNC_ENABLED, 0, nullptr, Vector2i(0, 0), DisplayServer::SCREEN_PRIMARY, err); break; } |