diff options
17 files changed, 179 insertions, 22 deletions
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 5e8a4e230e..6fcf092834 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3984,6 +3984,15 @@ HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node * return modified_property_map; } +void EditorNode::update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table) { + p_ownership_table.insert(p_current_node, p_current_node->get_owner()); + + for (int i = 0; i < p_current_node->get_child_count(); i++) { + Node *child = p_current_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, p_ownership_table); + } +} + void EditorNode::update_diff_data_for_node( Node *p_edited_scene, Node *p_root, @@ -4079,6 +4088,16 @@ void EditorNode::update_diff_data_for_node( if (node_3d) { new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); } + + // Gathers the ownership of all ancestor nodes for later use. + HashMap<Node *, Node *> ownership_table; + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_ownership_table_for_addition_node_ancestors(child, ownership_table); + } + + new_additive_node_entry.ownership_table = ownership_table; + p_addition_list.push_back(new_additive_node_entry); return; @@ -6203,6 +6222,18 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins node_3d->set_transform(additive_node_entry.transform_3d); } } + + // Restore the ownership of its ancestors + for (KeyValue<Node *, Node *> &E : additive_node_entry.ownership_table) { + Node *current_ancestor = E.key; + Node *ancestor_owner = E.value; + + if (ancestor_owner == original_node) { + ancestor_owner = instantiated_node; + } + + current_ancestor->set_owner(ancestor_owner); + } } // Restore the selection. diff --git a/editor/editor_node.h b/editor/editor_node.h index eefe45ca1f..8ad5969249 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -832,6 +832,8 @@ public: // Used if the original parent node is lost Transform2D transform_2d; Transform3D transform_3d; + // Used to keep track of the ownership of all ancestor nodes so they can be restored later. + HashMap<Node *, Node *> ownership_table; }; struct ConnectionWithNodePath { @@ -846,6 +848,8 @@ public: List<Node::GroupInfo> groups; }; + void update_ownership_table_for_addition_node_ancestors(Node *p_current_node, HashMap<Node *, Node *> &p_ownership_table); + void update_diff_data_for_node( Node *p_edited_scene, Node *p_root, diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 32c16255dd..0ee86c8ed0 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -353,13 +353,12 @@ static String _fixstr(const String &p_what, const String &p_str) { static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) { ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value"); - ERR_FAIL_NULL_MSG(mesh->get_mesh(), "Cannot generate shape list with null mesh value"); if (!p_convex) { Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape(); r_shape_list.push_back(shape); } else { Vector<Ref<Shape3D>> cd; - cd.push_back(mesh->get_mesh()->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false)); + cd.push_back(mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false)); if (cd.size()) { for (int i = 0; i < cd.size(); i++) { r_shape_list.push_back(cd[i]); @@ -1230,7 +1229,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< shapes = collision_map[m]; } else { shapes = get_collision_shapes( - m->get_mesh(), + m, node_settings, p_applied_root_scale); } @@ -1613,7 +1612,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.normalize_mesh)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), static_cast<int>(decomposition_default.mode))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.convexhull_approximation)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_convex_hulls)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.max_convex_hulls)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.project_hull_vertices)); // Primitives: Box, Sphere, Cylinder, Capsule. diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index a0fb95daa7..520ccd1070 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -39,6 +39,7 @@ #include "scene/resources/box_shape_3d.h" #include "scene/resources/capsule_shape_3d.h" #include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" #include "scene/resources/shape_3d.h" #include "scene/resources/sphere_shape_3d.h" @@ -298,7 +299,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, float p_applied_root_scale); + static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<ImporterMesh> &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 +315,8 @@ public: }; template <class M> -Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options, float p_applied_root_scale) { +Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<ImporterMesh> &p_mesh, const M &p_options, float p_applied_root_scale) { + ERR_FAIL_COND_V(p_mesh.is_null(), Vector<Ref<Shape3D>>()); 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(); @@ -373,7 +375,7 @@ Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> } if (p_options.has(SNAME("decomposition/max_convex_hulls"))) { - decomposition_settings.max_convex_hulls = p_options[SNAME("decomposition/max_convex_hulls")]; + decomposition_settings.max_convex_hulls = MAX(1, (int)p_options[SNAME("decomposition/max_convex_hulls")]); } if (p_options.has(SNAME("decomposition/project_hull_vertices"))) { diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 348aad1162..a09e0e7408 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -444,9 +444,45 @@ void SceneImportSettings::_update_view_gizmos() { collider_view->set_visible(show_collider_view); if (generate_collider) { // This collider_view doesn't have a mesh so we need to generate a new one. + Ref<ImporterMesh> mesh; + mesh.instantiate(); + // ResourceImporterScene::get_collision_shapes() expects ImporterMesh, not Mesh. + // TODO: Duplicate code with EditorSceneFormatImporterESCN::import_scene() + // Consider making a utility function to convert from Mesh to ImporterMesh. + Ref<Mesh> mesh_3d_mesh = mesh_node->get_mesh(); + Ref<ArrayMesh> array_mesh_3d_mesh = mesh_3d_mesh; + if (array_mesh_3d_mesh.is_valid()) { + // For the MeshInstance3D nodes, we need to convert the ArrayMesh to an ImporterMesh specially. + mesh->set_name(array_mesh_3d_mesh->get_name()); + for (int32_t blend_i = 0; blend_i < array_mesh_3d_mesh->get_blend_shape_count(); blend_i++) { + mesh->add_blend_shape(array_mesh_3d_mesh->get_blend_shape_name(blend_i)); + } + for (int32_t surface_i = 0; surface_i < array_mesh_3d_mesh->get_surface_count(); surface_i++) { + mesh->add_surface(array_mesh_3d_mesh->surface_get_primitive_type(surface_i), + array_mesh_3d_mesh->surface_get_arrays(surface_i), + array_mesh_3d_mesh->surface_get_blend_shape_arrays(surface_i), + array_mesh_3d_mesh->surface_get_lods(surface_i), + array_mesh_3d_mesh->surface_get_material(surface_i), + array_mesh_3d_mesh->surface_get_name(surface_i), + array_mesh_3d_mesh->surface_get_format(surface_i)); + } + mesh->set_blend_shape_mode(array_mesh_3d_mesh->get_blend_shape_mode()); + } else if (mesh_3d_mesh.is_valid()) { + // For the MeshInstance3D nodes, we need to convert the Mesh to an ImporterMesh specially. + mesh->set_name(mesh_3d_mesh->get_name()); + for (int32_t surface_i = 0; surface_i < mesh_3d_mesh->get_surface_count(); surface_i++) { + mesh->add_surface(mesh_3d_mesh->surface_get_primitive_type(surface_i), + mesh_3d_mesh->surface_get_arrays(surface_i), + Array(), + mesh_3d_mesh->surface_get_lods(surface_i), + mesh_3d_mesh->surface_get_material(surface_i), + mesh_3d_mesh->surface_get_material(surface_i).is_valid() ? mesh_3d_mesh->surface_get_material(surface_i)->get_name() : String(), + mesh_3d_mesh->surface_get_format(surface_i)); + } + } // Generate the mesh collider. - Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings, 1.0); + Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(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/main/main.cpp b/main/main.cpp index 5bb37df3ca..b15588e700 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1131,6 +1131,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph cmdline_tool = true; main_args.push_back(I->get()); #ifndef DISABLE_DEPRECATED + } else if (I->get() == "--export") { // For users used to 3.x syntax. + OS::get_singleton()->print("The Godot 3 --export option was changed to more explicit --export-release / --export-debug / --export-pack options.\nSee the --help output for details.\n"); + goto error; } else if (I->get() == "--convert-3to4") { // Actually handling is done in start(). cmdline_tool = true; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 5ce01a08bf..1161403c0c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1861,24 +1861,20 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { Vector<Variant> args; args.resize(call->arguments.size()); for (int i = 0; i < call->arguments.size(); i++) { - reduce_expression(call->arguments[i]); + GDScriptParser::ExpressionNode *argument = call->arguments[i]; + reduce_expression(argument); - if (!call->arguments[i]->is_constant) { + if (!argument->is_constant) { all_is_constant = false; - } else if (all_is_constant) { - args.write[i] = call->arguments[i]->reduced_value; + break; } - - GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype(); - if (!arg_type.is_variant()) { - if (arg_type.kind != GDScriptParser::DataType::BUILTIN) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); - } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); - } + if (argument->reduced_value.get_type() != Variant::INT && argument->reduced_value.get_type() != Variant::FLOAT) { + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, Variant::get_type_name(argument->reduced_value.get_type())), argument); + all_is_constant = false; + break; } + + args.write[i] = argument->reduced_value; } Variant reduced; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b32313dad4..c402b63f7b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3138,6 +3138,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p bool previous_in_lambda = in_lambda; in_lambda = true; + // Save break/continue state. + bool could_break = can_break; + bool could_continue = can_continue; + + // Disallow break/continue. + can_break = false; + can_continue = false; + function->body = parse_suite("lambda declaration", body, true); complete_extents(function); complete_extents(lambda); @@ -3155,6 +3163,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p current_function = previous_function; in_lambda = previous_in_lambda; lambda->function = function; + + // Reset break/continue state. + can_break = could_break; + can_continue = could_continue; + return lambda; } diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd new file mode 100644 index 0000000000..d2d9d04508 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd @@ -0,0 +1,9 @@ +enum E { E0 = 0, E3 = 3 } + +func test(): + var total := 0 + for value in range(E.E0, E.E3): + var inferable := value + total += inferable + assert(total == 0 + 1 + 2) + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd new file mode 100644 index 0000000000..319c1801c0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd @@ -0,0 +1,5 @@ +func test(): + for index in range(0, 1): + var lambda := func(): + continue + print('not ok') diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out new file mode 100644 index 0000000000..262dfbc09b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Cannot use "continue" outside of a loop. diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd new file mode 100644 index 0000000000..2fa45c1d7d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd @@ -0,0 +1,13 @@ +func test(): + var i_string := '' + for i in 3: + if i == 1: continue + var lambda := func(): + var j_string := '' + for j in 3: + if j == 1: continue + j_string += str(j) + return j_string + i_string += lambda.call() + assert(i_string == '0202') + print('ok') diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp index c12fc5e834..9e706dbeef 100644 --- a/modules/websocket/websocket_multiplayer_peer.cpp +++ b/modules/websocket/websocket_multiplayer_peer.cpp @@ -340,6 +340,7 @@ void WebSocketMultiplayerPeer::_poll_server() { to_remove.insert(id); continue; } + peer.connection = tls; } Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(peer.connection); tls->poll(); diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp index 672581bbe2..0fc72ca90f 100644 --- a/scene/resources/importer_mesh.cpp +++ b/scene/resources/importer_mesh.cpp @@ -31,6 +31,7 @@ #include "importer_mesh.h" #include "core/io/marshalls.h" +#include "core/math/convex_hull.h" #include "core/math/random_pcg.h" #include "core/math/static_raycaster.h" #include "scene/resources/surface_tool.h" @@ -984,6 +985,43 @@ Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecomposit return ret; } +Ref<ConvexPolygonShape3D> ImporterMesh::create_convex_shape(bool p_clean, bool p_simplify) const { + if (p_simplify) { + Mesh::ConvexDecompositionSettings settings; + settings.max_convex_hulls = 1; + Vector<Ref<Shape3D>> decomposed = convex_decompose(settings); + if (decomposed.size() == 1) { + return decomposed[0]; + } else { + ERR_PRINT("Convex shape simplification failed, falling back to simpler process."); + } + } + + Vector<Vector3> vertices; + for (int i = 0; i < get_surface_count(); i++) { + Array a = get_surface_arrays(i); + ERR_FAIL_COND_V(a.is_empty(), Ref<ConvexPolygonShape3D>()); + Vector<Vector3> v = a[Mesh::ARRAY_VERTEX]; + vertices.append_array(v); + } + + Ref<ConvexPolygonShape3D> shape = memnew(ConvexPolygonShape3D); + + if (p_clean) { + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(vertices, md); + if (err == OK) { + shape->set_points(md.vertices); + return shape; + } else { + ERR_PRINT("Convex shape cleaning failed, falling back to simpler process."); + } + } + + shape->set_points(vertices); + return shape; +} + Ref<ConcavePolygonShape3D> ImporterMesh::create_trimesh_shape() const { Vector<Face3> faces = get_faces(); if (faces.size() == 0) { diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h index 900a8851c0..33d0864342 100644 --- a/scene/resources/importer_mesh.h +++ b/scene/resources/importer_mesh.h @@ -119,6 +119,7 @@ public: Vector<Face3> get_faces() const; Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const; + Ref<ConvexPolygonShape3D> create_convex_shape(bool p_clean = true, bool p_simplify = false) const; Ref<ConcavePolygonShape3D> create_trimesh_shape() const; Ref<NavigationMesh> create_navigation_mesh(); Error 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); |