summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/OS.xml2
-rw-r--r--doc/classes/ProjectSettings.xml7
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp1
-rw-r--r--drivers/gles3/shaders/skeleton.glsl17
-rw-r--r--drivers/gles3/storage/mesh_storage.cpp25
-rw-r--r--drivers/gles3/storage/mesh_storage.h2
-rw-r--r--editor/editor_node.cpp11
-rw-r--r--modules/gdscript/gdscript.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp225
-rw-r--r--modules/gdscript/gdscript_analyzer.h5
-rw-r--r--modules/gdscript/gdscript_warning.cpp9
-rw-r--r--modules/gdscript/gdscript_warning.h3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd24
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/const_conversions.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd34
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/return_conversions.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.gd (renamed from modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.gd (renamed from modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out6
-rw-r--r--scene/2d/animated_sprite_2d.cpp3
-rw-r--r--scene/3d/sprite_3d.cpp3
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/animation/animation_tree.h6
-rw-r--r--scene/gui/graph_edit.cpp5
-rw-r--r--servers/rendering/dummy/storage/mesh_storage.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/skeleton.glsl17
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp21
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.h10
-rw-r--r--servers/rendering/storage/mesh_storage.h1
51 files changed, 384 insertions, 138 deletions
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 0058bf8a2f..04895c28a8 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -498,7 +498,7 @@
<description>
Returns [code]true[/code] if the Godot binary used to run the project is a [i]debug[/i] export template, or when running in the editor.
Returns [code]false[/code] if the Godot binary used to run the project is a [i]release[/i] export template.
- To check whether the Godot binary used to run the project is an export template (debug or release), use [code]OS.has_feature("standalone")[/code] instead.
+ To check whether the Godot binary used to run the project is an export template (debug or release), use [code]OS.has_feature("template")[/code] instead.
</description>
</method>
<method name="is_keycode_unicode" qualifiers="const">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 430f1c60fd..fa381adbb3 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -408,8 +408,11 @@
<member name="debug/gdscript/warnings/incompatible_ternary" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a ternary operator may emit values with incompatible types.
</member>
- <member name="debug/gdscript/warnings/int_assigned_to_enum" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to assign an integer to a variable that expects an enum value.
+ <member name="debug/gdscript/warnings/int_as_enum_without_cast" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum without an explicit cast.
+ </member>
+ <member name="debug/gdscript/warnings/int_as_enum_without_match" type="int" setter="" getter="" default="1">
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when trying to use an integer as an enum when there is no matching enum member for that numeric value.
</member>
<member name="debug/gdscript/warnings/integer_division" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when dividing an integer by another integer (the decimal part will be discarded).
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index fbe00d22af..8888d11a24 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -440,6 +440,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_
const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c);
if (cm->mesh_instance.is_valid()) {
mesh_storage->mesh_instance_check_for_update(cm->mesh_instance);
+ mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform);
update_skeletons = true;
}
}
diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl
index a1e3c098f4..aad856a5a2 100644
--- a/drivers/gles3/shaders/skeleton.glsl
+++ b/drivers/gles3/shaders/skeleton.glsl
@@ -87,6 +87,16 @@ uniform highp float blend_weight;
uniform lowp float blend_shape_count;
#endif
+#ifdef USE_SKELETON
+uniform mediump vec2 skeleton_transform_x;
+uniform mediump vec2 skeleton_transform_y;
+uniform mediump vec2 skeleton_transform_offset;
+
+uniform mediump vec2 inverse_transform_x;
+uniform mediump vec2 inverse_transform_y;
+uniform mediump vec2 inverse_transform_offset;
+#endif
+
vec2 signNotZero(vec2 v) {
return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0)));
}
@@ -164,10 +174,13 @@ void main() {
m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z);
m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w);
+ mat4 skeleton_matrix = mat4(vec4(skeleton_transform_x, 0.0, 0.0), vec4(skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(skeleton_transform_offset, 0.0, 1.0));
+ mat4 inverse_matrix = mat4(vec4(inverse_transform_x, 0.0, 0.0), vec4(inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(inverse_transform_offset, 0.0, 1.0));
mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
- //reverse order because its transposed
- out_vertex = (vec4(out_vertex, 0.0, 1.0) * bone_matrix).xy;
+ bone_matrix = skeleton_matrix * transpose(bone_matrix) * inverse_matrix;
+
+ out_vertex = (bone_matrix * vec4(out_vertex, 0.0, 1.0)).xy;
#endif // USE_SKELETON
#else // MODE_2D
diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp
index 71f262af20..5ba0c5a09c 100644
--- a/drivers/gles3/storage/mesh_storage.cpp
+++ b/drivers/gles3/storage/mesh_storage.cpp
@@ -997,6 +997,11 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
}
}
+void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) {
+ MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
+ mi->canvas_item_transform_2d = p_transform;
+}
+
void MeshStorage::_blend_shape_bind_mesh_instance_buffer(MeshInstance *p_mi, uint32_t p_surface) {
glBindBuffer(GL_ARRAY_BUFFER, p_mi->surfaces[p_surface].vertex_buffers[0]);
@@ -1163,6 +1168,16 @@ void MeshStorage::update_mesh_instances() {
skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::BLEND_SHAPE_COUNT, float(mi->mesh->blend_shape_count), skeleton_shader.shader_version, variant, specialization);
if (can_use_skeleton) {
+ Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d;
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_X, transform[0], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_Y, transform[1], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_OFFSET, transform[2], skeleton_shader.shader_version, variant, specialization);
+
+ Transform2D inverse_transform = transform.affine_inverse();
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_X, inverse_transform[0], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_Y, inverse_transform[1], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization);
+
// Do last blendshape in the same pass as the Skeleton.
_compute_skeleton(mi, sk, i);
can_use_skeleton = false;
@@ -1201,6 +1216,16 @@ void MeshStorage::update_mesh_instances() {
continue;
}
+ Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d;
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_X, transform[0], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_Y, transform[1], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::SKELETON_TRANSFORM_OFFSET, transform[2], skeleton_shader.shader_version, variant, specialization);
+
+ Transform2D inverse_transform = transform.affine_inverse();
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_X, inverse_transform[0], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_Y, inverse_transform[1], skeleton_shader.shader_version, variant, specialization);
+ skeleton_shader.shader.version_set_uniform(SkeletonShaderGLES3::INVERSE_TRANSFORM_OFFSET, inverse_transform[2], skeleton_shader.shader_version, variant, specialization);
+
glBindVertexArray(mi->mesh->surfaces[i]->skeleton_vertex_array);
_compute_skeleton(mi, sk, i);
}
diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h
index 2efc57462b..e1c2bc3f63 100644
--- a/drivers/gles3/storage/mesh_storage.h
+++ b/drivers/gles3/storage/mesh_storage.h
@@ -163,6 +163,7 @@ struct MeshInstance {
bool weights_dirty = false;
SelfList<MeshInstance> weight_update_list;
SelfList<MeshInstance> array_update_list;
+ Transform2D canvas_item_transform_2d;
MeshInstance() :
weight_update_list(this), array_update_list(this) {}
};
@@ -423,6 +424,7 @@ public:
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override;
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override;
virtual void mesh_instance_check_for_update(RID p_mesh_instance) override;
+ virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override;
virtual void update_mesh_instances() override;
// TODO: considering hashing versions with multimesh buffer RID.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 0d5c820a3c..8b28319a1d 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2096,14 +2096,25 @@ void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) {
if (!item_plugins.is_empty()) {
ObjectID owner_id = p_editing_owner->get_instance_id();
+ List<EditorPlugin *> to_remove;
for (EditorPlugin *plugin : active_plugins[owner_id]) {
if (!item_plugins.has(plugin)) {
+ // Remove plugins no longer used by this editing owner.
+ to_remove.push_back(plugin);
plugin->make_visible(false);
plugin->edit(nullptr);
}
}
+ for (EditorPlugin *plugin : to_remove) {
+ active_plugins[owner_id].erase(plugin);
+ }
+
for (EditorPlugin *plugin : item_plugins) {
+ if (active_plugins[owner_id].has(plugin)) {
+ continue;
+ }
+
for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) {
if (kv.key != owner_id) {
EditorPropertyResource *epres = Object::cast_to<EditorPropertyResource>(ObjectDB::get_instance(kv.key));
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4fc3929bbd..d9b8a540c0 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -98,7 +98,7 @@ Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p
return Object::callp(p_method, p_args, p_argcount, r_error);
}
MethodBind *method = ClassDB::get_method(name, p_method);
- if (method) {
+ if (method && method->is_static()) {
// Native static method.
return method->call(nullptr, p_args, p_argcount, r_error);
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 8c3521c530..3ad8f12192 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -160,19 +160,6 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
return type;
}
-static StringName enum_get_value_name(const GDScriptParser::DataType p_type, int64_t p_val) {
- // Check that an enum has a given value, not key.
- // Make sure that implicit conversion to int64_t is sensible before calling!
- HashMap<StringName, int64_t>::ConstIterator i = p_type.enum_values.begin();
- while (i) {
- if (i->value == p_val) {
- return i->key;
- }
- ++i;
- }
- return StringName();
-}
-
bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_member) {
if (p_class->members_indices.has(p_member_name)) {
int index = p_class->members_indices[p_member_name];
@@ -1596,6 +1583,9 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
}
}
+ if (has_specified_type && p_assignable->initializer->is_constant) {
+ update_const_expression_builtin_type(p_assignable->initializer, specified_type, "assign");
+ }
GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype();
if (p_assignable->infer_datatype) {
@@ -1630,7 +1620,7 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
downgrade_node_type_source(p_assignable->initializer);
}
} else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
- if (!is_constant && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
+ if (!is_constant && is_type_compatible(initializer_type, specified_type)) {
mark_node_unsafe(p_assignable->initializer);
p_assignable->use_conversion_assign = true;
} else {
@@ -1958,11 +1948,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
GDScriptParser::DataType result;
GDScriptParser::DataType expected_type;
- bool has_expected_type = false;
-
- if (parser->current_function != nullptr) {
+ bool has_expected_type = parser->current_function != nullptr;
+ if (has_expected_type) {
expected_type = parser->current_function->get_datatype();
- has_expected_type = true;
}
if (p_return->return_value != nullptr) {
@@ -1976,6 +1964,9 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
push_error("A void function cannot return a value.", p_return);
}
+ if (has_expected_type && expected_type.is_hard_type() && p_return->return_value->is_constant) {
+ update_const_expression_builtin_type(p_return->return_value, expected_type, "return");
+ }
result = p_return->return_value->get_datatype();
} else {
// Return type is null by default.
@@ -1985,24 +1976,21 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result.is_constant = true;
}
- if (has_expected_type) {
- expected_type.is_meta_type = false;
- if (expected_type.is_hard_type()) {
- if (!is_type_compatible(expected_type, result)) {
- // Try other way. Okay but not safe.
- if (!is_type_compatible(result, expected_type)) {
- push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_return);
- }
+ if (has_expected_type && !expected_type.is_variant()) {
+ if (result.is_variant() || !result.is_hard_type()) {
+ mark_node_unsafe(p_return);
+ if (!is_type_compatible(expected_type, result, true, p_return)) {
+ downgrade_node_type_source(p_return);
+ }
+ } else if (!is_type_compatible(expected_type, result, true, p_return)) {
+ mark_node_unsafe(p_return);
+ if (!is_type_compatible(result, expected_type)) {
+ push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return);
+ }
#ifdef DEBUG_ENABLED
- } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
- } else if (result.is_variant() && !expected_type.is_variant()) {
- mark_node_unsafe(p_return);
+ } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
#endif
- }
}
}
@@ -2116,6 +2104,68 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
p_array->set_datatype(arr_type);
}
+#ifdef DEBUG_ENABLED
+static bool enum_has_value(const GDScriptParser::DataType p_type, int64_t p_value) {
+ for (const KeyValue<StringName, int64_t> &E : p_type.enum_values) {
+ if (E.value == p_value) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast) {
+ if (p_expression->get_datatype() == p_type) {
+ return;
+ }
+ if (p_type.kind != GDScriptParser::DataType::BUILTIN && p_type.kind != GDScriptParser::DataType::ENUM) {
+ return;
+ }
+
+ GDScriptParser::DataType expression_type = p_expression->get_datatype();
+ bool is_enum_cast = p_is_cast && p_type.kind == GDScriptParser::DataType::ENUM && p_type.is_meta_type == false && expression_type.builtin_type == Variant::INT;
+ if (!is_enum_cast && !is_type_compatible(p_type, expression_type, true, p_expression)) {
+ push_error(vformat(R"(Cannot %s a value of type "%s" as "%s".)", p_usage, expression_type.to_string(), p_type.to_string()), p_expression);
+ return;
+ }
+
+ GDScriptParser::DataType value_type = type_from_variant(p_expression->reduced_value, p_expression);
+ if (expression_type.is_variant() && !is_enum_cast && !is_type_compatible(p_type, value_type, true, p_expression)) {
+ push_error(vformat(R"(Cannot %s a value of type "%s" as "%s".)", p_usage, value_type.to_string(), p_type.to_string()), p_expression);
+ return;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (p_type.kind == GDScriptParser::DataType::ENUM && value_type.builtin_type == Variant::INT && !enum_has_value(p_type, p_expression->reduced_value)) {
+ parser->push_warning(p_expression, GDScriptWarning::INT_AS_ENUM_WITHOUT_MATCH, p_usage, p_expression->reduced_value.stringify(), p_type.to_string());
+ }
+#endif
+
+ if (value_type.builtin_type == p_type.builtin_type) {
+ p_expression->set_datatype(p_type);
+ return;
+ }
+
+ Variant converted_to;
+ const Variant *converted_from = &p_expression->reduced_value;
+ Callable::CallError call_error;
+ Variant::construct(p_type.builtin_type, converted_to, &converted_from, 1, call_error);
+ if (call_error.error) {
+ push_error(vformat(R"(Failed to convert a value of type "%s" to "%s".)", value_type.to_string(), p_type.to_string()), p_expression);
+ return;
+ }
+
+#ifdef DEBUG_ENABLED
+ if (p_type.builtin_type == Variant::INT && value_type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_expression, GDScriptWarning::NARROWING_CONVERSION);
+ }
+#endif
+
+ p_expression->reduced_value = converted_to;
+ p_expression->set_datatype(p_type);
+}
+
// 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) {
@@ -2182,6 +2232,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
}
+ if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
+ update_const_expression_builtin_type(p_assignment->assigned_value, assignee_type, "assign");
+ }
+
GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
bool assignee_is_variant = assignee_type.is_variant();
@@ -2242,7 +2296,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
// non-variant assignee and incompatible result
mark_node_unsafe(p_assignment);
if (assignee_is_hard) {
- if (is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) {
+ if (is_type_compatible(op_type, assignee_type)) {
// hard non-variant assignee and maybe compatible result
p_assignment->use_conversion_assign = true;
} else {
@@ -2358,7 +2412,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
GDScriptParser::DataType test_type = right_type;
test_type.is_meta_type = false;
- if (!is_type_compatible(test_type, left_type, false)) {
+ if (!is_type_compatible(test_type, left_type)) {
push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)"), p_binary_op->left_operand);
p_binary_op->reduced_value = false;
} else {
@@ -2379,7 +2433,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
GDScriptParser::DataType test_type = right_type;
test_type.is_meta_type = false;
- if (!is_type_compatible(test_type, left_type, false) && !is_type_compatible(left_type, test_type, false)) {
+ if (!is_type_compatible(test_type, left_type) && !is_type_compatible(left_type, test_type)) {
if (left_type.is_hard_type()) {
push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", left_type.to_string(), test_type.to_string()), p_binary_op->left_operand);
} else {
@@ -2555,6 +2609,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (types_match) {
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ if (p_call->arguments[i]->is_constant) {
+ update_const_expression_builtin_type(p_call->arguments[i], type_from_property(info.arguments[i], true), "pass");
+ }
+ }
match = true;
call_type = type_from_property(info.return_val);
break;
@@ -2851,67 +2910,39 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
p_cast->set_datatype(cast_type);
+ if (p_cast->operand->is_constant) {
+ update_const_expression_builtin_type(p_cast->operand, cast_type, "cast", true);
+ if (cast_type.is_variant() || p_cast->operand->get_datatype() == cast_type) {
+ p_cast->is_constant = true;
+ p_cast->reduced_value = p_cast->operand->reduced_value;
+ }
+ }
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
- if (!op_type.is_variant()) {
+ if (op_type.is_variant() || !op_type.is_hard_type()) {
+ mark_node_unsafe(p_cast);
+#ifdef DEBUG_ENABLED
+ if (op_type.is_variant() && !op_type.is_hard_type()) {
+ parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string());
+ }
+#endif
+ } else {
bool valid = false;
- bool more_informative_error = false;
- if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Enum casts are compatible when value from operand exists in target enum
- if (p_cast->operand->is_constant && p_cast->operand->reduced) {
- if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
- valid = true;
- } else {
- valid = false;
- more_informative_error = true;
- push_error(vformat(R"(Invalid cast. Enum "%s" does not have value corresponding to "%s.%s" (%d).)",
- cast_type.to_string(), op_type.enum_type,
- enum_get_value_name(op_type, p_cast->operand->reduced_value), // Can never be null
- p_cast->operand->reduced_value.operator uint64_t()),
- p_cast->cast_type);
- }
- } else {
- // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
- mark_node_unsafe(p_cast);
- valid = true;
- }
- } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
- // Int assignment to enum not valid when exact int assigned is known but is not an enum value
- if (p_cast->operand->is_constant && p_cast->operand->reduced) {
- if (enum_get_value_name(cast_type, p_cast->operand->reduced_value) != StringName()) {
- valid = true;
- } else {
- valid = false;
- more_informative_error = true;
- push_error(vformat(R"(Invalid cast. Enum "%s" does not have enum value %d.)", cast_type.to_string(), p_cast->operand->reduced_value.operator uint64_t()), p_cast->cast_type);
- }
- } else {
- // Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
- mark_node_unsafe(p_cast);
- valid = true;
- }
+ if (op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) {
+ mark_node_unsafe(p_cast);
+ valid = true;
} else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) {
valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type);
} else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) {
valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type);
}
- if (!valid && !more_informative_error) {
+ if (!valid) {
push_error(vformat(R"(Invalid cast. Cannot convert from "%s" to "%s".)", op_type.to_string(), cast_type.to_string()), p_cast->cast_type);
}
}
- } else {
- mark_node_unsafe(p_cast);
}
-#ifdef DEBUG_ENABLED
- if (p_cast->operand->get_datatype().is_variant()) {
- parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string());
- mark_node_unsafe(p_cast);
- }
-#endif
-
- // TODO: Perform cast on constants.
}
void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
@@ -4210,26 +4241,22 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
return true;
}
-bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) {
+void GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) {
List<GDScriptParser::DataType> arg_types;
for (const PropertyInfo &E : p_method.arguments) {
arg_types.push_back(type_from_property(E, true));
}
- return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
+ validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
}
-bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call) {
- bool valid = true;
-
+void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call) {
if (p_call->arguments.size() < p_par_types.size() - p_default_args_count) {
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", p_call->function_name, p_par_types.size() - p_default_args_count, p_call->arguments.size()), p_call);
- valid = false;
}
if (!p_is_vararg && p_call->arguments.size() > p_par_types.size()) {
push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", p_call->function_name, p_par_types.size(), p_call->arguments.size()), p_call->arguments[p_par_types.size()]);
- valid = false;
}
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -4238,9 +4265,13 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
break;
}
GDScriptParser::DataType par_type = p_par_types[i];
+
+ if (par_type.is_hard_type() && p_call->arguments[i]->is_constant) {
+ update_const_expression_builtin_type(p_call->arguments[i], par_type, "pass");
+ }
GDScriptParser::DataType arg_type = p_call->arguments[i]->get_datatype();
- if (arg_type.is_variant() && !(par_type.is_hard_type() && par_type.is_variant())) {
+ if ((arg_type.is_variant() || !arg_type.is_hard_type()) && !(par_type.is_hard_type() && par_type.is_variant())) {
// Argument can be anything, so this is unsafe.
mark_node_unsafe(p_call->arguments[i]);
} else if (par_type.is_hard_type() && !is_type_compatible(par_type, arg_type, true)) {
@@ -4250,17 +4281,13 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be "%s" but is "%s".)*",
p_call->function_name, i + 1, par_type.to_string(), arg_type.to_string()),
p_call->arguments[i]);
- valid = false;
}
#ifdef DEBUG_ENABLED
- } else {
- if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
- }
+ } else if (par_type.kind == GDScriptParser::DataType::BUILTIN && par_type.builtin_type == Variant::INT && arg_type.kind == GDScriptParser::DataType::BUILTIN && arg_type.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
#endif
}
}
- return valid;
}
#ifdef DEBUG_ENABLED
@@ -4412,7 +4439,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
#ifdef DEBUG_ENABLED
if (p_source_node) {
- parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM);
+ parser->push_warning(p_source_node, GDScriptWarning::INT_AS_ENUM_WITHOUT_CAST);
}
#endif
return true;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 5397be33f0..cd2c4c6569 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -112,10 +112,11 @@ class GDScriptAnalyzer {
GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
bool get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
- bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
- bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
+ void validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
+ void validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
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);
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);
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index a6cbb7f6ae..024fed8517 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -148,9 +148,13 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(3);
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
}
- case INT_ASSIGNED_TO_ENUM: {
+ case INT_AS_ENUM_WITHOUT_CAST: {
return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type.";
}
+ case INT_AS_ENUM_WITHOUT_MATCH: {
+ CHECK_SYMBOLS(3);
+ return vformat(R"(Cannot %s %s as Enum "%s": no enum member has matching value.)", symbols[0], symbols[1], symbols[2]);
+ } break;
case STATIC_CALLED_ON_INSTANCE: {
CHECK_SYMBOLS(2);
return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]);
@@ -221,7 +225,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"REDUNDANT_AWAIT",
"EMPTY_FILE",
"SHADOWED_GLOBAL_IDENTIFIER",
- "INT_ASSIGNED_TO_ENUM",
+ "INT_AS_ENUM_WITHOUT_CAST",
+ "INT_AS_ENUM_WITHOUT_MATCH",
"STATIC_CALLED_ON_INSTANCE",
"CONFUSABLE_IDENTIFIER",
};
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index b485f02b9c..7492972c1a 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -76,7 +76,8 @@ public:
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
EMPTY_FILE, // A script file is empty.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
- INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
+ INT_AS_ENUM_WITHOUT_CAST, // An integer value was used as an enum value without casting.
+ INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
WARNING_MAX,
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd
new file mode 100644
index 0000000000..1b22d173c7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.gd
@@ -0,0 +1,3 @@
+func test():
+ var var_color: String = Color.RED
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out
new file mode 100644
index 0000000000..cc4b1d86bf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/assymetric_assignment_bad.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "Color" as "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
deleted file mode 100644
index 3a8d2a205a..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_ANALYZER_ERROR
-Invalid cast. Enum "cast_enum_bad_enum.gd::MyEnum" does not have value corresponding to "MyOtherEnum.OTHER_ENUM_VALUE_3" (2).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
deleted file mode 100644
index bc0d8b7834..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_ANALYZER_ERROR
-Invalid cast. Enum "cast_enum_bad_int.gd::MyEnum" does not have enum value 2.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
index 02c4633586..84958f1aa2 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum".
+Cannot assign a value of type "enum_class_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
index 441cccbf7b..e294f3496a 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "class_var" with specified type enum_class_var_init_with_wrong_enum_type.gd::MyEnum.
+Cannot assign a value of type "enum_class_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_class_var_init_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
index e85f7d6f9f..a91189e2dd 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_parameter_wrong_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Invalid argument for "enum_func()" function: argument 1 should be "enum_function_parameter_wrong_type.gd::MyEnum" but is "enum_function_parameter_wrong_type.gd::MyOtherEnum".
+Cannot pass a value of type "enum_function_parameter_wrong_type.gd::MyOtherEnum" as "enum_function_parameter_wrong_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
index f7ea3267fa..6b4eba3740 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_function_return_wrong_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot return value of type "enum_function_return_wrong_type.gd::MyOtherEnum" because the function return type is "enum_function_return_wrong_type.gd::MyEnum".
+Cannot return a value of type "enum_function_return_wrong_type.gd::MyOtherEnum" as "enum_function_return_wrong_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
index 38df5a0cd8..616358bb61 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_outer_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" cannot be assigned to a variable of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum".
+Cannot assign a value of type "enum_local_var_assign_outer_with_wrong_enum_type.gd::InnerClass::MyEnum" as "enum_local_var_assign_outer_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
index 2adcbd9edf..af3dde663f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_assign_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" cannot be assigned to a variable of type "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum".
+Cannot assign a value of type "enum_local_var_assign_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_assign_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
index 331113dd30..781b0bc85f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum to variable "local_var" with specified type enum_local_var_init_with_wrong_enum_type.gd::MyEnum.
+Cannot assign a value of type "enum_local_var_init_with_wrong_enum_type.gd::MyOtherEnum" as "enum_local_var_init_with_wrong_enum_type.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
index 6298c026b4..e8c7f86c4f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_preload_unnamed_assign_to_named.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "enum_value_from_parent.gd::<anonymous enum>" cannot be assigned to a variable of type "enum_preload_unnamed_assign_to_named.gd::MyEnum".
+Cannot assign a value of type "enum_value_from_parent.gd::<anonymous enum>" as "enum_preload_unnamed_assign_to_named.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
index b70121ed81..fb18c94d0b 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_unnamed_assign_to_named.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot assign a value of type enum_unnamed_assign_to_named.gd::<anonymous enum> to variable "local_var" with specified type enum_unnamed_assign_to_named.gd::MyEnum.
+Cannot assign a value of type "enum_unnamed_assign_to_named.gd::<anonymous enum>" as "enum_unnamed_assign_to_named.gd::MyEnum".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out
index 53e2b012e6..69af0984f7 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/lambda_wrong_return.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot return value of type "String" because the function return type is "int".
+Cannot return a value of type "String" as "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
index 5e3c446bf6..08a973503f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/preload_enum_error.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Value of type "enum_from_outer.gd::Named" cannot be assigned to a variable of type "preload_enum_error.gd::LocalNamed".
+Cannot assign a value of type "enum_from_outer.gd::Named" as "preload_enum_error.gd::LocalNamed".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd
new file mode 100644
index 0000000000..efd8ad6edb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.gd
@@ -0,0 +1,16 @@
+const const_color: Color = 'red'
+
+func func_color(arg_color: Color = 'blue') -> bool:
+ return arg_color == Color.BLUE
+
+@warning_ignore("assert_always_true")
+func test():
+ assert(const_color == Color.RED)
+
+ assert(func_color() == true)
+ assert(func_color('blue') == true)
+
+ var var_color: Color = 'green'
+ assert(var_color == Color.GREEN)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/assymetric_assignment_good.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd
new file mode 100644
index 0000000000..bed9dd0e96
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.gd
@@ -0,0 +1,24 @@
+const const_float_int: float = 19
+const const_float_plus: float = 12 + 22
+const const_float_cast: float = 76 as float
+
+const const_packed_empty: PackedFloat64Array = []
+const const_packed_ints: PackedFloat64Array = [52]
+
+@warning_ignore("assert_always_true")
+func test():
+ assert(typeof(const_float_int) == TYPE_FLOAT)
+ assert(str(const_float_int) == '19')
+ assert(typeof(const_float_plus) == TYPE_FLOAT)
+ assert(str(const_float_plus) == '34')
+ assert(typeof(const_float_cast) == TYPE_FLOAT)
+ assert(str(const_float_cast) == '76')
+
+ assert(typeof(const_packed_empty) == TYPE_PACKED_FLOAT64_ARRAY)
+ assert(str(const_packed_empty) == '[]')
+ assert(typeof(const_packed_ints) == TYPE_PACKED_FLOAT64_ARRAY)
+ assert(str(const_packed_ints) == '[52]')
+ assert(typeof(const_packed_ints[0]) == TYPE_FLOAT)
+ assert(str(const_packed_ints[0]) == '52')
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/const_conversions.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd
new file mode 100644
index 0000000000..0b1576e66e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.gd
@@ -0,0 +1,34 @@
+func convert_literal_int_to_float() -> float: return 76
+func convert_arg_int_to_float(arg: int) -> float: return arg
+func convert_var_int_to_float() -> float: var number := 59; return number
+
+func convert_literal_array_to_packed() -> PackedStringArray: return ['46']
+func convert_arg_array_to_packed(arg: Array) -> PackedStringArray: return arg
+func convert_var_array_to_packed() -> PackedStringArray: var array := ['79']; return array
+
+func test():
+ var converted_literal_int := convert_literal_int_to_float()
+ assert(typeof(converted_literal_int) == TYPE_FLOAT)
+ assert(converted_literal_int == 76.0)
+
+ var converted_arg_int := convert_arg_int_to_float(36)
+ assert(typeof(converted_arg_int) == TYPE_FLOAT)
+ assert(converted_arg_int == 36.0)
+
+ var converted_var_int := convert_var_int_to_float()
+ assert(typeof(converted_var_int) == TYPE_FLOAT)
+ assert(converted_var_int == 59.0)
+
+ var converted_literal_array := convert_literal_array_to_packed()
+ assert(typeof(converted_literal_array) == TYPE_PACKED_STRING_ARRAY)
+ assert(str(converted_literal_array) == '["46"]')
+
+ var converted_arg_array := convert_arg_array_to_packed(['91'])
+ assert(typeof(converted_arg_array) == TYPE_PACKED_STRING_ARRAY)
+ assert(str(converted_arg_array) == '["91"]')
+
+ var converted_var_array := convert_var_array_to_packed()
+ assert(typeof(converted_var_array) == TYPE_PACKED_STRING_ARRAY)
+ assert(str(converted_var_array) == '["79"]')
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/return_conversions.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.gd
index 71616ea3af..71616ea3af 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_enum.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.gd
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
new file mode 100644
index 0000000000..6e086a0918
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_enum.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> INT_AS_ENUM_WITHOUT_MATCH
+>> Cannot cast 2 as Enum "cast_enum_bad_enum.gd::MyEnum": no enum member has matching value.
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.gd
index 60a31fb318..60a31fb318 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/cast_enum_bad_int.gd
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.gd
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out
new file mode 100644
index 0000000000..c19d57f98e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/cast_enum_bad_int.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> INT_AS_ENUM_WITHOUT_MATCH
+>> Cannot cast 2 as Enum "cast_enum_bad_int.gd::MyEnum": no enum member has matching value.
+2
diff --git a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
index eef13bbff8..b8e243769f 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/enum_assign_int_without_casting.out
@@ -1,19 +1,19 @@
GDTEST_OK
>> WARNING
>> Line: 5
->> INT_ASSIGNED_TO_ENUM
+>> INT_AS_ENUM_WITHOUT_CAST
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
>> WARNING
>> Line: 9
->> INT_ASSIGNED_TO_ENUM
+>> INT_AS_ENUM_WITHOUT_CAST
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
>> WARNING
>> Line: 12
->> INT_ASSIGNED_TO_ENUM
+>> INT_AS_ENUM_WITHOUT_CAST
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
>> WARNING
>> Line: 14
->> INT_ASSIGNED_TO_ENUM
+>> INT_AS_ENUM_WITHOUT_CAST
>> Integer used when an enum value is expected. If this is intended cast the integer to the enum type.
0
1
diff --git a/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd
new file mode 100644
index 0000000000..0c15701364
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.gd
@@ -0,0 +1,6 @@
+# https://github.com/godotengine/godot/issues/66675
+func test():
+ example(Node2D)
+
+func example(thing):
+ print(thing.has_method('asdf'))
diff --git a/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out
new file mode 100644
index 0000000000..3a90f98d99
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/non_static_method_call_on_native_class.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: example()
+>> runtime/errors/non_static_method_call_on_native_class.gd
+>> 6
+>> Invalid call. Nonexistent function 'has_method' in base 'Node2D'.
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index ccf9696d82..8f7006caca 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -475,8 +475,9 @@ void AnimatedSprite2D::play(const StringName &p_name, float p_custom_scale, bool
}
}
- notify_property_list_changed();
set_process_internal(true);
+ notify_property_list_changed();
+ queue_redraw();
}
void AnimatedSprite2D::play_backwards(const StringName &p_name) {
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 635c566ef8..9712df0936 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -1182,8 +1182,9 @@ void AnimatedSprite3D::play(const StringName &p_name, float p_custom_scale, bool
}
}
- notify_property_list_changed();
set_process_internal(true);
+ notify_property_list_changed();
+ _queue_redraw();
}
void AnimatedSprite3D::play_backwards(const StringName &p_name) {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 25de278f97..b0975fbead 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -149,7 +149,7 @@ private:
HashMap<StringName, BezierAnim> bezier_anim;
struct PlayingAudioStreamInfo {
- int64_t index = -1;
+ AudioStreamPlaybackPolyphonic::ID index = -1;
double start = 0.0;
double len = 0.0;
};
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 394a3a2237..c54493828f 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -253,12 +253,14 @@ private:
}
};
+ // Audio stream information for each audio stream placed on the track.
struct PlayingAudioStreamInfo {
- int64_t index = -1;
+ AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic.
double start = 0.0;
double len = 0.0;
};
+ // Audio track information for mixng and ending.
struct PlayingAudioTrackInfo {
HashMap<int, PlayingAudioStreamInfo> stream_info;
double length = 0.0;
@@ -272,7 +274,7 @@ private:
struct TrackCacheAudio : public TrackCache {
Ref<AudioStreamPolyphonic> audio_stream;
Ref<AudioStreamPlaybackPolyphonic> audio_stream_playback;
- HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Animation resource RID & AudioTrack key index: PlayingAudioStreamInfo.
+ HashMap<ObjectID, PlayingAudioTrackInfo> playing_streams; // Key is Animation resource ObjectID.
TrackCacheAudio() {
type = Animation::TYPE_AUDIO;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index af52f6664a..dc23bcb14a 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -920,7 +920,8 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
scaled_points.push_back(points[i] * p_zoom);
}
- p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased);
+ // Thickness below 0.5 doesn't look good on the graph or its minimap.
+ p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased);
}
void GraphEdit::_connections_layer_draw() {
@@ -1088,7 +1089,7 @@ void GraphEdit::_minimap_draw() {
from_color = from_color.lerp(activity_color, E.activity);
to_color = to_color.lerp(activity_color, E.activity);
}
- _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
+ _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
}
// Draw the "camera" viewport.
diff --git a/servers/rendering/dummy/storage/mesh_storage.h b/servers/rendering/dummy/storage/mesh_storage.h
index 2d66c225e8..aba362c956 100644
--- a/servers/rendering/dummy/storage/mesh_storage.h
+++ b/servers/rendering/dummy/storage/mesh_storage.h
@@ -126,6 +126,7 @@ public:
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override {}
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override {}
virtual void mesh_instance_check_for_update(RID p_mesh_instance) override {}
+ virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override {}
virtual void update_mesh_instances() override {}
/* MULTIMESH API */
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 638fe44266..0f4fa1b9c4 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1431,6 +1431,7 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p
const Item::CommandMesh *cm = static_cast<const Item::CommandMesh *>(c);
if (cm->mesh_instance.is_valid()) {
mesh_storage->mesh_instance_check_for_update(cm->mesh_instance);
+ mesh_storage->mesh_instance_set_canvas_item_transform(cm->mesh_instance, canvas_transform_inverse * ci->final_transform);
update_skeletons = true;
}
}
diff --git a/servers/rendering/renderer_rd/shaders/skeleton.glsl b/servers/rendering/renderer_rd/shaders/skeleton.glsl
index f5b233cca0..59c161548c 100644
--- a/servers/rendering/renderer_rd/shaders/skeleton.glsl
+++ b/servers/rendering/renderer_rd/shaders/skeleton.glsl
@@ -51,6 +51,15 @@ layout(push_constant, std430) uniform Params {
bool normalized_blend_shapes;
uint pad0;
uint pad1;
+
+ vec2 skeleton_transform_x;
+ vec2 skeleton_transform_y;
+
+ vec2 skeleton_transform_offset;
+ vec2 inverse_transform_x;
+
+ vec2 inverse_transform_y;
+ vec2 inverse_transform_offset;
}
params;
@@ -158,8 +167,12 @@ void main() {
m += mat4(bone_transforms.data[bones_23.x], bone_transforms.data[bones_23.x + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.x;
m += mat4(bone_transforms.data[bones_23.y], bone_transforms.data[bones_23.y + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)) * weights_23.y;
- //reverse order because its transposed
- vertex = (vec4(vertex, 0.0, 1.0) * m).xy;
+ mat4 skeleton_matrix = mat4(vec4(params.skeleton_transform_x, 0.0, 0.0), vec4(params.skeleton_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.skeleton_transform_offset, 0.0, 1.0));
+ mat4 inverse_matrix = mat4(vec4(params.inverse_transform_x, 0.0, 0.0), vec4(params.inverse_transform_y, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(params.inverse_transform_offset, 0.0, 1.0));
+
+ m = skeleton_matrix * transpose(m) * inverse_matrix;
+
+ vertex = (m * vec4(vertex, 0.0, 1.0)).xy;
}
uint dst_offset = index * params.vertex_stride;
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 2015a2fca0..9124886764 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -930,6 +930,11 @@ void MeshStorage::mesh_instance_check_for_update(RID p_mesh_instance) {
}
}
+void MeshStorage::mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) {
+ MeshInstance *mi = mesh_instance_owner.get_or_null(p_mesh_instance);
+ mi->canvas_item_transform_2d = p_transform;
+}
+
void MeshStorage::update_mesh_instances() {
while (dirty_mesh_instance_weights.first()) {
MeshInstance *mi = dirty_mesh_instance_weights.first()->self();
@@ -981,6 +986,22 @@ void MeshStorage::update_mesh_instances() {
push_constant.skin_stride = (mi->mesh->surfaces[i]->skin_buffer_size / mi->mesh->surfaces[i]->vertex_count) / 4;
push_constant.skin_weight_offset = (mi->mesh->surfaces[i]->format & RS::ARRAY_FLAG_USE_8_BONE_WEIGHTS) ? 4 : 2;
+ Transform2D transform = mi->canvas_item_transform_2d.affine_inverse() * sk->base_transform_2d;
+ push_constant.skeleton_transform_x[0] = transform.columns[0][0];
+ push_constant.skeleton_transform_x[1] = transform.columns[0][1];
+ push_constant.skeleton_transform_y[0] = transform.columns[1][0];
+ push_constant.skeleton_transform_y[1] = transform.columns[1][1];
+ push_constant.skeleton_transform_offset[0] = transform.columns[2][0];
+ push_constant.skeleton_transform_offset[1] = transform.columns[2][1];
+
+ Transform2D inverse_transform = transform.affine_inverse();
+ push_constant.inverse_transform_x[0] = inverse_transform.columns[0][0];
+ push_constant.inverse_transform_x[1] = inverse_transform.columns[0][1];
+ push_constant.inverse_transform_y[0] = inverse_transform.columns[1][0];
+ push_constant.inverse_transform_y[1] = inverse_transform.columns[1][1];
+ push_constant.inverse_transform_offset[0] = inverse_transform.columns[2][0];
+ push_constant.inverse_transform_offset[1] = inverse_transform.columns[2][1];
+
push_constant.blend_shape_count = mi->mesh->blend_shape_count;
push_constant.normalized_blend_shapes = mi->mesh->blend_shape_mode == RS::BLEND_SHAPE_MODE_NORMALIZED;
push_constant.pad0 = 0;
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
index b62da5fd7b..c921523941 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h
@@ -178,6 +178,7 @@ private:
bool weights_dirty = false;
SelfList<MeshInstance> weight_update_list;
SelfList<MeshInstance> array_update_list;
+ Transform2D canvas_item_transform_2d;
MeshInstance() :
weight_update_list(this), array_update_list(this) {}
};
@@ -256,6 +257,14 @@ private:
uint32_t normalized_blend_shapes;
uint32_t pad0;
uint32_t pad1;
+ float skeleton_transform_x[2];
+ float skeleton_transform_y[2];
+
+ float skeleton_transform_offset[2];
+ float inverse_transform_x[2];
+
+ float inverse_transform_y[2];
+ float inverse_transform_offset[2];
};
enum {
@@ -548,6 +557,7 @@ public:
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) override;
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) override;
virtual void mesh_instance_check_for_update(RID p_mesh_instance) override;
+ virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) override;
virtual void update_mesh_instances() override;
/* MULTIMESH API */
diff --git a/servers/rendering/storage/mesh_storage.h b/servers/rendering/storage/mesh_storage.h
index 704c2e015c..76e46a696a 100644
--- a/servers/rendering/storage/mesh_storage.h
+++ b/servers/rendering/storage/mesh_storage.h
@@ -83,6 +83,7 @@ public:
virtual void mesh_instance_set_skeleton(RID p_mesh_instance, RID p_skeleton) = 0;
virtual void mesh_instance_set_blend_shape_weight(RID p_mesh_instance, int p_shape, float p_weight) = 0;
virtual void mesh_instance_check_for_update(RID p_mesh_instance) = 0;
+ virtual void mesh_instance_set_canvas_item_transform(RID p_mesh_instance, const Transform2D &p_transform) = 0;
virtual void update_mesh_instances() = 0;
/* MULTIMESH API */