diff options
Diffstat (limited to 'modules')
84 files changed, 1274 insertions, 369 deletions
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 171895ed24..bd8342e1aa 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -91,11 +91,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, // the data width in case of 8/4/1 bit images const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); + const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset; for (uint64_t i = 0; i < height; i++) { const uint8_t *line_ptr = line; for (unsigned int j = 0; j < w; j++) { + ERR_FAIL_COND_V(line_ptr >= end_buffer, ERR_FILE_CORRUPT); switch (bits_per_pixel) { case 1: { uint8_t color_index = *line_ptr; diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp index 94b7e0ce38..81b832fb42 100644 --- a/modules/bullet/soft_body_bullet.cpp +++ b/modules/bullet/soft_body_bullet.cpp @@ -71,7 +71,7 @@ void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering return; } - /// Update visual server vertices + /// Update rendering server vertices const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes); const int nodes_count = nodes.size(); @@ -190,22 +190,24 @@ void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) co } } -void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) { +void SoftBodyBullet::set_node_mass(int p_node_index, btScalar p_mass) { if (0 >= p_mass) { - pin_node(node_index); + pin_node(p_node_index); } else { - unpin_node(node_index); + unpin_node(p_node_index); } if (bt_soft_body) { - bt_soft_body->setMass(node_index, p_mass); + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + bt_soft_body->setMass(p_node_index, p_mass); } } -btScalar SoftBodyBullet::get_node_mass(int node_index) const { +btScalar SoftBodyBullet::get_node_mass(int p_node_index) const { if (bt_soft_body) { - return bt_soft_body->getMass(node_index); + ERR_FAIL_INDEX_V(p_node_index, bt_soft_body->m_nodes.size(), 1); + return bt_soft_body->getMass(p_node_index); } else { - return -1 == search_node_pinned(node_index) ? 1 : 0; + return -1 == search_node_pinned(p_node_index) ? 1 : 0; } } @@ -296,11 +298,11 @@ bool SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector ERR_FAIL_COND_V(p_indices.is_empty(), false); ERR_FAIL_COND_V(p_vertices.is_empty(), false); - /// Parse visual server indices to physical indices. - /// Merge all overlapping vertices and create a map of physical vertices to visual server + /// Parse rendering server indices to physical indices. + /// Merge all overlapping vertices and create a map of physical vertices to rendering server { - /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map + /// This is the map of rendering server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map Vector<int> vs_indices_to_physics_table; { // Map vertices @@ -418,17 +420,25 @@ void SoftBodyBullet::setup_soft_body() { // Set pinned nodes for (int i = pinned_nodes.size() - 1; 0 <= i; --i) { - bt_soft_body->setMass(pinned_nodes[i], 0); + const int node_index = pinned_nodes[i]; + ERR_CONTINUE(0 > node_index || bt_soft_body->m_nodes.size() <= node_index); + bt_soft_body->setMass(node_index, 0); } } void SoftBodyBullet::pin_node(int p_node_index) { + if (bt_soft_body) { + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + } if (-1 == search_node_pinned(p_node_index)) { pinned_nodes.push_back(p_node_index); } } void SoftBodyBullet::unpin_node(int p_node_index) { + if (bt_soft_body) { + ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size()); + } const int id = search_node_pinned(p_node_index); if (-1 != id) { pinned_nodes.remove(id); diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index 00469ab44c..fcdf282a7d 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -51,7 +51,7 @@ <argument index="3" name="data" type="int" default="0" /> <description> Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocting the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer. - Note: You must call either [method create_host] or [method create_host_bound] before calling this method. + [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method. </description> </method> <method name="create_host"> @@ -121,7 +121,7 @@ <return type="Array" /> <description> Returns the list of peers associated with this host. - Note: This list might include some peers that are not fully connected or are still being disconnected. + [b]Note:[/b] This list might include some peers that are not fully connected or are still being disconnected. </description> </method> <method name="pop_statistic"> @@ -136,7 +136,7 @@ <argument index="0" name="refuse" type="bool" /> <description> Configures the DTLS server to automatically drop new connections. - Note: This method is only relevant after calling [method dtls_server_setup]. + [b]Note:[/b] This method is only relevant after calling [method dtls_server_setup]. </description> </method> <method name="service"> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index f9659c092a..22136c3944 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -18,7 +18,7 @@ <argument index="1" name="host" type="ENetConnection" /> <description> Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code]. - Note: The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. + [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state. </description> </method> <method name="close_connection"> diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp index dcea476275..7343bf87af 100644 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -31,6 +31,7 @@ #include "fbx_mesh_data.h" #include "core/templates/local_vector.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" #include "scene/resources/surface_tool.h" @@ -101,7 +102,7 @@ HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, Hash return collection; } -EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) { +ImporterMeshInstance3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) { mesh_geometry = p_mesh_geometry; // todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately. const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials(); @@ -344,7 +345,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s } // Phase 6. Compose the mesh and return it. - Ref<EditorSceneImporterMesh> mesh; + Ref<ImporterMesh> mesh; mesh.instantiate(); // Add blend shape info. @@ -380,7 +381,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s in_mesh_surface_id += 1; } - EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *godot_mesh = memnew(ImporterMeshInstance3D); godot_mesh->set_mesh(mesh); const String name = ImportUtils::FBXNodeToName(model->Name()); godot_mesh->set_name(name); // hurry up compiling >.< diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h index 24db4a5469..eec7f38cd6 100644 --- a/modules/fbx/data/fbx_mesh_data.h +++ b/modules/fbx/data/fbx_mesh_data.h @@ -35,7 +35,7 @@ #include "core/templates/local_vector.h" #include "core/templates/ordered_hash_map.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh_node_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/resources/surface_tool.h" @@ -98,7 +98,7 @@ struct FBXMeshData : RefCounted { // translate fbx mesh data from document context to FBX Mesh Geometry Context bool valid_weight_indexes = false; - EditorSceneImporterMeshNode3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression); + ImporterMeshInstance3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression); void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const; @@ -107,7 +107,7 @@ struct FBXMeshData : RefCounted { int max_weight_count = 0; uint64_t armature_id = 0; bool valid_armature_id = false; - EditorSceneImporterMeshNode3D *godot_mesh_instance = nullptr; + ImporterMeshInstance3D *godot_mesh_instance = nullptr; private: void sanitize_vertex_weights(const ImportState &state); diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp index e0663ab49d..e4de204cf1 100644 --- a/modules/fbx/editor_scene_importer_fbx.cpp +++ b/modules/fbx/editor_scene_importer_fbx.cpp @@ -40,9 +40,9 @@ #include "editor/editor_log.h" #include "editor/editor_node.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh_node_3d.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/camera_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" #include "scene/main/node.h" #include "scene/resources/material.h" @@ -627,7 +627,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( node_element; node_element = node_element->next()) { Ref<FBXNode> fbx_node = node_element->get(); - EditorSceneImporterMeshNode3D *mesh_node = nullptr; + ImporterMeshInstance3D *mesh_node = nullptr; Ref<FBXMeshData> mesh_data_precached; // check for valid geometry @@ -768,7 +768,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene( for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) { Ref<FBXMeshData> mesh = mesh_data.value; const uint64_t mesh_id = mesh_data.key; - EditorSceneImporterMeshNode3D *mesh_instance = mesh->godot_mesh_instance; + ImporterMeshInstance3D *mesh_instance = mesh->godot_mesh_instance; const int mesh_weights = mesh->max_weight_count; Ref<FBXSkeleton> skeleton; const bool valid_armature = mesh->valid_armature_id; diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 3654870b09..6b3bd714b9 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -7,8 +7,8 @@ A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as XRInterfaceGDNative. The library must be compiled for each platform and architecture that the project will run on. </description> <tutorials> - <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link> - <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link> + <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_c_example.html</link> + <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/scripting/gdnative/gdnative_cpp_example.html</link> </tutorials> <methods> <method name="get_current_dependencies" qualifiers="const"> diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml index 9d34e89f02..221374a7a4 100644 --- a/modules/gdnative/doc_classes/NativeScript.xml +++ b/modules/gdnative/doc_classes/NativeScript.xml @@ -38,7 +38,7 @@ <return type="Variant" /> <description> Constructs a new object of the base type with a script of this type already attached. - [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. + [b]Note:[/b] Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension. </description> </method> </methods> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index d45202bd40..0a448ed88c 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -8,7 +8,7 @@ [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes. </description> <tutorials> - <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link> + <link title="GDScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html</link> </tutorials> <methods> <method name="get_as_byte_code" qualifiers="const"> diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index c35af9ca5b..fd7664049a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -244,6 +244,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, class_type.kind = GDScriptParser::DataType::CLASS; class_type.class_type = p_class; class_type.script_path = parser->script_path; + class_type.builtin_type = Variant::OBJECT; p_class->set_datatype(class_type); if (!p_class->extends_used) { @@ -874,18 +875,32 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { GDScriptParser::ClassNode *previous_class = parser->current_class; parser->current_class = p_class; - // Do functions now. + // Do functions and properties now. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) { - continue; - } + if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { + resolve_function_body(member.function); - resolve_function_body(member.function); + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { + E->apply(parser, member.function); + } + } else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) { + if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) { + if (member.variable->getter != nullptr) { + member.variable->getter->set_datatype(member.variable->datatype); + + resolve_function_body(member.variable->getter); + } + if (member.variable->setter != nullptr) { + if (member.variable->setter->parameters.size() > 0) { + member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier; + } - // Apply annotations. - for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { - E->apply(parser, member.function); + resolve_function_signature(member.variable->setter); + resolve_function_body(member.variable->setter); + } + } } } @@ -901,17 +916,80 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { resolve_class_body(member.m_class); } - // Check unused variables. + // Check unused variables and datatypes of property getters and setters. for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; - if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) { - continue; - } + if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) { #ifdef DEBUG_ENABLED - if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { - parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); - } + if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { + parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); + } #endif + + if (member.variable->property == GDScriptParser::VariableNode::PROP_SETGET) { + GDScriptParser::FunctionNode *getter_function = nullptr; + GDScriptParser::FunctionNode *setter_function = nullptr; + + bool has_valid_getter = false; + bool has_valid_setter = false; + + if (member.variable->getter_pointer != nullptr) { + if (p_class->has_function(member.variable->getter_pointer->name)) { + getter_function = p_class->get_member(member.variable->getter_pointer->name).function; + } + + if (getter_function == nullptr) { + push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable); + + } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) { + push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable); + + } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) { + push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable); + + } else { + has_valid_getter = true; + +#ifdef DEBUG_ENABLED + if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) { + parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + } + } + + if (member.variable->setter_pointer != nullptr) { + if (p_class->has_function(member.variable->setter_pointer->name)) { + setter_function = p_class->get_member(member.variable->setter_pointer->name).function; + } + + if (setter_function == nullptr) { + push_error(vformat(R"(Setter "%s" not found.)", member.variable->setter_pointer->name), member.variable); + + } else if (setter_function->parameters.size() != 1) { + push_error(vformat(R"(Function "%s" cannot be used as setter because of its signature.)", setter_function->identifier->name), member.variable); + + } else if (!is_type_compatible(member.variable->datatype, setter_function->parameters[0]->datatype, true)) { + push_error(vformat(R"(Function with argument type "%s" cannot be used as setter for a property of type "%s".)", setter_function->parameters[0]->datatype.to_string(), member.variable->datatype.to_string()), member.variable); + + } else { + has_valid_setter = true; + +#ifdef DEBUG_ENABLED + if (member.variable->datatype.builtin_type == Variant::INT && setter_function->return_type->datatype.builtin_type == Variant::FLOAT) { + parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + } + } + + if (member.variable->datatype.is_variant() && has_valid_getter && has_valid_setter) { + if (!is_type_compatible(getter_function->datatype, setter_function->parameters[0]->datatype, true)) { + push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable); + } + } + } + } } } @@ -1035,7 +1113,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * return_type.is_meta_type = false; p_function->set_datatype(return_type); if (p_function->return_type) { - push_error("Constructor cannot have an explicit return type.", p_function->return_type); + GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type); + if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) { + push_error("Constructor cannot have an explicit return type.", p_function->return_type); + } } } else { GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); @@ -1198,7 +1279,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.kind = GDScriptParser::DataType::BUILTIN; variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else? p_for->variable->set_datatype(variable_type); - } else { + } else if (p_for->list) { resolve_node(p_for->list); if (p_for->list->datatype.has_container_element_type()) { variable_type = p_for->list->datatype.get_container_element_type(); @@ -1213,7 +1294,9 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { variable_type.kind = GDScriptParser::DataType::VARIANT; } } - p_for->variable->set_datatype(variable_type); + if (p_for->variable) { + p_for->variable->set_datatype(variable_type); + } resolve_suite(p_for->loop); p_for->set_datatype(p_for->loop->get_datatype()); @@ -1523,12 +1606,20 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame 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) { + expected_type = parser->current_function->get_datatype(); + has_expected_type = true; + } + if (p_return->return_value != nullptr) { reduce_expression(p_return->return_value); if (p_return->return_value->type == GDScriptParser::Node::ARRAY) { // Check if assigned value is an array literal, so we can make it a typed array too if appropriate. - if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { - update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); + if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) { + update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value)); } } result = p_return->return_value->get_datatype(); @@ -1540,23 +1631,24 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { result.is_constant = true; } - GDScriptParser::DataType function_type = parser->current_function->get_datatype(); - function_type.is_meta_type = false; - if (function_type.is_hard_type()) { - if (!is_type_compatible(function_type, result)) { - // Try other way. Okay but not safe. - if (!is_type_compatible(result, function_type)) { - push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), function_type.to_string()), p_return); - } else { - // TODO: Add warning. - mark_node_unsafe(p_return); - } + 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); + } #ifdef DEBUG_ENABLED - } else if (function_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { - parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); - } else if (result.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); + } else if (result.is_variant()) { + mark_node_unsafe(p_return); #endif + } } } @@ -1737,7 +1829,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig push_error("Cannot assign a new value to a constant.", p_assignment->assignee); } - if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) { + if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) { bool compatible = true; GDScriptParser::DataType op_type = assigned_value_type; if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { @@ -1789,27 +1881,24 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: { GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->parameter_source->set_datatype(id_type); } } break; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: { GDScriptParser::DataType id_type = identifier->variable_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->variable_source->set_datatype(id_type); } } break; case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: { GDScriptParser::DataType id_type = identifier->bind_source->get_datatype(); if (!id_type.is_hard_type()) { - id_type = assigned_value_type; - id_type.type_source = GDScriptParser::DataType::INFERRED; - id_type.is_constant = false; + id_type.kind = GDScriptParser::DataType::VARIANT; + id_type.type_source = GDScriptParser::DataType::UNDETECTED; identifier->variable_source->set_datatype(id_type); } } break; @@ -2605,10 +2694,13 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod const StringName &native = base.native_type; if (class_exists(native)) { - PropertyInfo prop_info; MethodInfo method_info; - if (ClassDB::get_property_info(native, name, &prop_info)) { - p_identifier->set_datatype(type_from_property(prop_info)); + if (ClassDB::has_property(native, name)) { + StringName getter_name = ClassDB::get_property_getter(native, name); + MethodBind *getter = ClassDB::get_method(native, getter_name); + if (getter != nullptr) { + p_identifier->set_datatype(type_from_property(getter->get_return_info())); + } return; } if (ClassDB::get_method_info(native, name, &method_info)) { @@ -2936,7 +3028,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } else { GDScriptParser::DataType base_type = p_subscript->base->get_datatype(); - if (base_type.is_variant()) { + if (base_type.is_variant() || !base_type.is_hard_type()) { result_type.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_subscript); } else { @@ -3059,6 +3151,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri result_type.kind = GDScriptParser::DataType::BUILTIN; result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED; + if (base_type.kind != GDScriptParser::DataType::BUILTIN) { + base_type.builtin_type = Variant::OBJECT; + } switch (base_type.builtin_type) { // Can't index at all. case Variant::RID: @@ -3119,6 +3214,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri case Variant::PLANE: case Variant::COLOR: case Variant::DICTIONARY: + case Variant::OBJECT: result_type.kind = GDScriptParser::DataType::VARIANT; result_type.type_source = GDScriptParser::DataType::UNDETECTED; break; @@ -3133,7 +3229,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } break; // Here for completeness. - case Variant::OBJECT: case Variant::VARIANT_MAX: break; } @@ -3300,7 +3395,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va current = current->_owner; } - Ref<GDScriptParserRef> ref = get_parser_for(current->path); + Ref<GDScriptParserRef> ref = get_parser_for(current->get_path()); + if (ref.is_null()) { + push_error("Could not find script in path.", p_source); + GDScriptParser::DataType error_type; + error_type.kind = GDScriptParser::DataType::VARIANT; + return error_type; + } ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); GDScriptParser::ClassNode *found = ref->get_parser()->head; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 8121053245..bb0d9e9e9b 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -58,27 +58,27 @@ Error GDScriptParserRef::raise_status(Status p_new_status) { while (p_new_status > status) { switch (status) { case EMPTY: - result = parser->parse(GDScriptCache::get_source_code(path), path, false); status = PARSED; + result = parser->parse(GDScriptCache::get_source_code(path), path, false); break; case PARSED: { analyzer = memnew(GDScriptAnalyzer(parser)); - Error inheritance_result = analyzer->resolve_inheritance(); status = INHERITANCE_SOLVED; + Error inheritance_result = analyzer->resolve_inheritance(); if (result == OK) { result = inheritance_result; } } break; case INHERITANCE_SOLVED: { - Error interface_result = analyzer->resolve_interface(); status = INTERFACE_SOLVED; + Error interface_result = analyzer->resolve_interface(); if (result == OK) { result = interface_result; } } break; case INTERFACE_SOLVED: { - Error body_result = analyzer->resolve_body(); status = FULLY_SOLVED; + Error body_result = analyzer->resolve_body(); if (result == OK) { result = body_result; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 2c02291795..1f9aad40af 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -109,7 +109,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D // Locate class by constructing the path to it and following that path GDScriptParser::ClassNode *class_type = p_datatype.class_type; if (class_type) { - if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) { + const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path); + const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name); + if (is_inner_by_path || is_inner_by_name) { // Local class. List<StringName> names; while (class_type->outer) { @@ -1019,25 +1021,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) { // Assignment to member property. - GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value); + GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value); if (r_error) { return GDScriptCodeGenerator::Address(); } - GDScriptCodeGenerator::Address assign_temp = assigned; + + GDScriptCodeGenerator::Address to_assign = assigned_value; + bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE; StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name; - if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + if (has_operation) { + GDScriptCodeGenerator::Address op_result = codegen.add_temporary(); GDScriptCodeGenerator::Address member = codegen.add_temporary(); gen->write_get_member(member, name); - gen->write_binary_operator(assigned, assignment->variant_op, member, assigned); - gen->pop_temporary(); + gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value); + gen->pop_temporary(); // Pop member temp. + to_assign = op_result; } - gen->write_set_member(assigned, name); + gen->write_set_member(to_assign, name); - if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); + if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation. + } + if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); // Pop the assigned expression if not done before. } } else { // Regular assignment. @@ -2080,77 +2089,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) { Error error = OK; - CodeGen codegen; - codegen.generator = memnew(GDScriptByteCodeGenerator); - codegen.class_node = p_class; - codegen.script = p_script; - - StringName func_name; - - if (p_is_setter) { - func_name = "@" + p_variable->identifier->name + "_setter"; - } else { - func_name = "@" + p_variable->identifier->name + "_getter"; - } - - codegen.function_name = func_name; + GDScriptParser::FunctionNode *function; - GDScriptDataType return_type; if (p_is_setter) { - return_type.has_type = true; - return_type.kind = GDScriptDataType::BUILTIN; - return_type.builtin_type = Variant::NIL; + function = p_variable->setter; } else { - return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script); - } - - codegen.generator->write_start(p_script, func_name, false, Multiplayer::RPCConfig(), return_type); - - if (p_is_setter) { - uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype())); - codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype())); - } - - error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter); - if (error) { - memdelete(codegen.generator); - return error; + function = p_variable->getter; } - GDScriptFunction *gd_function = codegen.generator->write_end(); - - p_script->member_functions[func_name] = gd_function; + _parse_function(error, p_script, p_class, function); -#ifdef DEBUG_ENABLED - if (EngineDebugger::is_active()) { - String signature; - //path - if (p_script->get_path() != String()) { - signature += p_script->get_path(); - } - //loc - signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - - //function and class - - if (p_class->identifier) { - signature += "::" + String(p_class->identifier->name) + "." + String(func_name); - } else { - signature += "::" + String(func_name); - } - - codegen.generator->set_signature(signature); - } -#endif - codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line); - -#ifdef TOOLS_ENABLED - p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line; -#endif - memdelete(codegen.generator); - - return OK; + return error; } Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) { diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 83805f626a..e49bf518a2 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -2222,8 +2222,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c if (obj) { List<String> options; obj->get_argument_options(p_method, p_argidx, &options); - for (const String &F : options) { - ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION); + for (String &opt : options) { + if (opt.is_quoted()) { + opt = opt.unquote().quote(quote_style); // Handle user preference. + } + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION); r_result.insert(option.display, option); } } @@ -2643,23 +2646,26 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c } } break; case GDScriptParser::COMPLETION_GET_NODE: { + // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically. if (p_owner) { List<String> opts; p_owner->get_argument_options("get_node", 0, &opts); for (const String &E : opts) { + r_forced = true; String opt = E.strip_edges(); if (opt.is_quoted()) { - r_forced = true; - String idopt = opt.unquote(); - if (idopt.replace("/", "_").is_valid_identifier()) { - ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH); - options.insert(option.display, option); - } else { - ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); - options.insert(option.display, option); - } + // Remove quotes so that we can handle user preferred quote style, + // or handle NodePaths which are valid identifiers and don't need quotes. + opt = opt.unquote(); } + // The path needs quotes if it's not a valid identifier (with an exception + // for "/" as path separator, which also doesn't require quotes). + if (!opt.replace("/", "_").is_valid_identifier()) { + opt = opt.quote(quote_style); // Handle user preference. + } + ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH); + options.insert(option.display, option); } // Get autoloads. diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 63817e970a..40be5cb324 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -947,7 +947,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var void GDScriptParser::parse_property_setter(VariableNode *p_variable) { switch (p_variable->property) { - case VariableNode::PROP_INLINE: + case VariableNode::PROP_INLINE: { consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)"); if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) { p_variable->setter_parameter = parse_identifier(); @@ -955,9 +955,30 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) { consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*"); consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*"); - p_variable->setter = parse_suite("setter definition"); - break; + IdentifierNode *identifier = alloc_node<IdentifierNode>(); + identifier->name = "@" + p_variable->identifier->name + "_setter"; + + FunctionNode *function = alloc_node<FunctionNode>(); + function->identifier = identifier; + + FunctionNode *previous_function = current_function; + current_function = function; + + ParameterNode *parameter = alloc_node<ParameterNode>(); + parameter->identifier = p_variable->setter_parameter; + function->parameters_indices[parameter->identifier->name] = 0; + function->parameters.push_back(parameter); + + SuiteNode *body = alloc_node<SuiteNode>(); + body->add_local(parameter, function); + + function->body = parse_suite("setter declaration", body); + + p_variable->setter = function; + current_function = previous_function; + break; + } case VariableNode::PROP_SETGET: consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")"); make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable); @@ -972,11 +993,25 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) { void GDScriptParser::parse_property_getter(VariableNode *p_variable) { switch (p_variable->property) { - case VariableNode::PROP_INLINE: + case VariableNode::PROP_INLINE: { consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)"); - p_variable->getter = parse_suite("getter definition"); + IdentifierNode *identifier = alloc_node<IdentifierNode>(); + identifier->name = "@" + p_variable->identifier->name + "_getter"; + + FunctionNode *function = alloc_node<FunctionNode>(); + function->identifier = identifier; + + FunctionNode *previous_function = current_function; + current_function = function; + + SuiteNode *body = alloc_node<SuiteNode>(); + function->body = parse_suite("getter declaration", body); + + p_variable->getter = function; + current_function = previous_function; break; + } case VariableNode::PROP_SETGET: consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")"); make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable); @@ -1620,6 +1655,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { n_for->list = parse_expression(false); + if (!n_for->list) { + push_error(R"(Expected a list or range after "in".)"); + } + consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)"); // Save break/continue state. @@ -3419,7 +3458,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node String enum_hint_string; for (const Map<StringName, int>::Element *E = export_type.enum_values.front(); E; E = E->next()) { - enum_hint_string += E->key().operator String().camelcase_to_underscore(true).capitalize().xml_escape(); + enum_hint_string += E->key().operator String().capitalize().xml_escape(); enum_hint_string += ":"; enum_hint_string += String::num_int64(E->get()).xml_escape(); @@ -3486,9 +3525,9 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod } else if (mode == "authority") { rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY; } else if (mode == "call_local") { - rpc_config.sync = true; + rpc_config.call_local = true; } else if (mode == "call_remote") { - rpc_config.sync = false; + rpc_config.call_local = false; } else if (mode == "reliable") { rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; } else if (mode == "unreliable") { @@ -3496,7 +3535,7 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod } else if (mode == "unreliable_ordered") { rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED; } else { - push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'no_call_local' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation); + push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation); } } } @@ -4433,7 +4472,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) { if (p_variable->property == VariableNode::PROP_INLINE) { push_line(":"); increase_indent(); - print_suite(p_variable->getter); + print_suite(p_variable->getter->body); decrease_indent(); } else { push_line(" ="); @@ -4453,7 +4492,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) { } push_line("):"); increase_indent(); - print_suite(p_variable->setter); + print_suite(p_variable->setter->body); decrease_indent(); } else { push_line(" ="); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 593fb0cc5e..af9b973ada 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1109,12 +1109,12 @@ public: PropertyStyle property = PROP_NONE; union { - SuiteNode *setter = nullptr; + FunctionNode *setter = nullptr; IdentifierNode *setter_pointer; }; IdentifierNode *setter_parameter = nullptr; union { - SuiteNode *getter = nullptr; + FunctionNode *getter = nullptr; IdentifierNode *getter_pointer; }; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index c89cf2f25c..1bc7ae086f 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1719,6 +1719,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #define OPCODE_CALL_PTR(m_type) \ OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \ CHECK_SPACE(3 + instr_arg_count); \ + ip += instr_arg_count; \ int argc = _code_ptr[ip + 1]; \ GET_INSTRUCTION_ARG(base, argc); \ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \ diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index 730e554476..f4c0c4d9bb 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -165,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p case ClassNode::Member::VARIABLE: { lsp::DocumentSymbol symbol; symbol.name = m.variable->identifier->name; - symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property; + symbol.kind = m.variable->property == VariableNode::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property; symbol.deprecated = false; symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line); symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column); diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index c383830c82..50c1f68440 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -133,13 +133,12 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l if (do_init_languages) { init_language(p_source_dir); - - // Enable all warnings for GDScript, so we can test them. - ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true); - for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { - String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); - ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true); - } + } + // Enable all warnings for GDScript, so we can test them. + ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true); + for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { + String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); + ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true); } // Enable printing to show results diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd new file mode 100644 index 0000000000..f1be6aaa0c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd @@ -0,0 +1,11 @@ +var _prop : int + +# Getter function has wrong return type. +var prop : String: + get = get_prop + +func get_prop(): + return _prop + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out new file mode 100644 index 0000000000..29eec51ef2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Function with return type "int" cannot be used as getter for a property of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd new file mode 100644 index 0000000000..dd190157a1 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd @@ -0,0 +1,11 @@ +var _prop : int + +# Setter function has wrong argument type. +var prop : String: + set = set_prop + +func set_prop(value : int): + _prop = value + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out new file mode 100644 index 0000000000..7a25280d55 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Function with argument type "int" cannot be used as setter for a property of type "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd new file mode 100644 index 0000000000..7f2b29222a --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd @@ -0,0 +1,9 @@ +var _prop : int + +# Inline getter returns int instead of String. +var prop : String: + get: + return _prop + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out new file mode 100644 index 0000000000..e0adef1bf8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot return value of type "int" because the function return type is "String". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd new file mode 100644 index 0000000000..0ce239dbbd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd @@ -0,0 +1,9 @@ +var _prop : int + +# Inline setter assigns String to int. +var prop : String: + set(value): + _prop = value + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out new file mode 100644 index 0000000000..bbadf1ce27 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot assign a value of type "String" to a target of type "int". diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out index 0e9f482af4..481016138a 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out +++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out @@ -1,2 +1,6 @@ GDTEST_OK +>> WARNING +>> Line: 6 +>> UNSAFE_METHOD_ACCESS +>> The method 'free' is not present on the inferred type 'Variant' (but may be present on a subtype). Ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd new file mode 100644 index 0000000000..630b20c282 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd @@ -0,0 +1,11 @@ +# https://github.com/godotengine/godot/issues/43503 + +var test_var = null + + +func test(): + print(test_var.x) + + +func _init(): + test_var = Vector3() diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out new file mode 100644 index 0000000000..94e2ec2af8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out @@ -0,0 +1,2 @@ +GDTEST_OK +0 diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd new file mode 100644 index 0000000000..b3784dffa3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd @@ -0,0 +1,14 @@ +# https://github.com/godotengine/godot/issues/41064 +var x = true + +func test(): + var int_var: int = 0 + var dyn_var = 2 + + if x: + dyn_var = 5 + else: + dyn_var = Node.new() + + int_var = dyn_var + print(int_var) diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out new file mode 100644 index 0000000000..952029f665 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out @@ -0,0 +1,2 @@ +GDTEST_OK +5 diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd new file mode 100644 index 0000000000..1706087f82 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd @@ -0,0 +1,16 @@ +var _prop = 1 +var prop: + get = get_prop, set = set_prop + +func get_prop(): + return _prop + +func set_prop(value): + _prop = value + +func test(): + print(prop) + + prop = 2 + + print(prop) diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.out b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out new file mode 100644 index 0000000000..f1253ca57e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out @@ -0,0 +1,3 @@ +GDTEST_OK +1 +2 diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd new file mode 100644 index 0000000000..23eb011b23 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd @@ -0,0 +1,46 @@ +# Untyped inline property +var prop1: + get: + return prop1 + set(value): + prop1 = value + +# Typed inline property +var prop2 : int: + get: + return prop2 + set(value): + prop2 = value + +# Typed inline property with default value +var prop3 : int = 1: + get: + return prop3 + set(value): + prop3 = value + +# Typed inline property with backing variable +var _prop4 : int = 2 +var prop4: int: + get: + return _prop4 + set(value): + _prop4 = value + +func test(): + print(prop1) + print(prop2) + print(prop3) + print(prop4) + + print() + + prop1 = 1 + prop2 = 2 + prop3 = 3 + prop4 = 4 + + print(prop1) + print(prop2) + print(prop3) + print(prop4) diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out new file mode 100644 index 0000000000..5482592e90 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out @@ -0,0 +1,10 @@ +GDTEST_OK +null +0 +1 +2 + +1 +2 +3 +4 diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd new file mode 100644 index 0000000000..f9a8b23b92 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd @@ -0,0 +1,8 @@ +# https://github.com/godotengine/godot/issues/43221 +extends Node + +func test(): + name = "Node" + print(self["name"]) + self["name"] = "Changed" + print(name) diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out new file mode 100644 index 0000000000..6417f4f8da --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out @@ -0,0 +1,3 @@ +GDTEST_OK +Node +Changed diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd new file mode 100644 index 0000000000..f6526aefb4 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd @@ -0,0 +1,13 @@ +extends Node + +func test(): + process_priority = 10 + var change = 20 + + print(process_priority) + print(change) + + process_priority += change + + print(process_priority) + print(change) diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out new file mode 100644 index 0000000000..c9e6b34c77 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out @@ -0,0 +1,5 @@ +GDTEST_OK +10 +20 +30 +20 diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd new file mode 100644 index 0000000000..c9b5f8481e --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd @@ -0,0 +1,4 @@ +func test(): + var dict = {} + dict.test = 1 + print(dict.test) diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.out b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out new file mode 100644 index 0000000000..a7f1357bb2 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out @@ -0,0 +1,2 @@ +GDTEST_OK +1 diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd new file mode 100644 index 0000000000..de269b92b3 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd @@ -0,0 +1,42 @@ +func test(): + print(true, false) + print(-1, 0, 1) + print(-1.25, 0.25, 1.25) + print("hello world") + + print(Vector2(0.25, 0.25)) + print(Vector2i(0, 0)) + + print(Rect2(0.25, 0.25, 0.5, 0.5)) + print(Rect2i(0, 0, 0, 0)) + + print(Vector3(0.25, 0.25, 0.25)) + print(Vector3i(0, 0, 0)) + + print(Transform2D.IDENTITY) + print(Plane(1, 2, 3, 4)) + print(Quaternion(1, 2, 3, 4)) + print(AABB(Vector3.ZERO, Vector3.ONE)) + print(Basis(Vector3(0, 0, 0))) + print(Transform3D.IDENTITY) + + print(Color(1, 2, 3, 4)) + print(StringName("hello")) + print(NodePath("hello/world")) + var node := Node.new() + print(RID(node)) + print(node.get_name) + print(node.property_list_changed) + node.free() + print({"hello":123}) + print(["hello", 123]) + + print(PackedByteArray([-1, 0, 1])) + print(PackedInt32Array([-1, 0, 1])) + print(PackedInt64Array([-1, 0, 1])) + print(PackedFloat32Array([-1, 0, 1])) + print(PackedFloat64Array([-1, 0, 1])) + print(PackedStringArray(["hello", "world"])) + print(PackedVector2Array([Vector2.ONE, Vector2.ZERO])) + print(PackedVector3Array([Vector3.ONE, Vector3.ZERO])) + print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN])) diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out new file mode 100644 index 0000000000..7670fc0128 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -0,0 +1,34 @@ +GDTEST_OK +truefalse +-101 +-1.250.251.25 +hello world +(0.25, 0.25) +(0, 0) +[P: (0.25, 0.25), S: (0.5, 0.5)] +[P: (0, 0), S: (0, 0)] +(0.25, 0.25, 0.25) +(0, 0, 0) +[X: (1, 0), Y: (0, 1), O: (0, 0)] +[N: (1, 2, 3), D: 4] +(1, 2, 3, 4) +[P: (0, 0, 0), S: (1, 1, 1)] +[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1)] +[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)] +(1, 2, 3, 4) +hello +hello/world +RID(0) +Node::get_name +Node::[signal]property_list_changed +{hello:123} +[hello, 123] +[255, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[-1, 0, 1] +[hello, world] +[(1, 1), (0, 0)] +[(1, 1, 1), (0, 0, 0)] +[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)] diff --git a/modules/gltf/config.py b/modules/gltf/config.py index 52a97c93aa..a4736321fa 100644 --- a/modules/gltf/config.py +++ b/modules/gltf/config.py @@ -1,5 +1,5 @@ def can_build(env, platform): - return env["tools"] and not env["disable_3d"] + return not env["disable_3d"] def configure(env): @@ -22,6 +22,8 @@ def get_doc_classes(): "GLTFSpecGloss", "GLTFState", "GLTFTexture", + "GLTFDocumentExtension", + "GLTFDocumentExtensionConvertImporterMesh", ] diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index 16e649f390..8d8e25e8b3 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -30,4 +30,8 @@ </description> </method> </methods> + <members> + <member name="extensions" type="GLTFDocumentExtension[]" setter="set_extensions" getter="get_extensions" default="[]"> + </member> + </members> </class> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml new file mode 100644 index 0000000000..390bd3b30b --- /dev/null +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GLTFDocumentExtension" inherits="Resource" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="export_post"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <description> + </description> + </method> + <method name="export_preflight"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <argument index="1" name="node" type="Node" /> + <description> + </description> + </method> + <method name="get_export_setting" qualifiers="const"> + <return type="Variant" /> + <argument index="0" name="key" type="StringName" /> + <description> + </description> + </method> + <method name="get_export_setting_keys" qualifiers="const"> + <return type="Array" /> + <description> + </description> + </method> + <method name="get_import_setting" qualifiers="const"> + <return type="Variant" /> + <argument index="0" name="key" type="StringName" /> + <description> + </description> + </method> + <method name="get_import_setting_keys" qualifiers="const"> + <return type="Array" /> + <description> + </description> + </method> + <method name="import_post"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <argument index="1" name="node" type="Node" /> + <description> + </description> + </method> + <method name="import_preflight"> + <return type="int" enum="Error" /> + <argument index="0" name="document" type="GLTFDocument" /> + <description> + </description> + </method> + <method name="set_export_setting"> + <return type="void" /> + <argument index="0" name="key" type="StringName" /> + <argument index="1" name="value" type="Variant" /> + <description> + </description> + </method> + <method name="set_import_setting"> + <return type="void" /> + <argument index="0" name="key" type="StringName" /> + <argument index="1" name="value" type="Variant" /> + <description> + </description> + </method> + </methods> +</class> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml new file mode 100644 index 0000000000..452eec5f4f --- /dev/null +++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="GLTFDocumentExtensionConvertImporterMesh" inherits="GLTFDocumentExtension" version="4.0"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> +</class> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index 51e9fc032a..1e7199d229 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -9,7 +9,7 @@ <members> <member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()"> </member> - <member name="mesh" type="EditorSceneImporterMesh" setter="set_mesh" getter="get_mesh"> + <member name="mesh" type="ImporterMesh" setter="set_mesh" getter="get_mesh"> </member> </members> </class> diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp index 3ce5ea23dc..25fda7ef3b 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#if TOOLS_ENABLED #include "editor_scene_exporter_gltf_plugin.h" #include "core/config/project_settings.h" #include "core/error/error_list.h" @@ -93,3 +94,5 @@ void SceneExporterGLTFPlugin::convert_scene_to_gltf2() { file_export_lib->set_current_file(filename + String(".gltf")); file_export_lib->popup_centered_ratio(); } + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h index c4f277fca2..89a8e27053 100644 --- a/modules/gltf/editor_scene_exporter_gltf_plugin.h +++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h @@ -31,7 +31,9 @@ #ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H #define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H +#if TOOLS_ENABLED #include "editor/editor_plugin.h" + #include "editor_scene_importer_gltf.h" class SceneExporterGLTFPlugin : public EditorPlugin { @@ -47,5 +49,5 @@ public: bool has_main_screen() const override; SceneExporterGLTFPlugin(class EditorNode *p_node); }; - +#endif // TOOLS_ENABLED #endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp index 12796c41d7..25875e7396 100644 --- a/modules/gltf/editor_scene_importer_gltf.cpp +++ b/modules/gltf/editor_scene_importer_gltf.cpp @@ -28,6 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#if TOOLS_ENABLED #include "editor_scene_importer_gltf.h" #include "gltf_document.h" @@ -60,3 +61,5 @@ Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, int p_bake_fps) { return Ref<Animation>(); } + +#endif // TOOLS_ENABLED diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h index eb8775b137..90663612a1 100644 --- a/modules/gltf/editor_scene_importer_gltf.h +++ b/modules/gltf/editor_scene_importer_gltf.h @@ -30,16 +30,17 @@ #ifndef EDITOR_SCENE_IMPORTER_GLTF_H #define EDITOR_SCENE_IMPORTER_GLTF_H - +#ifdef TOOLS_ENABLED #include "gltf_state.h" +#include "gltf_document_extension.h" + #include "editor/import/resource_importer_scene.h" #include "scene/main/node.h" #include "scene/resources/packed_scene.h" class Animation; -#ifdef TOOLS_ENABLED class EditorSceneImporterGLTF : public EditorSceneImporter { GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); @@ -50,5 +51,5 @@ public: virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override; }; -#endif +#endif // TOOLS_ENABLED #endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/gltf_accessor.cpp index daeb084916..85cec3fec4 100644 --- a/modules/gltf/gltf_accessor.cpp +++ b/modules/gltf/gltf_accessor.cpp @@ -30,6 +30,8 @@ #include "gltf_accessor.h" +#include "gltf_document_extension.h" + void GLTFAccessor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view); ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view); diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h index 57aea1026c..bec511f974 100644 --- a/modules/gltf/gltf_accessor.h +++ b/modules/gltf/gltf_accessor.h @@ -32,7 +32,9 @@ #define GLTF_ACCESSOR_H #include "core/io/resource.h" + #include "gltf_document.h" +#include "gltf_document_extension.h" struct GLTFAccessor : public Resource { GDCLASS(GLTFAccessor, Resource); diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/gltf_buffer_view.cpp index ba38a11c4c..d00e0f040f 100644 --- a/modules/gltf/gltf_buffer_view.cpp +++ b/modules/gltf/gltf_buffer_view.cpp @@ -30,6 +30,8 @@ #include "gltf_buffer_view.h" +#include "gltf_document_extension.h" + void GLTFBufferView::_bind_methods() { ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer); ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 3f1f218e78..ba98592600 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -33,6 +33,8 @@ #include "gltf_accessor.h" #include "gltf_animation.h" #include "gltf_camera.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" @@ -49,6 +51,7 @@ #include "core/io/json.h" #include "core/math/disjoint_set.h" #include "core/math/vector2.h" +#include "core/variant/dictionary.h" #include "core/variant/typed_array.h" #include "core/variant/variant.h" #include "core/version.h" @@ -57,8 +60,12 @@ #include "editor/import/resource_importer_scene.h" #include "scene/2d/node_2d.h" #include "scene/3d/camera_3d.h" +#include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" #include "scene/animation/animation_player.h" +#include "scene/resources/importer_mesh.h" +#include "scene/resources/mesh.h" +#include "scene/resources/multimesh.h" #include "scene/resources/surface_tool.h" #include "modules/modules_enabled.gen.h" @@ -2101,7 +2108,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) { Array meshes; for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) { print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i)); - Ref<EditorSceneImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); + Ref<ImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh(); if (import_mesh.is_null()) { continue; } @@ -2493,7 +2500,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { Array primitives = d["primitives"]; const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary(); - Ref<EditorSceneImporterMesh> import_mesh; + Ref<ImporterMesh> import_mesh; import_mesh.instantiate(); String mesh_name = "mesh"; if (d.has("name") && !String(d["name"]).is_empty()) { @@ -2732,17 +2739,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")); + Ref<SurfaceTool> mesh_surface_tool; + mesh_surface_tool.instantiate(); + mesh_surface_tool->create_from_triangle_arrays(array); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + mesh_surface_tool->index(); if (generate_tangents) { //must generate mikktspace tangents.. ergh.. - Ref<SurfaceTool> st; - st.instantiate(); - st->create_from_triangle_arrays(array); - if (a.has("JOINTS_0") && a.has("JOINTS_1")) { - st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); - } - st->generate_tangents(); - array = st->commit_to_arrays(); + mesh_surface_tool->generate_tangents(); } + array = mesh_surface_tool->commit_to_arrays(); Array morphs; //blend shapes @@ -2772,8 +2780,6 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { array_copy[l] = array[l]; } - array_copy[Mesh::ARRAY_INDEX] = Variant(); - if (t.has("POSITION")) { Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true); const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX]; @@ -2852,17 +2858,17 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; } + Ref<SurfaceTool> blend_surface_tool; + blend_surface_tool.instantiate(); + blend_surface_tool->create_from_triangle_arrays(array_copy); + if (a.has("JOINTS_0") && a.has("JOINTS_1")) { + blend_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); + } + blend_surface_tool->index(); if (generate_tangents) { - Ref<SurfaceTool> st; - st.instantiate(); - st->create_from_triangle_arrays(array_copy); - if (a.has("JOINTS_0") && a.has("JOINTS_1")) { - st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS); - } - st->deindex(); - st->generate_tangents(); - array_copy = st->commit_to_arrays(); + blend_surface_tool->generate_tangents(); } + array_copy = blend_surface_tool->commit_to_arrays(); morphs.push_back(array_copy); } @@ -2875,19 +2881,23 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { const int material = p["material"]; ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT); Ref<BaseMaterial3D> mat3d = state->materials[material]; + ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT); if (has_vertex_color) { mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); } mat = mat3d; - } else if (has_vertex_color) { + } else { Ref<StandardMaterial3D> mat3d; mat3d.instantiate(); - mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + if (has_vertex_color) { + mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + } mat = mat3d; } - - import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat.is_valid() ? mat->get_name() : String(), flags); + ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT); + import_mesh->add_surface(primitive, array, morphs, + Dictionary(), mat, mat->get_name(), flags); } Vector<float> blend_weights; @@ -3610,7 +3620,6 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) { material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); } } - if (d.has("alphaMode")) { const String &am = d["alphaMode"]; if (am == "BLEND") { @@ -5007,72 +5016,65 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInst if (p_mesh_instance->get_mesh().is_null()) { return -1; } - Ref<EditorSceneImporterMesh> import_mesh; - import_mesh.instantiate(); - Ref<Mesh> godot_mesh = p_mesh_instance->get_mesh(); - if (godot_mesh.is_null()) { - return -1; - } + Ref<ImporterMesh> current_mesh; + current_mesh.instantiate(); Vector<float> blend_weights; - Vector<String> blend_names; - int32_t blend_count = godot_mesh->get_blend_shape_count(); - blend_names.resize(blend_count); - blend_weights.resize(blend_count); - for (int32_t blend_i = 0; blend_i < godot_mesh->get_blend_shape_count(); blend_i++) { - String blend_name = godot_mesh->get_blend_shape_name(blend_i); - blend_names.write[blend_i] = blend_name; - import_mesh->add_blend_shape(blend_name); - } - for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) { - Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i); - Array arrays = godot_mesh->surface_get_arrays(surface_i); - Array blend_shape_arrays = godot_mesh->surface_get_blend_shape_arrays(surface_i); - Ref<Material> mat = godot_mesh->surface_get_material(surface_i); - Ref<ArrayMesh> godot_array_mesh = godot_mesh; - String surface_name; - if (godot_array_mesh.is_valid()) { - surface_name = godot_array_mesh->surface_get_name(surface_i); - } - if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) { - mat = p_mesh_instance->get_surface_override_material(surface_i); - } - if (p_mesh_instance->get_material_override().is_valid()) { - mat = p_mesh_instance->get_material_override(); - } - import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name, godot_mesh->surface_get_format(surface_i)); - } - for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { - blend_weights.write[blend_i] = 0.0f; + { + Ref<Mesh> import_mesh = p_mesh_instance->get_mesh(); + Ref<ArrayMesh> import_array_mesh = p_mesh_instance->get_mesh(); + if (import_mesh->get_blend_shape_count()) { + ArrayMesh::BlendShapeMode shape_mode = ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED; + if (import_array_mesh.is_valid()) { + shape_mode = import_array_mesh->get_blend_shape_mode(); + } + current_mesh->set_blend_shape_mode(shape_mode); + for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { + current_mesh->add_blend_shape(import_mesh->get_blend_shape_name(morph_i)); + } + } + for (int32_t surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) { + Array array = import_mesh->surface_get_arrays(surface_i); + Ref<Material> mat = import_mesh->surface_get_material(surface_i); + String mat_name; + if (mat.is_valid()) { + mat_name = mat->get_name(); + } + current_mesh->add_surface(import_mesh->surface_get_primitive_type(surface_i), + array, import_mesh->surface_get_blend_shape_arrays(surface_i), import_mesh->surface_get_lods(surface_i), mat, + mat_name, import_mesh->surface_get_format(surface_i)); + } + int32_t blend_count = import_mesh->get_blend_shape_count(); + blend_weights.resize(blend_count); + for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) { + blend_weights.write[blend_i] = 0.0f; + } } Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); - gltf_mesh->set_mesh(import_mesh); + gltf_mesh->set_mesh(current_mesh); gltf_mesh->set_blend_weights(blend_weights); GLTFMeshIndex mesh_i = state->meshes.size(); state->meshes.push_back(gltf_mesh); return mesh_i; } -EditorSceneImporterMeshNode3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) { +ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index) { Ref<GLTFNode> gltf_node = state->nodes[node_index]; ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr); - EditorSceneImporterMeshNode3D *mi = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D); print_verbose("glTF: Creating mesh for: " + gltf_node->get_name()); Ref<GLTFMesh> mesh = state->meshes.write[gltf_node->mesh]; if (mesh.is_null()) { return mi; } - Ref<EditorSceneImporterMesh> import_mesh = mesh->get_mesh(); + Ref<ImporterMesh> import_mesh = mesh->get_mesh(); if (import_mesh.is_null()) { return mi; } mi->set_mesh(import_mesh); - for (int i = 0; i < mesh->get_blend_weights().size(); i++) { - mi->set("blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i), mesh->get_blend_weights()[i]); - } return mi; } @@ -5241,7 +5243,7 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, co return; } else if (cast_to<MultiMeshInstance3D>(p_current)) { MultiMeshInstance3D *multi = cast_to<MultiMeshInstance3D>(p_current); - _convert_mult_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); + _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state); #ifdef MODULE_CSG_ENABLED } else if (cast_to<CSGShape3D>(p_current)) { CSGShape3D *shape = cast_to<CSGShape3D>(p_current); @@ -5292,13 +5294,8 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd } Ref<GLTFMesh> gltf_mesh; gltf_mesh.instantiate(); - Ref<EditorSceneImporterMesh> import_mesh; - import_mesh.instantiate(); - Ref<ArrayMesh> array_mesh = csg->get_meshes()[1]; - for (int32_t surface_i = 0; surface_i < array_mesh->get_surface_count(); surface_i++) { - import_mesh->add_surface(Mesh::PrimitiveType::PRIMITIVE_TRIANGLES, array_mesh->surface_get_arrays(surface_i), Array(), Dictionary(), mat, array_mesh->surface_get_name(surface_i)); - } - gltf_mesh->set_mesh(import_mesh); + Ref<ImporterMesh> array_mesh = csg->get_meshes()[1]; + gltf_mesh->set_mesh(array_mesh); GLTFMeshIndex mesh_i = state->meshes.size(); state->meshes.push_back(gltf_mesh); gltf_node->mesh = mesh_i; @@ -5364,7 +5361,7 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex Vector3 cell_location = cells[k]; int32_t cell = p_grid_map->get_cell_item( Vector3(cell_location.x, cell_location.y, cell_location.z)); - EditorSceneImporterMeshNode3D *import_mesh_node = memnew(EditorSceneImporterMeshNode3D); + ImporterMeshInstance3D *import_mesh_node = memnew(ImporterMeshInstance3D); import_mesh_node->set_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell)); Transform3D cell_xform; cell_xform.basis.set_orthogonal_index( @@ -5386,49 +5383,72 @@ void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex } #endif // MODULE_GRIDMAP_ENABLED -void GLTFDocument::_convert_mult_mesh_instance_to_gltf(MultiMeshInstance3D *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) { +void GLTFDocument::_convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, + GLTFNodeIndex p_parent_node_index, + GLTFNodeIndex p_root_node_index, + Ref<GLTFNode> gltf_node, Ref<GLTFState> state) { + ERR_FAIL_COND(!p_multi_mesh_instance); Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh(); - if (multi_mesh.is_valid()) { - for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); - instance_i++) { - GLTFNode *new_gltf_node = memnew(GLTFNode); - Transform3D transform; - if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) { - Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i); - transform.origin = - Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y); - real_t rotation = xform_2d.get_rotation(); - Quaternion quaternion(Vector3(0, 1, 0), rotation); - Size2 scale = xform_2d.get_scale(); - transform.basis.set_quaternion_scale(quaternion, - Vector3(scale.x, 0, scale.y)); - transform = - p_multi_mesh_instance->get_transform() * transform; - } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { - transform = p_multi_mesh_instance->get_transform() * - multi_mesh->get_instance_transform(instance_i); - } - Ref<ArrayMesh> mm = multi_mesh->get_mesh(); - if (mm.is_valid()) { - Ref<EditorSceneImporterMesh> mesh; - mesh.instantiate(); - for (int32_t surface_i = 0; surface_i < mm->get_surface_count(); surface_i++) { - Array surface = mm->surface_get_arrays(surface_i); - mesh->add_surface(mm->surface_get_primitive_type(surface_i), surface, Array(), Dictionary(), - mm->surface_get_material(surface_i), mm->get_name()); - } - Ref<GLTFMesh> gltf_mesh; - gltf_mesh.instantiate(); - gltf_mesh->set_name(multi_mesh->get_name()); - gltf_mesh->set_mesh(mesh); - new_gltf_node->mesh = state->meshes.size(); - state->meshes.push_back(gltf_mesh); - } - new_gltf_node->xform = transform; - new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name())); - gltf_node->children.push_back(state->nodes.size()); - state->nodes.push_back(new_gltf_node); + if (multi_mesh.is_null()) { + return; + } + Ref<GLTFMesh> gltf_mesh; + gltf_mesh.instantiate(); + Ref<Mesh> mesh = multi_mesh->get_mesh(); + if (mesh.is_null()) { + return; + } + gltf_mesh->set_name(multi_mesh->get_name()); + Ref<ImporterMesh> importer_mesh; + importer_mesh.instantiate(); + Ref<ArrayMesh> array_mesh = multi_mesh->get_mesh(); + if (array_mesh.is_valid()) { + importer_mesh->set_blend_shape_mode(array_mesh->get_blend_shape_mode()); + for (int32_t blend_i = 0; blend_i < array_mesh->get_blend_shape_count(); blend_i++) { + importer_mesh->add_blend_shape(array_mesh->get_blend_shape_name(blend_i)); + } + } + for (int32_t surface_i = 0; surface_i < mesh->get_surface_count(); surface_i++) { + Ref<Material> mat = mesh->surface_get_material(surface_i); + String material_name; + if (mat.is_valid()) { + material_name = mat->get_name(); } + Array blend_arrays; + if (array_mesh.is_valid()) { + blend_arrays = array_mesh->surface_get_blend_shape_arrays(surface_i); + } + importer_mesh->add_surface(mesh->surface_get_primitive_type(surface_i), mesh->surface_get_arrays(surface_i), + blend_arrays, mesh->surface_get_lods(surface_i), mat, material_name, mesh->surface_get_format(surface_i)); + } + gltf_mesh->set_mesh(importer_mesh); + GLTFMeshIndex mesh_index = state->meshes.size(); + state->meshes.push_back(gltf_mesh); + for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count(); + instance_i++) { + Transform3D transform; + if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) { + Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i); + transform.origin = + Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y); + real_t rotation = xform_2d.get_rotation(); + Quaternion quaternion(Vector3(0, 1, 0), rotation); + Size2 scale = xform_2d.get_scale(); + transform.basis.set_quaternion_scale(quaternion, + Vector3(scale.x, 0, scale.y)); + transform = p_multi_mesh_instance->get_transform() * transform; + } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) { + transform = p_multi_mesh_instance->get_transform() * + multi_mesh->get_instance_transform(instance_i); + } + Ref<GLTFNode> new_gltf_node; + new_gltf_node.instantiate(); + new_gltf_node->mesh = mesh_index; + new_gltf_node->xform = transform; + new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name())); + gltf_node->children.push_back(state->nodes.size()); + state->nodes.push_back(new_gltf_node); } } @@ -6106,8 +6126,8 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i); ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i)); - EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(mi_element->get()); - ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to EditorSceneImporterMeshNode3D", node_i, mi_element->get()->get_class_name())); + ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->get()); + ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->get()->get_class_name())); const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton; Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i]; @@ -6748,10 +6768,25 @@ Error GLTFDocument::save_scene(Node *p_node, const String &p_path, Ref<GLTFDocument> gltf_document; gltf_document.instantiate(); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->export_preflight(this, p_node); + ERR_FAIL_COND_V(err != OK, err); + } + if (r_state == Ref<GLTFState>()) { r_state.instantiate(); } - return gltf_document->serialize(r_state, p_node, p_path); + Error err = gltf_document->serialize(r_state, p_node, p_path); + ERR_FAIL_COND_V(err != OK, err); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->export_post(this); + ERR_FAIL_COND_V(err != OK, err); + } + return OK; } Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err) { @@ -6764,6 +6799,15 @@ Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, in Ref<GLTFDocument> gltf_document; gltf_document.instantiate(); + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + Error err = ext->import_preflight(this); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, nullptr); + } Error err = gltf_document->parse(r_state, p_path); if (r_err) { *r_err = err; @@ -6783,7 +6827,15 @@ Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, in gltf_document->_import_animation(r_state, ap, i, p_bake_fps); } } - + for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) { + Ref<GLTFDocumentExtension> ext = document_extensions[ext_i]; + ERR_CONTINUE(ext.is_null()); + err = ext->import_post(this, root); + if (r_err) { + *r_err = err; + } + ERR_FAIL_COND_V(err != OK, nullptr); + } return root; } @@ -6792,6 +6844,14 @@ void GLTFDocument::_bind_methods() { &GLTFDocument::save_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); ClassDB::bind_method(D_METHOD("import_scene", "path", "flags", "bake_fps", "state"), &GLTFDocument::import_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>())); + ClassDB::bind_method(D_METHOD("set_extensions", "extensions"), + &GLTFDocument::set_extensions); + ClassDB::bind_method(D_METHOD("get_extensions"), + &GLTFDocument::get_extensions); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE, + vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"), + PROPERTY_USAGE_DEFAULT), + "set_extensions", "get_extensions"); } void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) { @@ -6817,3 +6877,21 @@ Node *GLTFDocument::import_scene(const String &p_path, uint32_t p_flags, int32_t } return node; } + +void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) { + document_extensions = p_extensions; +} + +TypedArray<GLTFDocumentExtension> GLTFDocument::get_extensions() const { + return document_extensions; +} + +GLTFDocument::GLTFDocument() { + bool is_editor = ::Engine::get_singleton()->is_editor_hint(); + if (is_editor) { + return; + } + Ref<GLTFDocumentExtensionConvertImporterMesh> extension_editor; + extension_editor.instantiate(); + document_extensions.push_back(extension_editor); +} diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index 18aeb81bc0..7317c6a9a3 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -33,8 +33,11 @@ #include "gltf_animation.h" -#include "editor/import/scene_importer_mesh_node_3d.h" +#include "core/variant/dictionary.h" +#include "core/variant/variant.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "scene/3d/bone_attachment_3d.h" +#include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/node_3d.h" @@ -54,6 +57,7 @@ class GLTFSkeleton; class CSGShape3D; class GridMap; class MultiMeshInstance3D; +class GLTFDocumentExtension; using GLTFAccessorIndex = int; using GLTFAnimationIndex = int; @@ -74,11 +78,13 @@ class GLTFDocument : public Resource { friend class GLTFState; friend class GLTFSkin; friend class GLTFSkeleton; + TypedArray<GLTFDocumentExtension> document_extensions; private: const float BAKE_FPS = 30.0f; public: + GLTFDocument(); const int32_t JOINT_GROUP_SIZE = 4; enum GLTFType { TYPE_SCALAR, @@ -118,6 +124,8 @@ public: Error save_scene(Node *p_node, const String &p_path, const String &p_src_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state); + void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions); + TypedArray<GLTFDocumentExtension> get_extensions() const; private: template <class T> @@ -280,12 +288,10 @@ private: Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index); - EditorSceneImporterMeshNode3D *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); - Camera3D *_generate_camera(Ref<GLTFState> state, Node *scene_parent, - const GLTFNodeIndex node_index); - Node3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index); - Node3D *_generate_spatial(Ref<GLTFState> state, Node *scene_parent, - const GLTFNodeIndex node_index); + ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Camera3D *_generate_camera(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Node3D *_generate_light(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); + Node3D *_generate_spatial(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index); void _assign_scene_names(Ref<GLTFState> state); template <class T> T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, @@ -420,8 +426,8 @@ public: GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state); #endif // MODULE_GRIDMAP_ENABLED - void _convert_mult_mesh_instance_to_gltf( - MultiMeshInstance3D *p_scene_parent, + void _convert_multi_mesh_instance_to_gltf( + MultiMeshInstance3D *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state); diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp new file mode 100644 index 0000000000..a423059a9c --- /dev/null +++ b/modules/gltf/gltf_document_extension.cpp @@ -0,0 +1,88 @@ +/*************************************************************************/ +/* gltf_document_extension.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_document_extension.h" + +#include "gltf_document.h" + +void GLTFDocumentExtension::_bind_methods() { + // Import + ClassDB::bind_method(D_METHOD("get_import_setting_keys"), + &GLTFDocumentExtension::get_import_setting_keys); + ClassDB::bind_method(D_METHOD("import_preflight", "document"), + &GLTFDocumentExtension::import_preflight); + ClassDB::bind_method(D_METHOD("get_import_setting", "key"), + &GLTFDocumentExtension::get_import_setting); + ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"), + &GLTFDocumentExtension::set_import_setting); + ClassDB::bind_method(D_METHOD("import_post", "document", "node"), + &GLTFDocumentExtension::import_post); + // Export + ClassDB::bind_method(D_METHOD("get_export_setting_keys"), + &GLTFDocumentExtension::get_export_setting_keys); + ClassDB::bind_method(D_METHOD("get_export_setting", "key"), + &GLTFDocumentExtension::get_export_setting); + ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"), + &GLTFDocumentExtension::set_export_setting); + ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"), + &GLTFDocumentExtension::export_preflight); + ClassDB::bind_method(D_METHOD("export_post", "document"), + &GLTFDocumentExtension::export_post); +} + +Array GLTFDocumentExtension::get_import_setting_keys() const { + return import_settings.keys(); +} + +Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const { + if (!import_settings.has(p_key)) { + return Variant(); + } + return import_settings[p_key]; +} + +void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) { + import_settings[p_key] = p_var; +} + +Array GLTFDocumentExtension::get_export_setting_keys() const { + return import_settings.keys(); +} + +Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const { + if (!import_settings.has(p_key)) { + return Variant(); + } + return import_settings[p_key]; +} + +void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) { + import_settings[p_key] = p_var; +} diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h new file mode 100644 index 0000000000..622a65708c --- /dev/null +++ b/modules/gltf/gltf_document_extension.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* gltf_document_extension.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_DOCUMENT_EXTENSION_H +#define GLTF_DOCUMENT_EXTENSION_H + +#include "core/io/resource.h" +#include "core/variant/dictionary.h" +#include "core/variant/typed_array.h" +#include "core/variant/variant.h" +class GLTFDocument; +class GLTFDocumentExtension : public Resource { + GDCLASS(GLTFDocumentExtension, Resource); + + Dictionary import_settings; + Dictionary export_settings; + +protected: + static void _bind_methods(); + +public: + virtual Array get_import_setting_keys() const; + virtual Variant get_import_setting(const StringName &p_key) const; + virtual void set_import_setting(const StringName &p_key, Variant p_var); + virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; } + virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } + +public: + virtual Array get_export_setting_keys() const; + virtual Variant get_export_setting(const StringName &p_key) const; + virtual void set_export_setting(const StringName &p_key, Variant p_var); + virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; } + virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; } +}; + +#endif // GLTF_DOCUMENT_EXTENSION_H diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp new file mode 100644 index 0000000000..78a98dfa3e --- /dev/null +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp @@ -0,0 +1,79 @@ +/*************************************************************************/ +/* gltf_document_extension_convert_importer_mesh.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gltf_document_extension_convert_importer_mesh.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" + +#include <cstddef> + +void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() { +} + +Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) { + List<Node *> queue; + queue.push_back(p_node); + List<Node *> delete_queue; + while (!queue.is_empty()) { + List<Node *>::Element *E = queue.front(); + Node *node = E->get(); + { + ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node); + if (mesh_3d) { + MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D); + Ref<ImporterMesh> mesh = mesh_3d->get_mesh(); + if (mesh.is_valid()) { + Ref<ArrayMesh> array_mesh = mesh->get_mesh(); + mesh_instance_node_3d->set_name(node->get_name()); + mesh_instance_node_3d->set_transform(mesh_3d->get_transform()); + mesh_instance_node_3d->set_mesh(array_mesh); + mesh_instance_node_3d->set_skin(mesh_3d->get_skin()); + mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path()); + node->replace_by(mesh_instance_node_3d); + delete_queue.push_back(node); + } else { + memdelete(mesh_instance_node_3d); + } + } + } + int child_count = node->get_child_count(); + for (int i = 0; i < child_count; i++) { + queue.push_back(node->get_child(i)); + } + queue.pop_front(); + } + while (!queue.is_empty()) { + List<Node *>::Element *E = delete_queue.front(); + Node *node = E->get(); + memdelete(node); + delete_queue.pop_front(); + } + return OK; +} diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h new file mode 100644 index 0000000000..85ddb4d250 --- /dev/null +++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h @@ -0,0 +1,55 @@ +/*************************************************************************/ +/* gltf_document_extension_convert_importer_mesh.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GLTF_EXTENSION_EDITOR_H +#define GLTF_EXTENSION_EDITOR_H + +#include "core/io/resource.h" +#include "core/variant/dictionary.h" + +#include "gltf_document.h" +#include "gltf_document_extension.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/main/node.h" +#include "scene/resources/importer_mesh.h" + +class GLTFDocumentExtension; +class GLTFDocument; +class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension { + GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension); + +protected: + static void _bind_methods(); + +public: + Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override; +}; +#endif // GLTF_EXTENSION_EDITOR_H diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/gltf_mesh.cpp index 8c10e42c89..747820521a 100644 --- a/modules/gltf/gltf_mesh.cpp +++ b/modules/gltf/gltf_mesh.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "gltf_mesh.h" -#include "editor/import/scene_importer_mesh.h" +#include "scene/resources/importer_mesh.h" void GLTFMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFMesh::get_mesh); @@ -41,11 +41,11 @@ void GLTFMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector<float> } -Ref<EditorSceneImporterMesh> GLTFMesh::get_mesh() { +Ref<ImporterMesh> GLTFMesh::get_mesh() { return mesh; } -void GLTFMesh::set_mesh(Ref<EditorSceneImporterMesh> p_mesh) { +void GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) { mesh = p_mesh; } diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/gltf_mesh.h index 0fc750fc9f..3aba0ede32 100644 --- a/modules/gltf/gltf_mesh.h +++ b/modules/gltf/gltf_mesh.h @@ -33,22 +33,23 @@ #include "core/io/resource.h" #include "editor/import/resource_importer_scene.h" -#include "editor/import/scene_importer_mesh.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/resources/importer_mesh.h" #include "scene/resources/mesh.h" class GLTFMesh : public Resource { GDCLASS(GLTFMesh, Resource); private: - Ref<EditorSceneImporterMesh> mesh; + Ref<ImporterMesh> mesh; Vector<float> blend_weights; protected: static void _bind_methods(); public: - Ref<EditorSceneImporterMesh> get_mesh(); - void set_mesh(Ref<EditorSceneImporterMesh> p_mesh); + Ref<ImporterMesh> get_mesh(); + void set_mesh(Ref<ImporterMesh> p_mesh); Vector<float> get_blend_weights(); void set_blend_weights(Vector<float> p_blend_weights); }; diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h index d6614da804..61faba0dc5 100644 --- a/modules/gltf/gltf_state.h +++ b/modules/gltf/gltf_state.h @@ -36,6 +36,7 @@ #include "gltf_buffer_view.h" #include "gltf_camera.h" #include "gltf_document.h" +#include "gltf_document_extension.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp index d6020f50f0..0aceb838f7 100644 --- a/modules/gltf/register_types.cpp +++ b/modules/gltf/register_types.cpp @@ -38,6 +38,8 @@ #include "gltf_buffer_view.h" #include "gltf_camera.h" #include "gltf_document.h" +#include "gltf_document_extension.h" +#include "gltf_document_extension_convert_importer_mesh.h" #include "gltf_light.h" #include "gltf_mesh.h" #include "gltf_node.h" @@ -79,6 +81,8 @@ void register_gltf_types() { GDREGISTER_CLASS(GLTFCamera); GDREGISTER_CLASS(GLTFLight); GDREGISTER_CLASS(GLTFState); + GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh); + GDREGISTER_CLASS(GLTFDocumentExtension); GDREGISTER_CLASS(GLTFDocument); #endif } diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index c170bb107e..08d55de976 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -603,9 +603,9 @@ void GridMapEditor::_do_paste() { _clear_clipboard_data(); } -bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) { +EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) { if (!node) { - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } Ref<InputEventMouseButton> mb = p_event; @@ -616,12 +616,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In floor->set_value(floor->get_value() + mb->get_factor()); } - return true; // Eaten. + return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten. } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) { if (mb->is_pressed()) { floor->set_value(floor->get_value() - mb->get_factor()); } - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (mb->is_pressed()) { @@ -648,19 +648,22 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In _clear_clipboard_data(); input_action = INPUT_NONE; _update_paste_indicator(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (selection.active) { _set_selection(false); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { input_action = INPUT_ERASE; set_items.clear(); } } else { - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } - return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true); + if (do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true)) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } else { if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_PAINT)) { if (set_items.size()) { @@ -677,7 +680,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In } set_items.clear(); input_action = INPUT_NONE; - return set_items.size() > 0; + + if (set_items.size() > 0) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_SELECT) { @@ -690,11 +697,11 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action != INPUT_NONE) { set_items.clear(); input_action = INPUT_NONE; - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) { input_action = INPUT_NONE; - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } } @@ -702,7 +709,10 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - return do_input_action(p_camera, mm->get_position(), false); + if (do_input_action(p_camera, mm->get_position(), false)) { + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + return EditorPlugin::AFTER_GUI_INPUT_PASS; } Ref<InputEventKey> k = p_event; @@ -714,16 +724,16 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In _clear_clipboard_data(); input_action = INPUT_NONE; _update_paste_indicator(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (selection.active) { _set_selection(false); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { selected_palette = -1; mesh_library_palette->deselect_all(); update_palette(); _update_cursor_instance(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } @@ -731,12 +741,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) { selection.click[edit_axis]--; _validate_selection(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) { selection.click[edit_axis]++; _validate_selection(); - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } } @@ -755,12 +765,12 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In if (step) { floor->set_value(floor->get_value() + step); } - return true; + return EditorPlugin::AFTER_GUI_INPUT_STOP; } } accumulated_floor_delta = 0.0; - return false; + return EditorPlugin::AFTER_GUI_INPUT_PASS; } struct _CGMEItemSort { diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h index 4bc849e071..1a6b1310d8 100644 --- a/modules/gridmap/grid_map_editor_plugin.h +++ b/modules/gridmap/grid_map_editor_plugin.h @@ -232,7 +232,7 @@ protected: static void _bind_methods(); public: - bool forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event); + EditorPlugin::AfterGUIInput forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event); void edit(GridMap *p_gridmap); GridMapEditor() {} @@ -250,7 +250,7 @@ protected: void _notification(int p_what); public: - virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); } + virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); } virtual String get_name() const override { return "GridMap"; } bool has_main_screen() const override { return false; } virtual void edit(Object *p_object) override; diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index 45a6f991bf..abd860a55f 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -8,7 +8,7 @@ See also [GodotSharp]. </description> <tutorials> - <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link> + <link title="C# documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/index.html</link> </tutorials> <methods> <method name="new" qualifiers="vararg"> diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index 0f50c90531..1d7bfaf0a4 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -121,6 +121,7 @@ namespace GodotTools.IdeMessaging this.messageHandler = messageHandler; this.logger = logger; + // TODO: Need to fetch the project data dir name from ProjectSettings instead of defaulting to ".godot" string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata"); MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs index 8fe08e7e1d..746612477d 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs @@ -117,7 +117,7 @@ namespace Godot /// { /// for (int i = 0; i < 100; i++) /// { - /// await ToSignal(GetTree(), "idle_frame"); + /// await ToSignal(GetTree(), "process_frame"); /// GD.Print($"Frame {i}"); /// } /// } diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 375ad413c4..24bd1ed492 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -122,7 +122,7 @@ public: private: _GodotSharpDirs() { - res_data_dir = "res://.godot/mono"; + res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono"); res_metadata_dir = res_data_dir.plus_file("metadata"); res_assemblies_base_dir = res_data_dir.plus_file("assemblies"); res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config()); diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 6b616dd52d..d0a27b27c1 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -35,7 +35,7 @@ #include "core/os/os.h" -// Here, after os/os.h +#define WIN32_LEAN_AND_MEAN #include <windows.h> namespace MonoRegUtils { diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index ec04d50704..64aec5d359 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -36,6 +36,7 @@ #include "core/os/os.h" #ifdef WINDOWS_ENABLED +#define WIN32_LEAN_AND_MEAN #include <windows.h> #define ENV_PATH_SEP ";" diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 2f6faec8ec..8e80dfffca 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -108,7 +108,7 @@ void VideoStreamPlaybackTheora::video_write() { Ref<Image> img = memnew(Image(size.x, size.y, 0, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation - texture->update(img); //zero copy send to visual server + texture->update(img); //zero copy send to rendering server frames_pending = 1; } diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py index b15479797c..e8990c43c8 100644 --- a/modules/visual_script/config.py +++ b/modules/visual_script/config.py @@ -17,6 +17,7 @@ def get_doc_classes(): "VisualScriptConstant", "VisualScriptConstructor", "VisualScriptCustomNode", + "VisualScriptCustomNodes", "VisualScriptDeconstruct", "VisualScriptEditor", "VisualScriptEmitSignal", diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml index 372d46bc10..be6bf00e50 100644 --- a/modules/visual_script/doc_classes/VisualScript.xml +++ b/modules/visual_script/doc_classes/VisualScript.xml @@ -9,7 +9,7 @@ You are most likely to use this class via the Visual Script editor or when writing plugins for it. </description> <tutorials> - <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link> + <link title="VisualScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/visual_script/index.html</link> </tutorials> <methods> <method name="add_custom_signal"> diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml new file mode 100644 index 0000000000..1681da7653 --- /dev/null +++ b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VisualScriptCustomNodes" inherits="Object" version="4.0"> + <brief_description> + Manages custom nodes for the Visual Script editor. + </brief_description> + <description> + This singleton can be used to manage (i.e., add or remove) custom nodes for the Visual Script editor. + </description> + <tutorials> + </tutorials> + <methods> + <method name="add_custom_node"> + <return type="void" /> + <argument index="0" name="name" type="String" /> + <argument index="1" name="category" type="String" /> + <argument index="2" name="script" type="Script" /> + <description> + Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter. + </description> + </method> + <method name="remove_custom_node"> + <return type="void" /> + <argument index="0" name="name" type="String" /> + <argument index="1" name="category" type="String" /> + <description> + Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed. + </description> + </method> + </methods> + <signals> + <signal name="custom_nodes_updated"> + <description> + Emitted when a custom Visual Script node is added or removed. + </description> + </signal> + </signals> +</class> diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp index cded1e587c..c62de64a85 100644 --- a/modules/visual_script/visual_script_yield_nodes.cpp +++ b/modules/visual_script/visual_script_yield_nodes.cpp @@ -121,7 +121,7 @@ public: ret = STEP_EXIT_FUNCTION_BIT; break; //return the yield case VisualScriptYield::YIELD_FRAME: - state->connect_to_signal(tree, "idle_frame", Array()); + state->connect_to_signal(tree, "process_frame", Array()); break; case VisualScriptYield::YIELD_PHYSICS_FRAME: state->connect_to_signal(tree, "physics_frame", Array()); diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp index 12e0f5bd25..187a27b6c2 100644 --- a/modules/webm/video_stream_webm.cpp +++ b/modules/webm/video_stream_webm.cpp @@ -311,7 +311,7 @@ void VideoStreamPlaybackWebm::update(float p_delta) { if (converted) { Ref<Image> img = memnew(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data)); - texture->update(img); //Zero copy send to visual server + texture->update(img); //Zero copy send to rendering server video_frame_done = true; } } diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index f6f360503f..618fe14137 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -26,7 +26,8 @@ <method name="close"> <return type="void" /> <description> - Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize]. + Close the peer connection and all data channels associated with it. + [b]Note:[/b] You cannot reuse this object for a new connection unless you call [method initialize]. </description> </method> <method name="create_data_channel"> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index ab7ef6c4d0..2f455c32fd 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -4,7 +4,7 @@ A class representing a specific WebSocket connection. </brief_description> <description> - This class represent a specific WebSocket connection, you can do lower level operations with it. + This class represents a specific WebSocket connection, allowing you to do lower level operations with it. You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer. </description> <tutorials> diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index f7a8944745..bf35c91c7f 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -129,7 +129,7 @@ void WebSocketClient::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate", "cert"), &WebSocketClient::set_trusted_ssl_certificate); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate"); diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index fb838109f3..e7f90fdeba 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -50,19 +50,19 @@ void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_bind_ip"), &WebSocketServer::get_bind_ip); - ClassDB::bind_method(D_METHOD("set_bind_ip"), &WebSocketServer::set_bind_ip); + ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &WebSocketServer::set_bind_ip); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bind_ip"), "set_bind_ip", "get_bind_ip"); ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key); - ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key); + ClassDB::bind_method(D_METHOD("set_private_key", "key"), &WebSocketServer::set_private_key); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", PROPERTY_USAGE_NONE), "set_private_key", "get_private_key"); ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate); - ClassDB::bind_method(D_METHOD("set_ssl_certificate"), &WebSocketServer::set_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_ssl_certificate", "cert"), &WebSocketServer::set_ssl_certificate); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ssl_certificate", "get_ssl_certificate"); ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain); - ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain); + ClassDB::bind_method(D_METHOD("set_ca_chain", "ca_chain"), &WebSocketServer::set_ca_chain); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ca_chain", "get_ca_chain"); ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout); |