summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/core_bind.cpp14
-rw-r--r--core/doc_data.cpp12
-rw-r--r--core/doc_data.h2
-rw-r--r--core/extension/gdextension_interface.cpp7
-rw-r--r--core/extension/gdextension_interface.h1
-rw-r--r--core/variant/array.cpp137
-rw-r--r--core/variant/array.h9
-rw-r--r--core/variant/container_type_validate.h11
-rw-r--r--core/variant/typed_array.h42
-rw-r--r--core/variant/variant_call.cpp11
-rw-r--r--core/variant/variant_internal.h19
-rw-r--r--core/variant/variant_parser.cpp148
-rw-r--r--doc/classes/Area2D.xml5
-rw-r--r--doc/classes/Area3D.xml5
-rw-r--r--doc/classes/Array.xml25
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/EditorPlugin.xml1
-rw-r--r--doc/classes/Label3D.xml6
-rw-r--r--doc/classes/PhysicsServer2D.xml18
-rw-r--r--doc/classes/PhysicsServer3D.xml26
-rw-r--r--doc/classes/RenderingServer.xml18
-rw-r--r--doc/classes/SpriteBase3D.xml6
-rw-r--r--doc/classes/VideoStream.xml14
-rw-r--r--doc/classes/VideoStreamPlayback.xml104
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp3
-rw-r--r--editor/doc_tools.cpp15
-rw-r--r--editor/editor_node.cpp7
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_properties_array_dict.cpp158
-rw-r--r--editor/editor_properties_array_dict.h2
-rw-r--r--editor/import/resource_importer_scene.cpp13
-rw-r--r--editor/import/resource_importer_scene.h18
-rw-r--r--editor/import/scene_import_settings.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp61
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h8
-rw-r--r--editor/progress_dialog.cpp1
-rw-r--r--editor/project_converter_3_to_4.cpp12
-rw-r--r--modules/csg/csg_shape.cpp4
-rw-r--r--modules/gdscript/gdscript.cpp145
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp280
-rw-r--r--modules/gdscript/gdscript_analyzer.h13
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp34
-rw-r--r--modules/gdscript/gdscript_compiler.cpp27
-rw-r--r--modules/gdscript/gdscript_parser.cpp48
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_vm.cpp87
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp32
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h4
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd (renamed from modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd204
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd2
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out2
-rw-r--r--modules/gltf/gltf_document.cpp17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rid.cs76
-rw-r--r--modules/openxr/doc_classes/OpenXRInterface.xml21
-rw-r--r--modules/openxr/openxr_interface.cpp36
-rw-r--r--modules/openxr/openxr_interface.h4
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml15
-rw-r--r--modules/theora/video_stream_theora.cpp31
-rw-r--r--modules/theora/video_stream_theora.h15
-rw-r--r--platform/windows/gl_manager_windows.h1
-rw-r--r--platform/windows/os_windows.cpp6
-rw-r--r--scene/2d/area_2d.cpp16
-rw-r--r--scene/2d/area_2d.h6
-rw-r--r--scene/3d/area_3d.cpp16
-rw-r--r--scene/3d/area_3d.h6
-rw-r--r--scene/3d/bone_attachment_3d.cpp5
-rw-r--r--scene/3d/label_3d.cpp33
-rw-r--r--scene/3d/label_3d.h8
-rw-r--r--scene/3d/sprite_3d.cpp33
-rw-r--r--scene/3d/sprite_3d.h8
-rw-r--r--scene/animation/animation_tree.cpp2
-rw-r--r--scene/gui/video_stream_player.cpp7
-rw-r--r--scene/gui/video_stream_player.h1
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/importer_mesh.cpp22
-rw-r--r--scene/resources/material.cpp6
-rw-r--r--scene/resources/material.h2
-rw-r--r--scene/resources/packed_scene.cpp14
-rw-r--r--scene/resources/resource_format_text.cpp24
-rw-r--r--scene/resources/video_stream.cpp198
-rw-r--r--scene/resources/video_stream.h76
-rw-r--r--servers/physics_2d/godot_area_2d.cpp25
-rw-r--r--servers/physics_2d/godot_area_2d.h10
-rw-r--r--servers/physics_3d/godot_area_3d.cpp25
-rw-r--r--servers/physics_3d/godot_area_3d.h10
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.cpp5
-rw-r--r--servers/physics_server_2d.cpp3
-rw-r--r--servers/physics_server_2d.h3
-rw-r--r--servers/physics_server_3d.cpp3
-rw-r--r--servers/physics_server_3d.h3
-rw-r--r--servers/rendering/renderer_rd/environment/sky.cpp12
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp7
-rw-r--r--servers/rendering/renderer_viewport.cpp16
-rw-r--r--servers/rendering/renderer_viewport.h6
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering_server.cpp7
-rw-r--r--servers/rendering_server.h10
-rw-r--r--tests/core/io/test_file_access.h4
-rw-r--r--tests/core/io/test_image.h5
123 files changed, 1927 insertions, 856 deletions
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/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/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/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/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="&quot;&quot;">
+ 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/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/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_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/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/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..319da02d7a 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -311,8 +311,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
@@ -509,8 +509,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
@@ -1111,8 +1111,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
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/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..f788236af3 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);
@@ -1970,11 +1973,8 @@ 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));
- }
+ 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() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
push_error("A void function cannot return a value.", p_return);
@@ -2183,49 +2183,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");
+ }
+ 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 (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);
+ 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 +2220,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 +2299,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);
}
}
}
@@ -2822,7 +2802,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);
@@ -2933,6 +2913,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 +3022,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;
}
@@ -3585,12 +3571,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 +3895,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();
+ }
+ }
+}
+
+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));
}
- if (p_is_const) {
- dict.make_read_only();
+
+ 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);
+ }
+ }
}
- p_dictionary->is_constant = true;
- p_dictionary->reduced_value = dict;
+
+ return result;
}
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
@@ -4437,14 +4505,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 +4608,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;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index cd2c4c6569..613399fb40 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -102,10 +102,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 +120,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,7 +128,7 @@ 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
@@ -137,6 +140,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..373d8a3efd 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1904,14 +1904,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 +2044,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 +2084,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..bb08b017c9 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;
}
}
@@ -982,14 +944,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 {
@@ -2921,7 +2883,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);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 74e12d0b5e..bb4549c15c 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;
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/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/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/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/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_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/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/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/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_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_server_2d.cpp b/servers/physics_server_2d.cpp
index 214de27b35..15c2749484 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);
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..864774374b 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);
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());