summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--editor/editor_node.cpp31
-rw-r--r--editor/editor_node.h4
-rw-r--r--editor/import/resource_importer_scene.cpp7
-rw-r--r--editor/import/resource_importer_scene.h8
-rw-r--r--editor/import/scene_import_settings.cpp38
-rw-r--r--main/main.cpp3
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp24
-rw-r--r--modules/gdscript/gdscript_parser.cpp13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out2
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp1
-rw-r--r--scene/resources/importer_mesh.cpp38
-rw-r--r--scene/resources/importer_mesh.h1
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);