diff options
Diffstat (limited to 'modules')
197 files changed, 1467 insertions, 1165 deletions
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index c241f1cabd..4c217dac28 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -953,12 +953,12 @@ void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) { return; } if (mesh.is_valid()) { - mesh->disconnect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed)); + mesh->disconnect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed)); } mesh = p_mesh; if (mesh.is_valid()) { - mesh->connect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed)); + mesh->connect_changed(callable_mp(this, &CSGMesh3D::_mesh_changed)); } _mesh_changed(); diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml index 3d756a3822..f56cb355ca 100644 --- a/modules/csg/doc_classes/CSGBox3D.xml +++ b/modules/csg/doc_classes/CSGBox3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGBox3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGBox3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG Box shape. </brief_description> diff --git a/modules/csg/doc_classes/CSGCombiner3D.xml b/modules/csg/doc_classes/CSGCombiner3D.xml index bf50d6151b..d785b2f818 100644 --- a/modules/csg/doc_classes/CSGCombiner3D.xml +++ b/modules/csg/doc_classes/CSGCombiner3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGCombiner3D" inherits="CSGShape3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGCombiner3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG node that allows you to combine other CSG modifiers. </brief_description> diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml index 489371bb0e..f722b569e7 100644 --- a/modules/csg/doc_classes/CSGCylinder3D.xml +++ b/modules/csg/doc_classes/CSGCylinder3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGCylinder3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGCylinder3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG Cylinder shape. </brief_description> diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml index 9d409e442d..9a0f121e19 100644 --- a/modules/csg/doc_classes/CSGMesh3D.xml +++ b/modules/csg/doc_classes/CSGMesh3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGMesh3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGMesh3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG Mesh shape that uses a mesh resource. </brief_description> diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml index b7cc7c29d0..338adc9b52 100644 --- a/modules/csg/doc_classes/CSGPolygon3D.xml +++ b/modules/csg/doc_classes/CSGPolygon3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGPolygon3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGPolygon3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Extrudes a 2D polygon shape to create a 3D mesh. </brief_description> diff --git a/modules/csg/doc_classes/CSGPrimitive3D.xml b/modules/csg/doc_classes/CSGPrimitive3D.xml index 1686175d1d..6afbf4a3d7 100644 --- a/modules/csg/doc_classes/CSGPrimitive3D.xml +++ b/modules/csg/doc_classes/CSGPrimitive3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGPrimitive3D" inherits="CSGShape3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGPrimitive3D" inherits="CSGShape3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Base class for CSG primitives. </brief_description> diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index cae30ed446..0414aa362d 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGShape3D" inherits="GeometryInstance3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGShape3D" inherits="GeometryInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> The CSG base class. </brief_description> diff --git a/modules/csg/doc_classes/CSGSphere3D.xml b/modules/csg/doc_classes/CSGSphere3D.xml index bdcee99647..a11979444e 100644 --- a/modules/csg/doc_classes/CSGSphere3D.xml +++ b/modules/csg/doc_classes/CSGSphere3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGSphere3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGSphere3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG Sphere shape. </brief_description> diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml index 856c2d8731..9bef522bb6 100644 --- a/modules/csg/doc_classes/CSGTorus3D.xml +++ b/modules/csg/doc_classes/CSGTorus3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSGTorus3D" inherits="CSGPrimitive3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSGTorus3D" inherits="CSGPrimitive3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A CSG Torus shape. </brief_description> diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index e6523e3d09..8a3a36e84b 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -31,6 +31,7 @@ #include "texture_loader_dds.h" #include "core/io/file_access.h" +#include "scene/resources/image_texture.h" #define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0]))) diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h index dc3df1fcee..3763700ff1 100644 --- a/modules/dds/texture_loader_dds.h +++ b/modules/dds/texture_loader_dds.h @@ -32,7 +32,6 @@ #define TEXTURE_LOADER_DDS_H #include "core/io/resource_loader.h" -#include "scene/resources/texture.h" class ResourceFormatDDS : public ResourceFormatLoader { public: diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml index b46a3273c8..1f2964522b 100644 --- a/modules/enet/doc_classes/ENetConnection.xml +++ b/modules/enet/doc_classes/ENetConnection.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ENetConnection" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="ENetConnection" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A wrapper class for an [url=http://enet.bespin.org/group__host.html]ENetHost[/url]. </brief_description> diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml index 646cf37b55..c49d16eb13 100644 --- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml +++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library. </brief_description> diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml index 0e531b0e89..3171da1f6d 100644 --- a/modules/enet/doc_classes/ENetPacketPeer.xml +++ b/modules/enet/doc_classes/ENetPacketPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ENetPacketPeer" inherits="PacketPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="ENetPacketPeer" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A wrapper class for an [url=http://enet.bespin.org/group__peer.html]ENetPeer[/url]. </brief_description> diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index f2a65451a7..0c300eade4 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="@GDScript" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="@GDScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Built-in GDScript constants, functions, and annotations. </brief_description> @@ -620,10 +620,10 @@ <param index="3" name="transfer_channel" type="int" default="0" /> <description> Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url]. - The accepted values: - - for [param mode] are [code]"any_peer"[/code] or [code]"authority"[/code]; - - for [param sync] are [code]"call_remote"[/code] or [code]"call_local"[/code]; - - and for [param transfer_mode] are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code]. + If [param mode] is set as [code]"any_peer"[/code], allows any peer to call this RPC function. Otherwise, only the authority peer is allowed to call it and [param mode] should be kept as [code]"authority"[/code]. When configuring functions as RPCs with [method Node.rpc_config], each of these modes respectively corresponds to the [constant MultiplayerAPI.RPC_MODE_AUTHORITY] and [constant MultiplayerAPI.RPC_MODE_ANY_PEER] RPC modes. See [enum MultiplayerAPI.RPCMode]. If a peer that is not the authority tries to call a function that is only allowed for the authority, the function will not be executed. If the error can be detected locally (when the RPC configuration is consistent between the local and the remote peer), an error message will be displayed on the sender peer. Otherwise, the remote peer will detect the error and print an error there. + If [param sync] is set as [code]"call_remote"[/code], the function will only be executed on the remote peer, but not locally. To run this function locally too, set [param sync] to [code]"call_local"[/code]. When configuring functions as RPCs with [method Node.rpc_config], this is equivalent to setting `call_local` to `true`. + The [param transfer_mode] accepted values are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code]. It sets the transfer mode of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_mode]. + The [param transfer_channel] defines the channel of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_channel]. The order of [param mode], [param sync] and [param transfer_mode] does not matter, but values related to the same argument must not be used more than once. [param transfer_channel] always has to be the 4th argument (you must specify 3 preceding arguments). [codeblock] @rpc diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index f383eed480..5f7a7e2915 100644 --- a/modules/gdscript/doc_classes/GDScript.xml +++ b/modules/gdscript/doc_classes/GDScript.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GDScript" inherits="Script" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GDScript" inherits="Script" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A script implemented in the GDScript programming language. </brief_description> <description> - A script implemented in the GDScript programming language. The script extends the functionality of all objects that instantiate it. + A script implemented in the GDScript programming language, saved with the [code].gd[/code] extension. The script extends the functionality of all objects that instantiate it. Calling [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. If you are looking for GDScript's built-in functions, see [@GDScript] instead. </description> diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index df17581ad1..0d8453738d 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -236,6 +236,8 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c p_script->member_lines[name] = m_enum->start_line; + doc.enums[name] = m_enum->doc_description; + for (const GDP::EnumNode::Value &val : m_enum->values) { DocData::ConstantDoc const_doc; const_doc.name = val.identifier->name; @@ -244,7 +246,6 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c const_doc.description = val.doc_description; const_doc.enumeration = name; - doc.enums[const_doc.name] = const_doc.description; doc.constants.push_back(const_doc); } diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8f870368ce..7117337827 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -787,11 +787,11 @@ Error GDScript::reload(bool p_keep_state) { err = compiler.compile(&parser, this, p_keep_state); if (err) { + _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); if (can_run) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } - _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); reloading = false; return ERR_COMPILATION_FAILED; } else { @@ -1542,7 +1542,7 @@ GDScript::~GDScript() { { MutexLock lock(GDScriptLanguage::get_singleton()->mutex); - GDScriptLanguage::get_singleton()->script_list.remove(&script_list); + script_list.remove_from_list(); } } @@ -1747,11 +1747,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const Dictionary d = arr[i]; ERR_CONTINUE(!d.has("name")); ERR_CONTINUE(!d.has("type")); + PropertyInfo pinfo; - pinfo.type = Variant::Type(d["type"].operator int()); - ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX); pinfo.name = d["name"]; - ERR_CONTINUE(pinfo.name.is_empty()); + pinfo.type = Variant::Type(d["type"].operator int()); if (d.has("hint")) { pinfo.hint = PropertyHint(d["hint"].operator int()); } @@ -1765,6 +1764,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const pinfo.class_name = d["class_name"]; } + ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE)); + ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX); + props.push_back(pinfo); } } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 9d39cbf1a5..d3445b8cc0 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3097,16 +3097,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new"); if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) { - // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there. - // MethodInfo's above the class that defines the method might still have the virtual flag set. - if (method_flags.has_flag(METHOD_FLAG_VIRTUAL)) { - if (p_call->is_super) { - push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call); - } else { - push_error(vformat(R"*(Cannot call virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call); - } - } - // If the function require typed arrays we must make literals be typed. for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index e5bb93e3c8..d191bd0224 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -294,8 +294,12 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro if (p_update_from_disk) { r_error = script->load_source_code(p_path); + if (r_error) { + return script; + } } + r_error = script->reload(true); if (r_error) { return script; } @@ -303,7 +307,6 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro singleton->full_gdscript_cache[p_path] = script; singleton->shallow_gdscript_cache.erase(p_path); - script->reload(true); return script; } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 90ee56b9de..3f571602e8 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1767,25 +1767,39 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern."); } -void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { +List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) { + List<GDScriptCodeGenerator::Address> addresses; for (int i = 0; i < p_block->locals.size(); i++) { if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) { // Parameters are added directly from function and loop variables are declared explicitly. continue; } - codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script)); + addresses.push_back(codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script))); } + return addresses; } -Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals) { +// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed. +void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) { + for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) { + GDScriptDataType type = E->get().type; + // If not an object and cannot contain an object, no need to clear. + if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) { + codegen.generator->write_assign_false(E->get()); + } + } +} + +Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) { Error err = OK; GDScriptCodeGenerator *gen = codegen.generator; + List<GDScriptCodeGenerator::Address> block_locals; gen->clean_temporaries(); codegen.start_block(); if (p_add_locals) { - _add_locals_in_block(codegen, p_block); + block_locals = _add_locals_in_block(codegen, p_block); } for (int i = 0; i < p_block->statements.size(); i++) { @@ -1841,7 +1855,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui codegen.start_block(); // Create an extra block around for binds. // Add locals in block before patterns, so temporaries don't use the stack address for binds. - _add_locals_in_block(codegen, branch->block); + List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block); #ifdef DEBUG_ENABLED // Add a newline before each branch, since the debugger needs those. @@ -1868,6 +1882,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } + _clear_addresses(codegen, branch_locals); + codegen.end_block(); // Get out of extra block. } @@ -2052,7 +2068,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } // Assigns a null for the unassigned variables in loops. - if (!initialized && p_block->is_loop) { + if (!initialized && p_block->is_in_loop) { codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>()); } } break; @@ -2088,6 +2104,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui gen->clean_temporaries(); } + if (p_add_locals && p_reset_locals) { + _clear_addresses(codegen, block_locals); + } + codegen.end_block(); return OK; } @@ -2241,7 +2261,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ codegen.generator->end_parameters(); } - r_error = _parse_block(codegen, p_func->body); + // No need to reset locals at the end of the function, the stack will be cleared anyway. + r_error = _parse_block(codegen, p_func->body, true, false); if (r_error) { memdelete(codegen.generator); return nullptr; @@ -2558,9 +2579,9 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri } } else if (!base->is_valid()) { Error err = OK; - Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path); + Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path); if (err) { - _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr); + _set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr); return err; } if (base_root.is_valid()) { @@ -2570,7 +2591,12 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri _set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr); return ERR_COMPILATION_FAILED; } - ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG); + + err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state); + if (err) { + _set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr); + return err; + } } p_script->base = base; @@ -2947,7 +2973,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri GDScriptCache::add_static_script(p_script); } - return GDScriptCache::finish_compiling(main_script->get_path()); + return GDScriptCache::finish_compiling(main_script->path); } String GDScriptCompiler::get_error() const { diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index 494eef41d9..2f522da4ea 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -129,8 +129,9 @@ class GDScriptCompiler { GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address()); GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested); - void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); - Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true); + List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block); + void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses); + Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_reset_locals = true); GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false); GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class); Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index cf750958ee..5c2d4a060c 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -1139,6 +1139,7 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant(bool p_is_static) { ConstantNode *constant = alloc_node<ConstantNode>(); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) { + complete_extents(constant); return nullptr; } @@ -1536,6 +1537,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, suite->parent_function = current_function; current_suite = suite; + if (!p_for_lambda && suite->parent_block != nullptr && suite->parent_block->is_in_loop) { + // Do not reset to false if true is set before calling parse_suite(). + suite->is_in_loop = true; + } + bool multiline = false; if (match(GDScriptTokenizer::Token::NEWLINE)) { @@ -1871,9 +1877,8 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { } suite->add_local(SuiteNode::Local(n_for->variable, current_function)); } - + suite->is_in_loop = true; n_for->loop = parse_suite(R"("for" block)", suite); - n_for->loop->is_loop = true; complete_extents(n_for); // Reset break/continue state. @@ -2143,6 +2148,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_ ExpressionNode *expression = parse_expression(false); if (expression == nullptr) { push_error(R"(Expected expression for match pattern.)"); + complete_extents(pattern); return nullptr; } else { if (expression->type == GDScriptParser::Node::LITERAL) { @@ -2186,8 +2192,9 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() { can_break = true; can_continue = true; - n_while->loop = parse_suite(R"("while" block)"); - n_while->loop->is_loop = true; + SuiteNode *suite = alloc_node<SuiteNode>(); + suite->is_in_loop = true; + n_while->loop = parse_suite(R"("while" block)", suite); complete_extents(n_while); // Reset break/continue state. @@ -2231,7 +2238,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign); while (p_precedence <= get_rule(current.type)->precedence) { - if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) { + if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) { return previous_operand; } // Also switch multiline mode on here for infix operators. @@ -3224,7 +3231,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_type_test(ExpressionNode * } GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) { - push_error(R"("yield" was removed in Godot 4.0. Use "await" instead.)"); + push_error(R"("yield" was removed in Godot 4. Use "await" instead.)"); return nullptr; } diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index ad08c3bfd6..18757eb9fd 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1121,7 +1121,7 @@ public: bool has_return = false; bool has_continue = false; bool has_unreachable_code = false; // Just so warnings aren't given more than once per block. - bool is_loop = false; + bool is_in_loop = false; // The block is nested in a loop (directly or indirectly). bool has_local(const StringName &p_name) const; const Local &get_local(const StringName &p_name) const; diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index e23bd50b8b..605e82be6e 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -98,7 +98,7 @@ public: return; } - virtual String _get_name() const override { return "GDScript"; } + virtual String get_name() const override { return "GDScript"; } }; static void _editor_init() { diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd index 38c2faa859..8709b89b54 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd @@ -1,2 +1,2 @@ func test(): - CanvasItem.new() + InstancePlaceholder.new() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out index 9eff912b59..36224c6b6f 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Native class "CanvasItem" cannot be constructed as it is abstract. +Native class "InstancePlaceholder" cannot be constructed as it is abstract. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd index 118e7e8a45..be67182efb 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd +++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd @@ -1,4 +1,4 @@ -class A extends CanvasItem: +class A extends InstancePlaceholder: func _init(): print('no') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out index 8b956f5974..260f062555 100644 --- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out +++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out @@ -1,2 +1,2 @@ GDTEST_ANALYZER_ERROR -Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem". +Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "InstancePlaceholder". diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd deleted file mode 100644 index c34d927035..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd +++ /dev/null @@ -1,2 +0,0 @@ -func test(): - _get_property_list() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out deleted file mode 100644 index ce2f49a5e5..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Cannot call virtual function "_get_property_list()" because it hasn't been defined. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd deleted file mode 100644 index 57dfffdbee..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd +++ /dev/null @@ -1,5 +0,0 @@ -func _init(): - super() - -func test(): - pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out deleted file mode 100644 index e68759223c..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out +++ /dev/null @@ -1,2 +0,0 @@ -GDTEST_ANALYZER_ERROR -Cannot call the parent class' virtual function "_init()" because it hasn't been defined. diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd deleted file mode 100644 index 1aacd1d11c..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd +++ /dev/null @@ -1,11 +0,0 @@ -class TestOne: - func _get_property_list(): - return {} - -class TestTwo extends TestOne: - func _init(): - var _x = _get_property_list() - -func test(): - var x = TestTwo.new() - var _x = x._get_property_list() diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd deleted file mode 100644 index c447003619..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd +++ /dev/null @@ -1,10 +0,0 @@ -class TestOne: - func _init(): - pass - -class TestTwo extends TestOne: - func _init(): - super() - -func test(): - pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out b/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out deleted file mode 100644 index d73c5eb7cd..0000000000 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out +++ /dev/null @@ -1 +0,0 @@ -GDTEST_OK diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd new file mode 100644 index 0000000000..8c5fb46109 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd @@ -0,0 +1,6 @@ +# https://github.com/godotengine/godot/issues/73273 + +func not_called(): + var v + v=func(): v=1 + in v diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out new file mode 100644 index 0000000000..539240f790 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Expected statement, found "in" instead. diff --git a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out index 36cb699e92..f68d76d101 100644 --- a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out +++ b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out @@ -1,2 +1,2 @@ GDTEST_PARSER_ERROR -"yield" was removed in Godot 4.0. Use "await" instead. +"yield" was removed in Godot 4. Use "await" instead. diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd new file mode 100644 index 0000000000..df6001c7e2 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd @@ -0,0 +1,59 @@ +# https://github.com/godotengine/godot/issues/73273 + +func other(callable : Callable): + callable.call() + +func four_parameters(_a, callable : Callable, b=func(): print(10)): + callable.call() + b.call() + +func test(): + var v + v=func():v=1 + if true: v=1 + print(v) + print() + + v=func(): print(2) if false else print(3) + @warning_ignore("unsafe_cast") + (v as Callable).call() + print() + + v=func(): + print(4) + print(5) + @warning_ignore("unsafe_cast") + if true: (v as Callable).call() + print() + + other(v) + print() + + other(func(): print(6)) + print() + + other(func(): + print(7) + print(8) + ) + print() + + four_parameters(1,func():print(9)) + four_parameters(1,func():print(9), func(): print(11)) + four_parameters(1,func(): + print(12) + print(13) + , func(): print(11)) + print() + + from_ticket() + +func from_ticket(): + var _v + if true: _v = (func(): test()) + if true: _v = (func(): test()) + if true: _v = (func(): test()) + + if true: _v = func(): test() + if true: _v = func(): test() + print(14) diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out new file mode 100644 index 0000000000..4347310960 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out @@ -0,0 +1,25 @@ +GDTEST_OK +1 + +3 + +4 +5 + +4 +5 + +6 + +7 +8 + +9 +10 +9 +11 +12 +13 +11 + +14 diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd new file mode 100644 index 0000000000..c774ebf83c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd @@ -0,0 +1,10 @@ +# GH-77666 + +func test(): + var ref := RefCounted.new() + print(ref.get_reference_count()) + + if true: + var _temp := ref + + print(ref.get_reference_count()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.out index d73c5eb7cd..04b4638adf 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out +++ b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.out @@ -1 +1,3 @@ GDTEST_OK +1 +1 diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd new file mode 100644 index 0000000000..c45f8dce48 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd @@ -0,0 +1,28 @@ +# GH-56223, GH-76569 + +func test(): + for i in 3: + var a + if true: + var b + if true: + var c + prints("Begin:", i, a, b, c) + a = 1 + b = 1 + c = 1 + prints("End:", i, a, b, c) + print("===") + var j := 0 + while j < 3: + var a + if true: + var b + if true: + var c + prints("Begin:", j, a, b, c) + a = 1 + b = 1 + c = 1 + prints("End:", j, a, b, c) + j += 1 diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out new file mode 100644 index 0000000000..7eddcbf903 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out @@ -0,0 +1,14 @@ +GDTEST_OK +Begin: 0 <null> <null> <null> +End: 0 1 1 1 +Begin: 1 <null> <null> <null> +End: 1 1 1 1 +Begin: 2 <null> <null> <null> +End: 2 1 1 1 +=== +Begin: 0 <null> <null> <null> +End: 0 1 1 1 +Begin: 1 <null> <null> <null> +End: 1 1 1 1 +Begin: 2 <null> <null> <null> +End: 2 1 1 1 diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml index 62263eaea6..24f6dbd887 100644 --- a/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterBlend.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneFormatImporterBlend" inherits="EditorSceneFormatImporter" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="EditorSceneFormatImporterBlend" inherits="EditorSceneFormatImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Importer for Blender's [code].blend[/code] scene file format. </brief_description> diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml index 7e609d8c7d..b33735f4ad 100644 --- a/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterFBX.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneFormatImporterFBX" inherits="EditorSceneFormatImporter" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="EditorSceneFormatImporterFBX" inherits="EditorSceneFormatImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Importer for the [code].fbx[/code] scene file format. </brief_description> diff --git a/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml index 80932fad59..2293e75a3c 100644 --- a/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml +++ b/modules/gltf/doc_classes/EditorSceneFormatImporterGLTF.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="EditorSceneFormatImporterGLTF" inherits="EditorSceneFormatImporter" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="EditorSceneFormatImporterGLTF" inherits="EditorSceneFormatImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml index e4e4b79d6e..8e4a72e6ae 100644 --- a/modules/gltf/doc_classes/GLTFAccessor.xml +++ b/modules/gltf/doc_classes/GLTFAccessor.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFAccessor" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFAccessor" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml index df7fa50a93..c1fe85c1c0 100644 --- a/modules/gltf/doc_classes/GLTFAnimation.xml +++ b/modules/gltf/doc_classes/GLTFAnimation.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFAnimation" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFAnimation" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml index 0acf8942ac..ce19650b5b 100644 --- a/modules/gltf/doc_classes/GLTFBufferView.xml +++ b/modules/gltf/doc_classes/GLTFBufferView.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFBufferView" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFBufferView" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml index e34e5d028c..c2ed4d08e3 100644 --- a/modules/gltf/doc_classes/GLTFCamera.xml +++ b/modules/gltf/doc_classes/GLTFCamera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFCamera" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFCamera" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents a GLTF camera. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml index a1cdbdea25..5bc6081803 100644 --- a/modules/gltf/doc_classes/GLTFDocument.xml +++ b/modules/gltf/doc_classes/GLTFDocument.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFDocument" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFDocument" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml index 6b8f2aa904..927ffb6aae 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFDocumentExtension" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFDocumentExtension" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> [GLTFDocument] extension class. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml index d54d675e15..3ca0359311 100644 --- a/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml +++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFDocumentExtensionConvertImporterMesh" inherits="GLTFDocumentExtension" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFDocumentExtensionConvertImporterMesh" inherits="GLTFDocumentExtension" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml index 9b12d42d63..c55962eeaa 100644 --- a/modules/gltf/doc_classes/GLTFLight.xml +++ b/modules/gltf/doc_classes/GLTFLight.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFLight" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFLight" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents a GLTF light. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml index aa8e8448f7..df4d436f5c 100644 --- a/modules/gltf/doc_classes/GLTFMesh.xml +++ b/modules/gltf/doc_classes/GLTFMesh.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFMesh" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml index 853af99257..2ec39801f3 100644 --- a/modules/gltf/doc_classes/GLTFNode.xml +++ b/modules/gltf/doc_classes/GLTFNode.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFNode" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> GLTF node class. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml index 5d21deff05..3aab1c5183 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFPhysicsBody" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFPhysicsBody" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents a GLTF physics body. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml index 4b673d6001..2891fab115 100644 --- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml +++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFPhysicsShape" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFPhysicsShape" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents a GLTF physics shape. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml index 8073db3ce9..c7b8cb2ac3 100644 --- a/modules/gltf/doc_classes/GLTFSkeleton.xml +++ b/modules/gltf/doc_classes/GLTFSkeleton.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFSkeleton" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFSkeleton" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml index 3d5d8000ab..98436ea2e7 100644 --- a/modules/gltf/doc_classes/GLTFSkin.xml +++ b/modules/gltf/doc_classes/GLTFSkin.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFSkin" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFSkin" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml index b153444c84..d64a918920 100644 --- a/modules/gltf/doc_classes/GLTFSpecGloss.xml +++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFSpecGloss" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFSpecGloss" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Archived GLTF extension for specular/glossy materials. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml index 7614839b8b..32023de696 100644 --- a/modules/gltf/doc_classes/GLTFState.xml +++ b/modules/gltf/doc_classes/GLTFState.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFState" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFState" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents all data of a GLTF file. </brief_description> diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml index 768e7a8945..41c20439ea 100644 --- a/modules/gltf/doc_classes/GLTFTexture.xml +++ b/modules/gltf/doc_classes/GLTFTexture.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFTexture" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml index a24c2f0f84..027d35e9d2 100644 --- a/modules/gltf/doc_classes/GLTFTextureSampler.xml +++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GLTFTextureSampler" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GLTFTextureSampler" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Represents a GLTF texture sampler </brief_description> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index d77d9c6f66..d828363e03 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -49,6 +49,8 @@ #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/multimesh_instance_3d.h" +#include "scene/resources/image_texture.h" +#include "scene/resources/portable_compressed_texture.h" #include "scene/resources/skin.h" #include "scene/resources/surface_tool.h" @@ -3769,6 +3771,12 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> p_state) { extensions["KHR_materials_unlit"] = mat_unlit; p_state->add_used_extension("KHR_materials_unlit"); } + if (base_material->get_feature(BaseMaterial3D::FEATURE_EMISSION) && !Math::is_equal_approx(base_material->get_emission_energy_multiplier(), 1.0f)) { + Dictionary mat_emissive_strength; + mat_emissive_strength["emissiveStrength"] = base_material->get_emission_energy_multiplier(); + extensions["KHR_materials_emissive_strength"] = mat_emissive_strength; + p_state->add_used_extension("KHR_materials_emissive_strength"); + } d["extensions"] = extensions; materials.push_back(d); @@ -3789,28 +3797,35 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { const Array &materials = p_state->json["materials"]; for (GLTFMaterialIndex i = 0; i < materials.size(); i++) { - const Dictionary &d = materials[i]; + const Dictionary &material_dict = materials[i]; Ref<StandardMaterial3D> material; material.instantiate(); - if (d.has("name") && !String(d["name"]).is_empty()) { - material->set_name(d["name"]); + if (material_dict.has("name") && !String(material_dict["name"]).is_empty()) { + material->set_name(material_dict["name"]); } else { material->set_name(vformat("material_%s", itos(i))); } material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - Dictionary pbr_spec_gloss_extensions; - if (d.has("extensions")) { - pbr_spec_gloss_extensions = d["extensions"]; + Dictionary material_extensions; + if (material_dict.has("extensions")) { + material_extensions = material_dict["extensions"]; } - if (pbr_spec_gloss_extensions.has("KHR_materials_unlit")) { + if (material_extensions.has("KHR_materials_unlit")) { material->set_shading_mode(BaseMaterial3D::SHADING_MODE_UNSHADED); } - if (pbr_spec_gloss_extensions.has("KHR_materials_pbrSpecularGlossiness")) { + if (material_extensions.has("KHR_materials_emissive_strength")) { + Dictionary emissive_strength = material_extensions["KHR_materials_emissive_strength"]; + if (emissive_strength.has("emissiveStrength")) { + material->set_emission_energy_multiplier(emissive_strength["emissiveStrength"]); + } + } + + if (material_extensions.has("KHR_materials_pbrSpecularGlossiness")) { WARN_PRINT("Material uses a specular and glossiness workflow. Textures will be converted to roughness and metallic workflow, which may not be 100% accurate."); - Dictionary sgm = pbr_spec_gloss_extensions["KHR_materials_pbrSpecularGlossiness"]; + Dictionary sgm = material_extensions["KHR_materials_pbrSpecularGlossiness"]; Ref<GLTFSpecGloss> spec_gloss; spec_gloss.instantiate(); @@ -3858,8 +3873,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { } spec_gloss_to_rough_metal(spec_gloss, material); - } else if (d.has("pbrMetallicRoughness")) { - const Dictionary &mr = d["pbrMetallicRoughness"]; + } else if (material_dict.has("pbrMetallicRoughness")) { + const Dictionary &mr = material_dict["pbrMetallicRoughness"]; if (mr.has("baseColorFactor")) { const Array &arr = mr["baseColorFactor"]; ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR); @@ -3911,8 +3926,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { } } - if (d.has("normalTexture")) { - const Dictionary &bct = d["normalTexture"]; + if (material_dict.has("normalTexture")) { + const Dictionary &bct = material_dict["normalTexture"]; if (bct.has("index")) { material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(p_state, bct["index"], TEXTURE_TYPE_NORMAL)); material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true); @@ -3921,8 +3936,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { material->set_normal_scale(bct["scale"]); } } - if (d.has("occlusionTexture")) { - const Dictionary &bct = d["occlusionTexture"]; + if (material_dict.has("occlusionTexture")) { + const Dictionary &bct = material_dict["occlusionTexture"]; if (bct.has("index")) { material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED); @@ -3930,8 +3945,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { } } - if (d.has("emissiveFactor")) { - const Array &arr = d["emissiveFactor"]; + if (material_dict.has("emissiveFactor")) { + const Array &arr = material_dict["emissiveFactor"]; ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); const Color c = Color(arr[0], arr[1], arr[2]).linear_to_srgb(); material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); @@ -3939,8 +3954,8 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { material->set_emission(c); } - if (d.has("emissiveTexture")) { - const Dictionary &bct = d["emissiveTexture"]; + if (material_dict.has("emissiveTexture")) { + const Dictionary &bct = material_dict["emissiveTexture"]; if (bct.has("index")) { material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(p_state, bct["index"], TEXTURE_TYPE_GENERIC)); material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true); @@ -3948,20 +3963,20 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> p_state) { } } - if (d.has("doubleSided")) { - const bool ds = d["doubleSided"]; + if (material_dict.has("doubleSided")) { + const bool ds = material_dict["doubleSided"]; if (ds) { material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); } } - if (d.has("alphaMode")) { - const String &am = d["alphaMode"]; + if (material_dict.has("alphaMode")) { + const String &am = material_dict["alphaMode"]; if (am == "BLEND") { material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS); } else if (am == "MASK") { material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR); - if (d.has("alphaCutoff")) { - material->set_alpha_scissor_threshold(d["alphaCutoff"]); + if (material_dict.has("alphaCutoff")) { + material->set_alpha_scissor_threshold(material_dict["alphaCutoff"]); } else { material->set_alpha_scissor_threshold(0.5f); } @@ -6308,29 +6323,21 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> p_state) { node->rotation = mi_xform.basis.get_rotation_quaternion(); node->position = mi_xform.origin; - Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(mi->get_node(mi->get_skeleton_path())); - if (!skeleton) { - continue; - } - if (!skeleton->get_bone_count()) { + Node *skel_node = mi->get_node_or_null(mi->get_skeleton_path()); + Skeleton3D *godot_skeleton = Object::cast_to<Skeleton3D>(skel_node); + if (!godot_skeleton || godot_skeleton->get_bone_count() == 0) { continue; } + // At this point in the code, we know we have a Skeleton3D with at least one bone. Ref<Skin> skin = mi->get_skin(); Ref<GLTFSkin> gltf_skin; gltf_skin.instantiate(); Array json_joints; - - NodePath skeleton_path = mi->get_skeleton_path(); - Node *skel_node = mi->get_node_or_null(skeleton_path); - Skeleton3D *godot_skeleton = nullptr; - if (skel_node != nullptr) { - godot_skeleton = cast_to<Skeleton3D>(skel_node); - } - if (godot_skeleton != nullptr && p_state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) { + if (p_state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) { // This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible. const GLTFSkeletonIndex skeleton_gltf_i = p_state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()]; Ref<GLTFSkeleton> gltf_skeleton = p_state->skeletons[skeleton_gltf_i]; - int bone_cnt = skeleton->get_bone_count(); + int bone_cnt = godot_skeleton->get_bone_count(); ERR_FAIL_COND(bone_cnt != gltf_skeleton->joints.size()); ObjectID gltf_skin_key; @@ -6348,7 +6355,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> p_state) { } else { if (skin.is_null()) { // Note that gltf_skin_key should remain null, so these can share a reference. - skin = skeleton->create_skin_from_rest_transforms(); + skin = godot_skeleton->create_skin_from_rest_transforms(); } gltf_skin.instantiate(); gltf_skin->godot_skin = skin; @@ -6358,7 +6365,7 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> p_state) { //gltf_state->godot_to_gltf_node[skel_node] HashMap<StringName, int> bone_name_to_idx; for (int bone_i = 0; bone_i < bone_cnt; bone_i++) { - bone_name_to_idx[skeleton->get_bone_name(bone_i)] = bone_i; + bone_name_to_idx[godot_skeleton->get_bone_name(bone_i)] = bone_i; } for (int bind_i = 0, cnt = skin->get_bind_count(); bind_i < cnt; bind_i++) { int bone_i = skin->get_bind_bone(bind_i); @@ -6369,13 +6376,13 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> p_state) { } ERR_CONTINUE(bone_i < 0 || bone_i >= bone_cnt); if (bind_name == StringName()) { - bind_name = skeleton->get_bone_name(bone_i); + bind_name = godot_skeleton->get_bone_name(bone_i); } GLTFNodeIndex skeleton_bone_i = gltf_skeleton->joints[bone_i]; gltf_skin->joints_original.push_back(skeleton_bone_i); gltf_skin->joints.push_back(skeleton_bone_i); gltf_skin->inverse_binds.push_back(bind_pose); - if (skeleton->get_bone_parent(bone_i) == -1) { + if (godot_skeleton->get_bone_parent(bone_i) == -1) { gltf_skin->roots.push_back(skeleton_bone_i); } gltf_skin->joint_i_to_bone_i[bind_i] = bone_i; @@ -7469,6 +7476,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) { supported_extensions.insert("KHR_materials_pbrSpecularGlossiness"); supported_extensions.insert("KHR_texture_transform"); supported_extensions.insert("KHR_materials_unlit"); + supported_extensions.insert("KHR_materials_emissive_strength"); for (Ref<GLTFDocumentExtension> ext : document_extensions) { ERR_CONTINUE(ext.is_null()); Vector<String> ext_supported_extensions = ext->get_supported_extensions(); diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index e413560c16..f9c3ca476a 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GridMap" inherits="Node3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GridMap" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Node for 3D tile-based maps. </brief_description> @@ -138,11 +138,11 @@ Returns the position of a grid cell in the GridMap's local coordinate space. To convert the returned value into global coordinates, use [method Node3D.to_global]. See also [method map_to_local]. </description> </method> - <method name="resource_changed"> + <method name="resource_changed" is_deprecated="true"> <return type="void" /> <param index="0" name="resource" type="Resource" /> <description> - Notifies the [GridMap] about changed resource and recreates octant data. + [i]Obsoleted.[/i] Use [signal Resource.changed] instead. </description> </method> <method name="set_cell_item"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index c77fa98be2..f1e2218434 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -258,11 +258,11 @@ RID GridMap::get_navigation_map() const { void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) { if (!mesh_library.is_null()) { - mesh_library->unregister_owner(this); + mesh_library->disconnect_changed(callable_mp(this, &GridMap::_recreate_octant_data)); } mesh_library = p_mesh_library; if (!mesh_library.is_null()) { - mesh_library->register_owner(this); + mesh_library->connect_changed(callable_mp(this, &GridMap::_recreate_octant_data)); } _recreate_octant_data(); @@ -1005,9 +1005,10 @@ void GridMap::clear() { clear_baked_meshes(); } +#ifndef DISABLE_DEPRECATED void GridMap::resource_changed(const Ref<Resource> &p_res) { - _recreate_octant_data(); } +#endif void GridMap::_update_octants_callback() { if (!awaiting_update) { @@ -1079,7 +1080,9 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("map_to_local", "map_position"), &GridMap::map_to_local); ClassDB::bind_method(D_METHOD("_update_octants_callback"), &GridMap::_update_octants_callback); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("resource_changed", "resource"), &GridMap::resource_changed); +#endif ClassDB::bind_method(D_METHOD("set_center_x", "enable"), &GridMap::set_center_x); ClassDB::bind_method(D_METHOD("get_center_x"), &GridMap::get_center_x); @@ -1336,10 +1339,6 @@ void GridMap::_navigation_map_changed(RID p_map) { #endif // DEBUG_ENABLED GridMap::~GridMap() { - if (!mesh_library.is_null()) { - mesh_library->unregister_owner(this); - } - clear(); #ifdef DEBUG_ENABLED NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed)); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 18c3f90269..e05979efbc 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -203,7 +203,9 @@ class GridMap : public Node3D { void _queue_octants_dirty(); void _update_octants_callback(); +#ifndef DISABLE_DEPRECATED void resource_changed(const Ref<Resource> &p_res); +#endif void _clear_internal(); diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index 50a6909d1a..47c0dc9bb6 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -139,11 +139,11 @@ X509Certificate *X509CertificateMbedTLS::create() { } Error X509CertificateMbedTLS::load(String p_path) { - ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use"); + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is already in use."); PackedByteArray out; Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, "Cannot open X509CertificateMbedTLS file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, vformat("Cannot open X509CertificateMbedTLS file '%s'.", p_path)); uint64_t flen = f->get_length(); out.resize(flen + 1); @@ -151,22 +151,28 @@ Error X509CertificateMbedTLS::load(String p_path) { out.write[flen] = 0; // string terminator int ret = mbedtls_x509_crt_parse(&cert, out.ptr(), out.size()); - ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing some certificates: " + itos(ret)); + ERR_FAIL_COND_V_MSG(ret < 0, FAILED, vformat("Error parsing X509 certificates from file '%s': %d.", p_path, ret)); + if (ret > 0) { // Some certs parsed fine, don't error. + print_verbose(vformat("MbedTLS: Some X509 certificates could not be parsed from file '%s' (%d certificates skipped).", p_path, ret)); + } return OK; } Error X509CertificateMbedTLS::load_from_memory(const uint8_t *p_buffer, int p_len) { - ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use"); + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is already in use."); int ret = mbedtls_x509_crt_parse(&cert, p_buffer, p_len); - ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing certificates: " + itos(ret)); + ERR_FAIL_COND_V_MSG(ret < 0, FAILED, vformat("Error parsing X509 certificates: %d.", ret)); + if (ret > 0) { // Some certs parsed fine, don't error. + print_verbose(vformat("MbedTLS: Some X509 certificates could not be parsed (%d certificates skipped).", ret)); + } return OK; } Error X509CertificateMbedTLS::save(String p_path) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, "Cannot save X509CertificateMbedTLS file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_INVALID_PARAMETER, vformat("Cannot save X509CertificateMbedTLS file '%s'.", p_path)); mbedtls_x509_crt *crt = &cert; while (crt) { @@ -203,11 +209,14 @@ String X509CertificateMbedTLS::save_to_string() { } Error X509CertificateMbedTLS::load_from_string(const String &p_string_key) { - ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is in use"); + ERR_FAIL_COND_V_MSG(locks, ERR_ALREADY_IN_USE, "Certificate is already in use."); CharString cs = p_string_key.utf8(); int ret = mbedtls_x509_crt_parse(&cert, (const unsigned char *)cs.get_data(), cs.size()); - ERR_FAIL_COND_V_MSG(ret, FAILED, "Error parsing some certificates: " + itos(ret)); + ERR_FAIL_COND_V_MSG(ret < 0, FAILED, vformat("Error parsing X509 certificates: %d.", ret)); + if (ret > 0) { // Some certs parsed fine, don't error. + print_verbose(vformat("MbedTLS: Some X509 certificates could not be parsed (%d certificates skipped).", ret)); + } return OK; } diff --git a/modules/minimp3/config.py b/modules/minimp3/config.py index bd35d099b9..e6bdcb2a83 100644 --- a/modules/minimp3/config.py +++ b/modules/minimp3/config.py @@ -9,6 +9,7 @@ def configure(env): def get_doc_classes(): return [ "AudioStreamMP3", + "ResourceImporterMP3", ] diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml index 3ece7ce15e..14676a4545 100644 --- a/modules/minimp3/doc_classes/AudioStreamMP3.xml +++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamMP3" inherits="AudioStream" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="AudioStreamMP3" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> MP3 audio stream driver. </brief_description> diff --git a/modules/minimp3/doc_classes/ResourceImporterMP3.xml b/modules/minimp3/doc_classes/ResourceImporterMP3.xml new file mode 100644 index 0000000000..7283577f36 --- /dev/null +++ b/modules/minimp3/doc_classes/ResourceImporterMP3.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ResourceImporterMP3" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <members> + <member name="bar_beats" type="int" setter="" getter="" default="4"> + </member> + <member name="beat_count" type="int" setter="" getter="" default="0"> + </member> + <member name="bpm" type="float" setter="" getter="" default="0"> + </member> + <member name="loop" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the audio will play again from the specified [member loop_offset] once it is done playing. Useful for ambient sounds and background music. + </member> + <member name="loop_offset" type="float" setter="" getter="" default="0"> + </member> + </members> +</class> diff --git a/modules/minimp3/register_types.cpp b/modules/minimp3/register_types.cpp index da89321018..627d093bc1 100644 --- a/modules/minimp3/register_types.cpp +++ b/modules/minimp3/register_types.cpp @@ -51,7 +51,11 @@ void initialize_minimp3_module(ModuleInitializationLevel p_level) { mp3_import.instantiate(); ResourceFormatImporter::get_singleton()->add_importer(mp3_import); } + + // Required to document import options in the class reference. + GDREGISTER_CLASS(ResourceImporterMP3); #endif + GDREGISTER_CLASS(AudioStreamMP3); } diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index ecd07d4520..1be8cc828d 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MobileVRInterface" inherits="XRInterface" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="MobileVRInterface" inherits="XRInterface" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Generic mobile VR implementation. </brief_description> diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index 0b91cda9b8..580f51c973 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -286,15 +286,29 @@ def generate_sdk_package_versions(): version_status = version_status[:pos] + "." + version_status[pos:] version_str += "-" + version_status + import version + + version_defines = ( + [ + f"GODOT{version.major}", + f"GODOT{version.major}_{version.minor}", + f"GODOT{version.major}_{version.minor}_{version.patch}", + ] + + [f"GODOT{v}_OR_GREATER" for v in range(4, version.major + 1)] + + [f"GODOT{version.major}_{v}_OR_GREATER" for v in range(0, version.minor + 1)] + + [f"GODOT{version.major}_{version.minor}_{v}_OR_GREATER" for v in range(0, version.patch + 1)] + ) + props = """<Project> <PropertyGroup> <PackageVersion_GodotSharp>{0}</PackageVersion_GodotSharp> <PackageVersion_Godot_NET_Sdk>{0}</PackageVersion_Godot_NET_Sdk> <PackageVersion_Godot_SourceGenerators>{0}</PackageVersion_Godot_SourceGenerators> + <GodotVersionConstants>{1}</GodotVersionConstants> </PropertyGroup> </Project> """.format( - version_str + version_str, ";".join(version_defines) ) # We write in ../SdkPackageVersions.props. diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index bc26352e9c..1ed495943f 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -120,6 +120,7 @@ void CSharpLanguage::init() { GLOBAL_DEF("dotnet/project/assembly_name", ""); #ifdef TOOLS_ENABLED GLOBAL_DEF("dotnet/project/solution_directory", ""); + GLOBAL_DEF(PropertyInfo(Variant::INT, "dotnet/project/assembly_reload_attempts", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), 3); #endif gdmono = memnew(GDMono); @@ -770,10 +771,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { return; } - // TODO: - // Currently, this reloads all scripts, including those whose class is not part of the - // assembly load context being unloaded. As such, we unnecessarily reload GodotTools. - print_verbose(".NET: Reloading assemblies..."); // There is no soft reloading with Mono. It's always hard reloading. @@ -784,8 +781,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { MutexLock lock(script_instances_mutex); for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) { - // Cast to CSharpScript to avoid being erased by accident - scripts.push_back(Ref<CSharpScript>(elem->self())); + // Do not reload scripts with only non-collectible instances to avoid disrupting event subscriptions and such. + bool is_reloadable = elem->self()->instances.size() == 0; + for (Object *obj : elem->self()->instances) { + ERR_CONTINUE(!obj->get_script_instance()); + CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance()); + if (GDMonoCache::managed_callbacks.GCHandleBridge_GCHandleIsTargetCollectible(csi->get_gchandle_intptr())) { + is_reloadable = true; + break; + } + } + if (is_reloadable) { + // Cast to CSharpScript to avoid being erased by accident. + scripts.push_back(Ref<CSharpScript>(elem->self())); + } } } @@ -800,6 +809,10 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { ERR_CONTINUE(managed_callable->delegate_handle.value == nullptr); + if (!GDMonoCache::managed_callbacks.GCHandleBridge_GCHandleIsTargetCollectible(managed_callable->delegate_handle)) { + continue; + } + Array serialized_data; bool success = GDMonoCache::managed_callbacks.DelegateUtils_TrySerializeDelegateWithGCHandle( @@ -907,6 +920,15 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) { scr->_clear(); } + // Release the delegates that were serialized earlier. + { + MutexLock lock(ManagedCallable::instances_mutex); + + for (KeyValue<ManagedCallable *, Array> &kv : ManagedCallable::instances_pending_reload) { + kv.key->release_delegate_handle(); + } + } + // Do domain reload if (gdmono->reload_project_assemblies() != OK) { // Failed to reload the scripts domain @@ -1158,19 +1180,6 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) { } } -void CSharpLanguage::_on_scripts_domain_about_to_unload() { -#ifdef GD_MONO_HOT_RELOAD - { - MutexLock lock(ManagedCallable::instances_mutex); - - for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) { - ManagedCallable *managed_callable = elem->self(); - managed_callable->release_delegate_handle(); - } - } -#endif -} - #ifdef TOOLS_ENABLED void CSharpLanguage::_editor_init_callback() { // Load GodotTools and initialize GodotSharpEditor @@ -2263,7 +2272,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) { // If the EditorFileSystem singleton is available, update the file; // otherwise, the file will be updated when the singleton becomes available. EditorFileSystem *efs = EditorFileSystem::get_singleton(); - if (efs) { + if (efs && !p_script->get_path().is_empty()) { efs->update_file(p_script->get_path()); } #endif diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index cfdf8ae6f8..9802067b46 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -347,7 +347,6 @@ class CSharpLanguage : public ScriptLanguage { String _debug_error; friend class GDMono; - void _on_scripts_domain_about_to_unload(); #ifdef TOOLS_ENABLED EditorPlugin *godotsharp_editor = nullptr; diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml index e8da9d8465..b559ca20b2 100644 --- a/modules/mono/doc_classes/CSharpScript.xml +++ b/modules/mono/doc_classes/CSharpScript.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="CSharpScript" inherits="Script" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="CSharpScript" inherits="Script" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - A script implemented in the C# programming language (Mono-enabled builds only). + A script implemented in the C# programming language, saved with the [code].cs[/code] extension (Mono-enabled builds only). </brief_description> <description> This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds. diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml index 76eefc4925..969ca14350 100644 --- a/modules/mono/doc_classes/GodotSharp.xml +++ b/modules/mono/doc_classes/GodotSharp.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="GodotSharp" inherits="Object" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GodotSharp" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Bridge between Godot and the Mono runtime (Mono-enabled builds only). </brief_description> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj index e8ad6a77ea..663eb14f07 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj @@ -7,7 +7,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>Godot.NET.Sdk</PackageId> - <Version>4.1.0</Version> + <Version>4.2.0</Version> <PackageVersion>$(PackageVersion_Godot_NET_Sdk)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props index 45f930fdf7..b0bee795f8 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props @@ -74,15 +74,8 @@ <!-- Godot DefineConstants. --> <PropertyGroup> - <!-- Define constants to identify Godot builds and versions. --> - <GodotDefineConstants> - GODOT; - GODOT4;GODOT4_OR_GREATER; - GODOT4_1;GODOT4_1_OR_GREATER;GODOT4_0_OR_GREATER; - GODOT4_1_0;GODOT4_1_0_OR_GREATER; - </GodotDefineConstants> - <!-- Ensure the define constants don't contain whitespace (see https://github.com/dotnet/roslyn/issues/58391). --> - <GodotDefineConstants>$(GodotDefineConstants.Replace('%0A','').Replace('%0D','').Replace('%09','').Replace(' ',''))</GodotDefineConstants> + <!-- Define constants to identify Godot builds. --> + <GodotDefineConstants>GODOT</GodotDefineConstants> <!-- Define constant to determine the target Godot platform. This includes the @@ -97,7 +90,7 @@ <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants> <GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'web' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants> - <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants> + <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants);$(GodotVersionConstants)</GodotDefineConstants> </PropertyGroup> <PropertyGroup> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs index 8be1151142..72614dd7e0 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs @@ -384,5 +384,65 @@ namespace Godot.SourceGenerators typeArgumentSyntax.GetLocation(), typeArgumentSyntax.SyntaxTree.FilePath)); } + + public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule = + new DiagnosticDescriptor(id: "GD0401", + title: "The class must derive from GodotObject or a derived class", + messageFormat: "The class '{0}' must derive from GodotObject or a derived class.", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute."); + + public static void ReportGlobalClassMustDeriveFromGodotObject( + SyntaxNodeAnalysisContext context, + SyntaxNode classSyntax, + ISymbol typeSymbol) + { + string message = $"The class '{typeSymbol.ToDisplayString()}' must derive from GodotObject or a derived class"; + + string description = $"{message}. Change the base class or remove the '[GlobalClass]' attribute."; + + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor(id: "GD0401", + title: message, + messageFormat: message, + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description), + classSyntax.GetLocation(), + classSyntax.SyntaxTree.FilePath)); + } + + public static readonly DiagnosticDescriptor GlobalClassMustNotBeGenericRule = + new DiagnosticDescriptor(id: "GD0402", + title: "The class must not contain generic arguments", + messageFormat: "The class '{0}' must not contain generic arguments", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute."); + + public static void ReportGlobalClassMustNotBeGeneric( + SyntaxNodeAnalysisContext context, + SyntaxNode classSyntax, + ISymbol typeSymbol) + { + string message = $"The class '{typeSymbol.ToDisplayString()}' must not contain generic arguments"; + + string description = $"{message}. Remove the generic arguments or the '[GlobalClass]' attribute."; + + context.ReportDiagnostic(Diagnostic.Create( + new DiagnosticDescriptor(id: "GD0402", + title: message, + messageFormat: message, + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description), + classSyntax.GetLocation(), + classSyntax.SyntaxTree.FilePath)); + } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index 38af1cbade..b6ea4b8e88 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -37,7 +37,7 @@ namespace Godot.SourceGenerators while (symbol != null) { if (symbol.ContainingAssembly?.Name == assemblyName && - symbol.ToString() == typeFullName) + symbol.FullQualifiedNameOmitGlobal() == typeFullName) { return true; } @@ -81,7 +81,7 @@ namespace Godot.SourceGenerators return godotClassName ?? nativeType.Name; } - private static bool IsGodotScriptClass( + private static bool TryGetGodotScriptClass( this ClassDeclarationSyntax cds, Compilation compilation, out INamedTypeSymbol? symbol ) @@ -108,7 +108,7 @@ namespace Godot.SourceGenerators { foreach (var cds in source) { - if (cds.IsGodotScriptClass(compilation, out var symbol)) + if (cds.TryGetGodotScriptClass(compilation, out var symbol)) yield return (cds, symbol!); } } @@ -230,22 +230,22 @@ namespace Godot.SourceGenerators .Replace(">", ")"); public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.ExportAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.ExportAttr; public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.SignalAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SignalAttr; public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.MustBeVariantAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.MustBeVariantAttr; public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.GodotClassNameAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GodotClassNameAttr; public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.GlobalClassAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GlobalClassAttr; public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GodotClasses.SystemFlagsAttr; + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SystemFlagsAttr; public static GodotMethodData? HasGodotCompatibleSignature( this IMethodSymbol method, diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs new file mode 100644 index 0000000000..bcb35dae8a --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs @@ -0,0 +1,42 @@ +using System.Collections.Immutable; +using System.Linq; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Godot.SourceGenerators +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class GlobalClassAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics + => ImmutableArray.Create( + Common.GlobalClassMustDeriveFromGodotObjectRule, + Common.GlobalClassMustNotBeGenericRule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration); + } + + private void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + var typeClassDecl = (ClassDeclarationSyntax)context.Node; + + // Return if not a type symbol or the type is not a global class. + if (context.ContainingSymbol is not INamedTypeSymbol typeSymbol || + !typeSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false)) + return; + + if (typeSymbol.IsGenericType) + Common.ReportGlobalClassMustNotBeGeneric(context, typeClassDecl, typeSymbol); + + if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject)) + Common.ReportGlobalClassMustDeriveFromGodotObject(context, typeClassDecl, typeSymbol); + } + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj index 2557b70e75..a03c9bc06c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj @@ -9,7 +9,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>Godot.SourceGenerators</PackageId> - <Version>4.1.0</Version> + <Version>4.2.0</Version> <PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index e856ad5c13..09a4ab538f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -362,7 +362,7 @@ namespace Godot.SourceGenerators { foreach (var attr in memberSymbol.GetAttributes()) { - PropertyUsageFlags? propertyUsage = attr.AttributeClass?.ToString() switch + PropertyUsageFlags? propertyUsage = attr.AttributeClass?.FullQualifiedNameOmitGlobal() switch { GodotClasses.ExportCategoryAttr => PropertyUsageFlags.Category, GodotClasses.ExportGroupAttr => PropertyUsageFlags.Group, @@ -620,7 +620,7 @@ namespace Godot.SourceGenerators bool isPresetHint = false; - if (elementVariantType == VariantType.String) + if (elementVariantType == VariantType.String || elementVariantType == VariantType.StringName) isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString); if (!isPresetHint) diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index 27963be00f..312c65e364 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -206,16 +206,16 @@ namespace GodotTools.Build private static bool BuildProjectBlocking(BuildInfo buildInfo) { - if (!File.Exists(buildInfo.Solution)) - return true; // No solution to build + if (!File.Exists(buildInfo.Project)) + return true; // No project to build. using var pr = new EditorProgress("dotnet_build_project", "Building .NET project...", 1); - pr.Step("Building project solution", 0); + pr.Step("Building project", 0); if (!Build(buildInfo)) { - ShowBuildErrorDialog("Failed to build project solution"); + ShowBuildErrorDialog("Failed to build project"); return false; } @@ -224,16 +224,16 @@ namespace GodotTools.Build private static bool CleanProjectBlocking(BuildInfo buildInfo) { - if (!File.Exists(buildInfo.Solution)) - return true; // No solution to clean + if (!File.Exists(buildInfo.Project)) + return true; // No project to clean. using var pr = new EditorProgress("dotnet_clean_project", "Cleaning .NET project...", 1); - pr.Step("Cleaning project solution", 0); + pr.Step("Cleaning project", 0); if (!Build(buildInfo)) { - ShowBuildErrorDialog("Failed to clean project solution"); + ShowBuildErrorDialog("Failed to clean project"); return false; } @@ -322,11 +322,11 @@ namespace GodotTools.Build public static bool EditorBuildCallback() { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) - return true; // No solution to build + if (!File.Exists(GodotSharpDirs.ProjectCsProjPath)) + return true; // No project to build. if (GodotSharpEditor.Instance.SkipBuildBeforePlaying) - return true; // Requested play from an external editor/IDE which already built the project + return true; // Requested play from an external editor/IDE which already built the project. return BuildProjectBlocking("Debug"); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs index 8fe7d3c2d7..1bb1b3227e 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs @@ -29,46 +29,46 @@ namespace GodotTools.Build BuildOutputView.UpdateIssuesList(); } - public void BuildSolution() + public void BuildProject() { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) - return; // No solution to build + if (!File.Exists(GodotSharpDirs.ProjectCsProjPath)) + return; // No project to build. if (!BuildManager.BuildProjectBlocking("Debug")) - return; // Build failed + return; // Build failed. - // Notify running game for hot-reload + // Notify running game for hot-reload. Internal.EditorDebuggerNodeReloadScripts(); - // Hot-reload in the editor + // Hot-reload in the editor. GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); if (Internal.IsAssembliesReloadingNeeded()) Internal.ReloadAssemblies(softReload: false); } - private void RebuildSolution() + private void RebuildProject() { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) - return; // No solution to build + if (!File.Exists(GodotSharpDirs.ProjectCsProjPath)) + return; // No project to build. if (!BuildManager.BuildProjectBlocking("Debug", rebuild: true)) - return; // Build failed + return; // Build failed. - // Notify running game for hot-reload + // Notify running game for hot-reload. Internal.EditorDebuggerNodeReloadScripts(); - // Hot-reload in the editor + // Hot-reload in the editor. GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer(); if (Internal.IsAssembliesReloadingNeeded()) Internal.ReloadAssemblies(softReload: false); } - private void CleanSolution() + private void CleanProject() { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) - return; // No solution to build + if (!File.Exists(GodotSharpDirs.ProjectCsProjPath)) + return; // No project to build. _ = BuildManager.CleanProjectBlocking("Debug"); } @@ -83,14 +83,14 @@ namespace GodotTools.Build { switch ((BuildMenuOptions)id) { - case BuildMenuOptions.BuildSolution: - BuildSolution(); + case BuildMenuOptions.BuildProject: + BuildProject(); break; - case BuildMenuOptions.RebuildSolution: - RebuildSolution(); + case BuildMenuOptions.RebuildProject: + RebuildProject(); break; - case BuildMenuOptions.CleanSolution: - CleanSolution(); + case BuildMenuOptions.CleanProject: + CleanProject(); break; default: throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option"); @@ -99,9 +99,9 @@ namespace GodotTools.Build private enum BuildMenuOptions { - BuildSolution, - RebuildSolution, - CleanSolution + BuildProject, + RebuildProject, + CleanProject } public override void _Ready() @@ -118,9 +118,9 @@ namespace GodotTools.Build toolBarHBox.AddChild(_buildMenuBtn); var buildMenu = _buildMenuBtn.GetPopup(); - buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution); - buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution); - buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution); + buildMenu.AddItem("Build Project".TTR(), (int)BuildMenuOptions.BuildProject); + buildMenu.AddItem("Rebuild Project".TTR(), (int)BuildMenuOptions.RebuildProject); + buildMenu.AddItem("Clean Project".TTR(), (int)BuildMenuOptions.CleanProject); buildMenu.IdPressed += BuildMenuOptionPressed; _errorsBtn = new Button diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 4e33b38ac2..622a155d37 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -140,15 +140,15 @@ namespace GodotTools } } - private void BuildSolutionPressed() + private void BuildProjectPressed() { - if (!File.Exists(GodotSharpDirs.ProjectSlnPath)) + if (!File.Exists(GodotSharpDirs.ProjectCsProjPath)) { if (!CreateProjectSolution()) - return; // Failed to create solution + return; // Failed to create project. } - Instance.MSBuildPanel.BuildSolution(); + Instance.MSBuildPanel.BuildProject(); } private enum MenuOptions @@ -507,10 +507,10 @@ namespace GodotTools Shortcut = buildSolutionShortcut, ShortcutInTooltip = true }; - _toolBarBuildButton.Pressed += BuildSolutionPressed; + _toolBarBuildButton.Pressed += BuildProjectPressed; AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton); - if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath)) + if (File.Exists(GodotSharpDirs.ProjectCsProjPath)) { ApplyNecessaryChangesToSolution(); } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index 30525ba04a..4a0b7f9bed 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -28,10 +28,9 @@ </PropertyGroup> <ItemGroup> <PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" /> + <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.1" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> - <!-- For RiderPathLocator --> - <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" /> <Reference Include="GodotSharp"> <HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath> <Private>False</Private> diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs index 62db6e3af5..51c7a8aa22 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs @@ -385,9 +385,12 @@ namespace GodotTools.Ides // However, it doesn't fix resource loading if the rest of the path is also case insensitive. string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile); + // The node API can only be called from the main thread. + await Godot.Engine.GetMainLoop().ToSignal(Godot.Engine.GetMainLoop(), "process_frame"); + var response = new CodeCompletionResponse { Kind = request.Kind, ScriptFile = request.ScriptFile }; - response.Suggestions = await Task.Run(() => - Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile)); + response.Suggestions = Internal.CodeCompletionRequest(response.Kind, + scriptFileLocalized ?? request.ScriptFile); return response; } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs new file mode 100644 index 0000000000..7e08d8c01d --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs @@ -0,0 +1,51 @@ +using System; +using Godot; +using JetBrains.Rider.PathLocator; +using Newtonsoft.Json; +using OS = GodotTools.Utils.OS; + +namespace GodotTools.Ides.Rider; + +public class RiderLocatorEnvironment : IRiderLocatorEnvironment +{ + public JetBrains.Rider.PathLocator.OS CurrentOS + { + get + { + if (OS.IsWindows) + return JetBrains.Rider.PathLocator.OS.Windows; + if (OS.IsMacOS) return JetBrains.Rider.PathLocator.OS.MacOSX; + if (OS.IsUnixLike) return JetBrains.Rider.PathLocator.OS.Linux; + return JetBrains.Rider.PathLocator.OS.Other; + } + } + + public T FromJson<T>(string json) + { + return JsonConvert.DeserializeObject<T>(json); + } + + public void Info(string message, Exception e = null) + { + if (e == null) + GD.Print(message); + else + GD.Print(message, e); + } + + public void Warn(string message, Exception e = null) + { + if (e == null) + GD.PushWarning(message); + else + GD.PushWarning(message, e); + } + + public void Error(string message, Exception e = null) + { + if (e == null) + GD.PushError(message); + else + GD.PushError(message, e); + } +} diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs deleted file mode 100644 index dad6e35344..0000000000 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs +++ /dev/null @@ -1,474 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Runtime.Versioning; -using Godot; -using Microsoft.Win32; -using Newtonsoft.Json; -using Directory = System.IO.Directory; -using Environment = System.Environment; -using File = System.IO.File; -using Path = System.IO.Path; -using OS = GodotTools.Utils.OS; - -// ReSharper disable UnassignedField.Local -// ReSharper disable InconsistentNaming -// ReSharper disable UnassignedField.Global -// ReSharper disable MemberHidesStaticFromOuterClass - -namespace GodotTools.Ides.Rider -{ - /// <summary> - /// This code is a modified version of the JetBrains resharper-unity plugin listed under Apache License 2.0 license: - /// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs - /// </summary> - public static class RiderPathLocator - { - public static RiderInfo[] GetAllRiderPaths() - { - try - { - if (OS.IsWindows) - { - return CollectRiderInfosWindows(); - } - if (OS.IsMacOS) - { - return CollectRiderInfosMac(); - } - if (OS.IsUnixLike) - { - return CollectAllRiderPathsLinux(); - } - throw new InvalidOperationException("Unexpected OS."); - } - catch (Exception e) - { - GD.PushWarning(e.Message); - } - - return Array.Empty<RiderInfo>(); - } - - private static RiderInfo[] CollectAllRiderPathsLinux() - { - var installInfos = new List<RiderInfo>(); - string home = Environment.GetEnvironmentVariable("HOME"); - if (!string.IsNullOrEmpty(home)) - { - string toolboxRiderRootPath = GetToolboxBaseDir(); - installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false) - .Select(a => new RiderInfo(a, true)).ToList()); - - //$Home/.local/share/applications/jetbrains-rider.desktop - var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop")); - - if (shortcut.Exists) - { - string[] lines = File.ReadAllLines(shortcut.FullName); - foreach (string line in lines) - { - if (!line.StartsWith("Exec=\"")) - continue; - string path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault(); - if (string.IsNullOrEmpty(path)) - continue; - - if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox - continue; - installInfos.Add(new RiderInfo(path, false)); - } - } - } - - // snap install - string snapInstallPath = "/snap/rider/current/bin/rider.sh"; - if (new FileInfo(snapInstallPath).Exists) - installInfos.Add(new RiderInfo(snapInstallPath, false)); - - return installInfos.ToArray(); - } - - private static RiderInfo[] CollectRiderInfosMac() - { - var installInfos = new List<RiderInfo>(); - // "/Applications/*Rider*.app" - // should be combined with "Contents/MacOS/rider" - var folder = new DirectoryInfo("/Applications"); - if (folder.Exists) - { - installInfos.AddRange(folder.GetDirectories("*Rider*.app") - .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false)) - .ToList()); - } - - // /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app - // should be combined with "Contents/MacOS/rider" - string toolboxRiderRootPath = GetToolboxBaseDir(); - var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true) - .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true)); - installInfos.AddRange(paths); - - return installInfos.ToArray(); - } - - [SupportedOSPlatform("windows")] - private static RiderInfo[] CollectRiderInfosWindows() - { - var installInfos = new List<RiderInfo>(); - var toolboxRiderRootPath = GetToolboxBaseDir(); - var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList(); - installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList()); - - var installPaths = new List<string>(); - const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; - CollectPathsFromRegistry(registryKey, installPaths); - const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"; - CollectPathsFromRegistry(wowRegistryKey, installPaths); - - installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList()); - - return installInfos.ToArray(); - } - - private static string GetToolboxBaseDir() - { - if (OS.IsWindows) - { - string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - return GetToolboxRiderRootPath(localAppData); - } - - if (OS.IsMacOS) - { - var home = Environment.GetEnvironmentVariable("HOME"); - if (string.IsNullOrEmpty(home)) - return string.Empty; - var localAppData = Path.Combine(home, @"Library/Application Support"); - return GetToolboxRiderRootPath(localAppData); - } - - if (OS.IsUnixLike) - { - var home = Environment.GetEnvironmentVariable("HOME"); - if (string.IsNullOrEmpty(home)) - return string.Empty; - var localAppData = Path.Combine(home, @".local/share"); - return GetToolboxRiderRootPath(localAppData); - } - - return string.Empty; - } - - - private static string GetToolboxRiderRootPath(string localAppData) - { - var toolboxPath = Path.Combine(localAppData, @"JetBrains/Toolbox"); - var settingsJson = Path.Combine(toolboxPath, ".settings.json"); - - if (File.Exists(settingsJson)) - { - var path = SettingsJson.GetInstallLocationFromJson(File.ReadAllText(settingsJson)); - if (!string.IsNullOrEmpty(path)) - toolboxPath = path; - } - - var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps/Rider"); - return toolboxRiderRootPath; - } - - internal static ProductInfo GetBuildVersion(string path) - { - var buildTxtFileInfo = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt())); - var dir = buildTxtFileInfo.DirectoryName; - if (!Directory.Exists(dir)) - return null; - var buildVersionFile = new FileInfo(Path.Combine(dir, "product-info.json")); - if (!buildVersionFile.Exists) - return null; - var json = File.ReadAllText(buildVersionFile.FullName); - return ProductInfo.GetProductInfo(json); - } - - internal static Version GetBuildNumber(string path) - { - var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt())); - if (!file.Exists) - return null; - var text = File.ReadAllText(file.FullName); - if (text.Length <= 3) - return null; - - var versionText = text.Substring(3); - return Version.TryParse(versionText, out var v) ? v : null; - } - - internal static bool IsToolbox(string path) - { - return path.StartsWith(GetToolboxBaseDir()); - } - - private static string GetRelativePathToBuildTxt() - { - if (OS.IsWindows || OS.IsUnixLike) - return "../../build.txt"; - if (OS.IsMacOS) - return "Contents/Resources/build.txt"; - throw new InvalidOperationException("Unknown OS."); - } - - [SupportedOSPlatform("windows")] - private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths) - { - using (var key = Registry.CurrentUser.OpenSubKey(registryKey)) - { - CollectPathsFromRegistry(installPaths, key); - } - using (var key = Registry.LocalMachine.OpenSubKey(registryKey)) - { - CollectPathsFromRegistry(installPaths, key); - } - } - - [SupportedOSPlatform("windows")] - private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key) - { - if (key == null) return; - foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider"))) - { - using (var subkey = key.OpenSubKey(subkeyName)) - { - var folderObject = subkey?.GetValue("InstallLocation"); - if (folderObject == null) continue; - var folder = folderObject.ToString(); - var possiblePath = Path.Combine(folder, @"bin\rider64.exe"); - if (File.Exists(possiblePath)) - installPaths.Add(possiblePath); - } - } - } - - private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern, - bool isMac) - { - if (!Directory.Exists(toolboxRiderRootPath)) - return Array.Empty<string>(); - - var channelDirs = Directory.GetDirectories(toolboxRiderRootPath); - var paths = channelDirs.SelectMany(channelDir => - { - try - { - // use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D - var historyFile = Path.Combine(channelDir, ".history.json"); - if (File.Exists(historyFile)) - { - var json = File.ReadAllText(historyFile); - var build = ToolboxHistory.GetLatestBuildFromJson(json); - if (build != null) - { - var buildDir = Path.Combine(channelDir, build); - var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir); - if (executablePaths.Any()) - return executablePaths; - } - } - - var channelFile = Path.Combine(channelDir, ".channel.settings.json"); - if (File.Exists(channelFile)) - { - var json = File.ReadAllText(channelFile).Replace("active-application", "active_application"); - var build = ToolboxInstallData.GetLatestBuildFromJson(json); - if (build != null) - { - var buildDir = Path.Combine(channelDir, build); - var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir); - if (executablePaths.Any()) - return executablePaths; - } - } - - // changes in toolbox json files format may brake the logic above, so return all found Rider installations - return Directory.GetDirectories(channelDir) - .SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir)); - } - catch (Exception e) - { - // do not write to Debug.Log, just log it. - Logger.Warn($"Failed to get RiderPath from {channelDir}", e); - } - - return Array.Empty<string>(); - }) - .Where(c => !string.IsNullOrEmpty(c)) - .ToArray(); - return paths; - } - - private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir) - { - var folder = new DirectoryInfo(Path.Combine(buildDir, dirName)); - if (!folder.Exists) - return Array.Empty<string>(); - - if (!isMac) - return new[] { Path.Combine(folder.FullName, searchPattern) }.Where(File.Exists).ToArray(); - return folder.GetDirectories(searchPattern).Select(f => f.FullName) - .Where(Directory.Exists).ToArray(); - } - - // Disable the "field is never assigned" compiler warning. We never assign it, but Unity does. - // Note that Unity disable this warning in the generated C# projects -#pragma warning disable 0649 - - [Serializable] - class SettingsJson - { - public string install_location; - - [return: MaybeNull] - public static string GetInstallLocationFromJson(string json) - { - try - { - return JsonConvert.DeserializeObject<SettingsJson>(json).install_location; - } - catch (Exception) - { - Logger.Warn($"Failed to get install_location from json {json}"); - } - - return null; - } - } - - [Serializable] - class ToolboxHistory - { - public List<ItemNode> history; - - public static string GetLatestBuildFromJson(string json) - { - try - { - return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build; - } - catch (Exception) - { - Logger.Warn($"Failed to get latest build from json {json}"); - } - - return null; - } - } - - [Serializable] - class ItemNode - { - public BuildNode item; - } - - [Serializable] - class BuildNode - { - public string build; - } - - [Serializable] - public class ProductInfo - { - public string version; - public string versionSuffix; - - [return: MaybeNull] - internal static ProductInfo GetProductInfo(string json) - { - try - { - var productInfo = JsonConvert.DeserializeObject<ProductInfo>(json); - return productInfo; - } - catch (Exception) - { - Logger.Warn($"Failed to get version from json {json}"); - } - - return null; - } - } - - // ReSharper disable once ClassNeverInstantiated.Global - [Serializable] - class ToolboxInstallData - { - // ReSharper disable once InconsistentNaming - public ActiveApplication active_application; - - [return: MaybeNull] - public static string GetLatestBuildFromJson(string json) - { - try - { - var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json); - var builds = toolbox.active_application.builds; - if (builds != null && builds.Any()) - return builds.First(); - } - catch (Exception) - { - Logger.Warn($"Failed to get latest build from json {json}"); - } - - return null; - } - } - - [Serializable] - class ActiveApplication - { - public List<string> builds; - } - -#pragma warning restore 0649 - - public struct RiderInfo - { - // ReSharper disable once NotAccessedField.Global - public bool IsToolbox; - public string Presentation; - public Version BuildNumber; - public ProductInfo ProductInfo; - public string Path; - - public RiderInfo(string path, bool isToolbox) - { - BuildNumber = GetBuildNumber(path); - ProductInfo = GetBuildVersion(path); - Path = new FileInfo(path).FullName; // normalize separators - var presentation = $"Rider {BuildNumber}"; - - if (ProductInfo != null && !string.IsNullOrEmpty(ProductInfo.version)) - { - var suffix = string.IsNullOrEmpty(ProductInfo.versionSuffix) ? "" : $" {ProductInfo.versionSuffix}"; - presentation = $"Rider {ProductInfo.version}{suffix}"; - } - - if (isToolbox) - presentation += " (JetBrains Toolbox)"; - - Presentation = presentation; - IsToolbox = isToolbox; - } - } - - private static class Logger - { - internal static void Warn(string message, Exception e = null) - { - throw new Exception(message, e); - } - } - } -} diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs index f55ca4c7d7..5c09f1f83a 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs @@ -4,11 +4,19 @@ using System.IO; using System.Linq; using Godot; using GodotTools.Internals; +using JetBrains.Rider.PathLocator; namespace GodotTools.Ides.Rider { public static class RiderPathManager { + private static readonly RiderPathLocator RiderPathLocator; + + static RiderPathManager() + { + RiderPathLocator = new RiderPathLocator(new RiderLocatorEnvironment()); + } + public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional"; private static string GetRiderPathFromSettings() diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs index 37f7005d01..a0bd96412a 100644 --- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs +++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs @@ -38,7 +38,7 @@ internal static class ExtensionMethods } private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol) - => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr; + => symbol.FullQualifiedNameOmitGlobal() == GeneratorClasses.GenerateUnmanagedCallbacksAttr; public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses( this IEnumerable<ClassDeclarationSyntax> source, diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs index 2a72b7c53e..6117ae17ea 100644 --- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs +++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs @@ -21,6 +21,13 @@ namespace GodotPlugins private sealed class PluginLoadContextWrapper { private PluginLoadContext? _pluginLoadContext; + private readonly WeakReference _weakReference; + + private PluginLoadContextWrapper(PluginLoadContext pluginLoadContext, WeakReference weakReference) + { + _pluginLoadContext = pluginLoadContext; + _weakReference = weakReference; + } public string? AssemblyLoadedPath { @@ -31,7 +38,14 @@ namespace GodotPlugins public bool IsCollectible { [MethodImpl(MethodImplOptions.NoInlining)] - get => _pluginLoadContext?.IsCollectible ?? false; + // if _pluginLoadContext is null we already started unloading, so it was collectible + get => _pluginLoadContext?.IsCollectible ?? true; + } + + public bool IsAlive + { + [MethodImpl(MethodImplOptions.NoInlining)] + get => _weakReference.IsAlive; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -43,20 +57,14 @@ namespace GodotPlugins bool isCollectible ) { - var wrapper = new PluginLoadContextWrapper(); - wrapper._pluginLoadContext = new PluginLoadContext( - pluginPath, sharedAssemblies, mainLoadContext, isCollectible); - var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName); + var context = new PluginLoadContext(pluginPath, sharedAssemblies, mainLoadContext, isCollectible); + var reference = new WeakReference(context, trackResurrection: true); + var wrapper = new PluginLoadContextWrapper(context, reference); + var assembly = context.LoadFromAssemblyName(assemblyName); return (assembly, wrapper); } [MethodImpl(MethodImplOptions.NoInlining)] - public WeakReference CreateWeakReference() - { - return new WeakReference(_pluginLoadContext, trackResurrection: true); - } - - [MethodImpl(MethodImplOptions.NoInlining)] internal void Unload() { _pluginLoadContext?.Unload(); @@ -165,7 +173,7 @@ namespace GodotPlugins if (_editorApiAssembly == null) throw new InvalidOperationException("The Godot editor API assembly is not loaded."); - var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint); + var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: false); NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!); @@ -236,32 +244,29 @@ namespace GodotPlugins Console.WriteLine("Unloading assembly load context..."); - var alcWeakReference = pluginLoadContext.CreateWeakReference(); - pluginLoadContext.Unload(); - pluginLoadContext = null; int startTimeMs = Environment.TickCount; bool takingTooLong = false; - while (alcWeakReference.IsAlive) + while (pluginLoadContext.IsAlive) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); - if (!alcWeakReference.IsAlive) + if (!pluginLoadContext.IsAlive) break; int elapsedTimeMs = Environment.TickCount - startTimeMs; - if (!takingTooLong && elapsedTimeMs >= 2000) + if (!takingTooLong && elapsedTimeMs >= 200) { takingTooLong = true; // TODO: How to log from GodotPlugins? (delegate pointer?) Console.Error.WriteLine("Assembly unloading is taking longer than expected..."); } - else if (elapsedTimeMs >= 5000) + else if (elapsedTimeMs >= 1000) { // TODO: How to log from GodotPlugins? (delegate pointer?) Console.Error.WriteLine( @@ -273,6 +278,7 @@ namespace GodotPlugins Console.WriteLine("Assembly load context unloaded successfully."); + pluginLoadContext = null; return true; } catch (Exception e) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index 36f5d8e2ab..74425c9835 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.ComponentModel; namespace Godot { @@ -623,21 +624,31 @@ namespace Godot /// </summary> /// <param name="target">The position to look at.</param> /// <param name="up">The relative up direction.</param> + /// <param name="useModelFront"> + /// If true, then the model is oriented in reverse, + /// towards the model front axis (+Z, Vector3.ModelFront), + /// which is more useful for orienting 3D models. + /// </param> /// <returns>The resulting basis matrix.</returns> - public static Basis LookingAt(Vector3 target, Vector3 up) + public static Basis LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false) { + up ??= Vector3.Up; #if DEBUG if (target.IsZeroApprox()) { throw new ArgumentException("The vector can't be zero.", nameof(target)); } - if (up.IsZeroApprox()) + if (up.Value.IsZeroApprox()) { throw new ArgumentException("The vector can't be zero.", nameof(up)); } #endif - Vector3 column2 = -target.Normalized(); - Vector3 column0 = up.Cross(column2); + Vector3 column2 = target.Normalized(); + if (!useModelFront) + { + column2 = -column2; + } + Vector3 column0 = up.Value.Cross(column2); #if DEBUG if (column0.IsZeroApprox()) { @@ -649,6 +660,13 @@ namespace Godot return new Basis(column0, column1, column2); } + /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/> + [EditorBrowsable(EditorBrowsableState.Never)] + public static Basis LookingAt(Vector3 target, Vector3 up) + { + return LookingAt(target, up, false); + } + /// <summary> /// Returns the orthonormalized version of the basis matrix (useful to /// call occasionally to avoid rounding errors for orthogonal matrices). diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs index 456a118b90..8217572648 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs @@ -18,5 +18,26 @@ namespace Godot.Bridge ExceptionUtils.LogException(e); } } + + // Returns true, if releasing the provided handle is necessary for assembly unloading to succeed. + // This check is not perfect and only intended to prevent things in GodotTools from being reloaded. + [UnmanagedCallersOnly] + internal static godot_bool GCHandleIsTargetCollectible(IntPtr gcHandlePtr) + { + try + { + var target = GCHandle.FromIntPtr(gcHandlePtr).Target; + + if (target is Delegate @delegate) + return DelegateUtils.IsDelegateCollectible(@delegate).ToGodotBool(); + + return target.GetType().IsCollectible.ToGodotBool(); + } + catch (Exception e) + { + ExceptionUtils.LogException(e); + return godot_bool.True; + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs index 0571515e61..109643c2d4 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs @@ -38,6 +38,7 @@ namespace Godot.Bridge public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState; public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState; public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle; + public delegate* unmanaged<IntPtr, godot_bool> GCHandleBridge_GCHandleIsTargetCollectible; public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo; public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown; public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded; @@ -78,6 +79,7 @@ namespace Godot.Bridge CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState, CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState, GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle, + GCHandleBridge_GCHandleIsTargetCollectible = &GCHandleBridge.GCHandleIsTargetCollectible, DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo, DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown, GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded, diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index 279dadf425..6c2fb7374c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -500,24 +500,17 @@ namespace Godot Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void); int parametersCount = reader.ReadInt32(); + var parameterTypes = parametersCount == 0 ? Type.EmptyTypes : new Type[parametersCount]; - if (parametersCount > 0) + for (int i = 0; i < parametersCount; i++) { - var parameterTypes = new Type[parametersCount]; - - for (int i = 0; i < parametersCount; i++) - { - Type? parameterType = DeserializeType(reader); - if (parameterType == null) - return false; - parameterTypes[i] = parameterType; - } - - methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null); - return methodInfo != null && methodInfo.ReturnType == returnType; + Type? parameterType = DeserializeType(reader); + if (parameterType == null) + return false; + parameterTypes[i] = parameterType; } - methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags); + methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null); return methodInfo != null && methodInfo.ReturnType == returnType; } @@ -560,6 +553,38 @@ namespace Godot return type; } + // Returns true, if unloading the delegate is necessary for assembly unloading to succeed. + // This check is not perfect and only intended to prevent things in GodotTools from being reloaded. + internal static bool IsDelegateCollectible(Delegate @delegate) + { + if (@delegate.GetType().IsCollectible) + return true; + + if (@delegate is MulticastDelegate multicastDelegate) + { + Delegate[] invocationList = multicastDelegate.GetInvocationList(); + + if (invocationList.Length > 1) + { + foreach (Delegate oneDelegate in invocationList) + if (IsDelegateCollectible(oneDelegate)) + return true; + + return false; + } + } + + if (@delegate.Method.IsCollectible) + return true; + + object? target = @delegate.Target; + + if (target is not null && target.GetType().IsCollectible) + return true; + + return false; + } + internal static class RuntimeTypeConversionHelper { [SuppressMessage("ReSharper", "RedundantNameQualifier")] diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs index b9a5ac82d1..12e8a638d3 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs @@ -125,7 +125,10 @@ namespace Godot NativePtr = IntPtr.Zero; } - DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf); + if (_weakReferenceToSelf != null) + { + DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf); + } } /// <summary> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 1e2aaa299f..ae2c025137 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using System.ComponentModel; namespace Godot { @@ -175,14 +176,26 @@ namespace Godot /// </summary> /// <param name="target">The object to look at.</param> /// <param name="up">The relative up direction.</param> + /// <param name="useModelFront"> + /// If true, then the model is oriented in reverse, + /// towards the model front axis (+Z, Vector3.ModelFront), + /// which is more useful for orienting 3D models. + /// </param> /// <returns>The resulting transform.</returns> - public readonly Transform3D LookingAt(Vector3 target, Vector3 up) + public readonly Transform3D LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false) { Transform3D t = this; - t.SetLookAt(Origin, target, up); + t.SetLookAt(Origin, target, up ?? Vector3.Up, useModelFront); return t; } + /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/> + [EditorBrowsable(EditorBrowsableState.Never)] + public readonly Transform3D LookingAt(Vector3 target, Vector3 up) + { + return LookingAt(target, up, false); + } + /// <summary> /// Returns the transform with the basis orthogonal (90 degrees), /// and normalized axis vectors (scale of 1 or -1). @@ -247,9 +260,9 @@ namespace Godot return new Transform3D(Basis * tmpBasis, Origin); } - private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up) + private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up, bool useModelFront = false) { - Basis = Basis.LookingAt(target - eye, up); + Basis = Basis.LookingAt(target - eye, up, useModelFront); Origin = eye; } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs index c773c0fda6..d929b5c6ab 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs @@ -660,6 +660,13 @@ namespace Godot private static readonly Vector3 _forward = new Vector3(0, 0, -1); private static readonly Vector3 _back = new Vector3(0, 0, 1); + private static readonly Vector3 _modelLeft = new Vector3(1, 0, 0); + private static readonly Vector3 _modelRight = new Vector3(-1, 0, 0); + private static readonly Vector3 _modelTop = new Vector3(0, 1, 0); + private static readonly Vector3 _modelBottom = new Vector3(0, -1, 0); + private static readonly Vector3 _modelFront = new Vector3(0, 0, 1); + private static readonly Vector3 _modelRear = new Vector3(0, 0, -1); + /// <summary> /// Zero vector, a vector with all components set to <c>0</c>. /// </summary> @@ -712,6 +719,31 @@ namespace Godot public static Vector3 Back { get { return _back; } } /// <summary> + /// Unit vector pointing towards the left side of imported 3D assets. + /// </summary> + public static Vector3 ModelLeft { get { return _modelLeft; } } + /// <summary> + /// Unit vector pointing towards the right side of imported 3D assets. + /// </summary> + public static Vector3 ModelRight { get { return _modelRight; } } + /// <summary> + /// Unit vector pointing towards the top side (up) of imported 3D assets. + /// </summary> + public static Vector3 ModelTop { get { return _modelTop; } } + /// <summary> + /// Unit vector pointing towards the bottom side (down) of imported 3D assets. + /// </summary> + public static Vector3 ModelBottom { get { return _modelBottom; } } + /// <summary> + /// Unit vector pointing towards the front side (facing forward) of imported 3D assets. + /// </summary> + public static Vector3 ModelFront { get { return _modelFront; } } + /// <summary> + /// Unit vector pointing towards the rear side (back) of imported 3D assets. + /// </summary> + public static Vector3 ModelRear { get { return _modelRear; } } + + /// <summary> /// Constructs a new <see cref="Vector3"/> with the given components. /// </summary> /// <param name="x">The vector's X component.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 16820e363a..8a36b3e514 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -20,7 +20,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>GodotSharp</PackageId> - <Version>4.1.0</Version> + <Version>4.2.0</Version> <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj index c249ebf804..db9337d4eb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj @@ -15,7 +15,7 @@ <Authors>Godot Engine contributors</Authors> <PackageId>GodotSharpEditor</PackageId> - <Version>4.1.0</Version> + <Version>4.2.0</Version> <PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion> <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl> <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl> diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 92fa30e5e8..4337478370 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -488,15 +488,31 @@ bool GDMono::_load_project_assembly() { #endif #ifdef GD_MONO_HOT_RELOAD +void GDMono::reload_failure() { + if (++project_load_failure_count >= (int)GLOBAL_GET("dotnet/project/assembly_reload_attempts")) { + // After reloading a project has failed n times in a row, update the path and modification time + // to stop any further attempts at loading this assembly, which probably is never going to work anyways. + project_load_failure_count = 0; + + ERR_PRINT_ED(".NET: Giving up on assembly reloading. Please restart the editor if unloading was failing."); + + String assembly_name = path::get_csharp_project_name(); + String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir().path_join(assembly_name + ".dll"); + assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path); + project_assembly_path = assembly_path.simplify_path(); + project_assembly_modified_time = FileAccess::get_modified_time(assembly_path); + } +} + Error GDMono::reload_project_assemblies() { ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG); finalizing_scripts_domain = true; - CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload(); - if (!get_plugin_callbacks().UnloadProjectPluginCallback()) { - ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies."); + ERR_PRINT_ED(".NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information."); + reload_failure(); + return FAILED; } finalizing_scripts_domain = false; @@ -504,10 +520,16 @@ Error GDMono::reload_project_assemblies() { // Load the project's main assembly. Here, during hot-reloading, we do // consider failing to load the project's main assembly to be an error. if (!_load_project_assembly()) { - print_error(".NET: Failed to load project assembly."); + ERR_PRINT_ED(".NET: Failed to load project assembly."); + reload_failure(); return ERR_CANT_OPEN; } + if (project_load_failure_count > 0) { + project_load_failure_count = 0; + ERR_PRINT_ED(".NET: Assembly reloading succeeded after failures."); + } + return OK; } #endif diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h index 398f94d924..c629ab2eff 100644 --- a/modules/mono/mono_gd/gd_mono.h +++ b/modules/mono/mono_gd/gd_mono.h @@ -68,6 +68,7 @@ class GDMono { String project_assembly_path; uint64_t project_assembly_modified_time = 0; + int project_load_failure_count = 0; #ifdef TOOLS_ENABLED bool _load_project_assembly(); @@ -144,6 +145,7 @@ public: #endif #ifdef GD_MONO_HOT_RELOAD + void reload_failure(); Error reload_project_assemblies(); #endif diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp index e254484df9..8fdf163b26 100644 --- a/modules/mono/mono_gd/gd_mono_cache.cpp +++ b/modules/mono/mono_gd/gd_mono_cache.cpp @@ -79,6 +79,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) { CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState); CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState); CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle); + CHECK_CALLBACK_NOT_NULL(GCHandleBridge, GCHandleIsTargetCollectible); CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo); CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown); CHECK_CALLBACK_NOT_NULL(GD, OnCoreApiAssemblyLoaded); diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h index 9201da7cae..f604e4d681 100644 --- a/modules/mono/mono_gd/gd_mono_cache.h +++ b/modules/mono/mono_gd/gd_mono_cache.h @@ -104,6 +104,7 @@ struct ManagedCallbacks { using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *); using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *); using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr); + using FuncGCHandleBridge_GCHandleIsTargetCollectible = bool(GD_CLR_STDCALL *)(GCHandleIntPtr); using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *); using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)(); using FuncGD_OnCoreApiAssemblyLoaded = void(GD_CLR_STDCALL *)(bool); @@ -138,6 +139,7 @@ struct ManagedCallbacks { FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState; FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState; FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle; + FuncGCHandleBridge_GCHandleIsTargetCollectible GCHandleBridge_GCHandleIsTargetCollectible; FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo; FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown; FuncGD_OnCoreApiAssemblyLoaded GD_OnCoreApiAssemblyLoaded; diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml index e6564a8aac..482db3e8b5 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiplayerSpawner" inherits="Node" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="MultiplayerSpawner" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Automatically replicates spawnable nodes from the authority to other multiplayer peers. </brief_description> diff --git a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml index 82698f7b15..df2644767d 100644 --- a/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml +++ b/modules/multiplayer/doc_classes/MultiplayerSynchronizer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="MultiplayerSynchronizer" inherits="Node" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="MultiplayerSynchronizer" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Synchronizes properties from the multiplayer authority to the remote peers. </brief_description> diff --git a/modules/multiplayer/doc_classes/SceneMultiplayer.xml b/modules/multiplayer/doc_classes/SceneMultiplayer.xml index 2df2d53b4d..7abee8e2c8 100644 --- a/modules/multiplayer/doc_classes/SceneMultiplayer.xml +++ b/modules/multiplayer/doc_classes/SceneMultiplayer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SceneMultiplayer" inherits="MultiplayerAPI" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="SceneMultiplayer" inherits="MultiplayerAPI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> High-level multiplayer API implementation. </brief_description> @@ -64,7 +64,7 @@ If [code]true[/code], the MultiplayerAPI will allow encoding and decoding of object during RPCs. [b]Warning:[/b] Deserialized objects can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threat such as remote code execution. </member> - <member name="auth_callback" type="Callable" setter="set_auth_callback" getter="get_auth_callback"> + <member name="auth_callback" type="Callable" setter="set_auth_callback" getter="get_auth_callback" default="Callable()"> The callback to execute when when receiving authentication data sent via [method send_auth]. If the [Callable] is empty (default), peers will be automatically accepted as soon as they connect. </member> <member name="auth_timeout" type="float" setter="set_auth_timeout" getter="get_auth_timeout" default="3.0"> diff --git a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml index 55dd9cade2..b976eea30b 100644 --- a/modules/multiplayer/doc_classes/SceneReplicationConfig.xml +++ b/modules/multiplayer/doc_classes/SceneReplicationConfig.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="SceneReplicationConfig" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="SceneReplicationConfig" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Configuration for properties to synchronize with a [MultiplayerSynchronizer]. </brief_description> diff --git a/modules/multiplayer/multiplayer_spawner.cpp b/modules/multiplayer/multiplayer_spawner.cpp index 4b1b6b541d..6c6aa28344 100644 --- a/modules/multiplayer/multiplayer_spawner.cpp +++ b/modules/multiplayer/multiplayer_spawner.cpp @@ -175,7 +175,7 @@ void MultiplayerSpawner::_bind_methods() { ClassDB::bind_method(D_METHOD("get_spawn_function"), &MultiplayerSpawner::get_spawn_function); ClassDB::bind_method(D_METHOD("set_spawn_function", "spawn_function"), &MultiplayerSpawner::set_spawn_function); - ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_spawn_function", "get_spawn_function"); + ADD_PROPERTY(PropertyInfo(Variant::CALLABLE, "spawn_function", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_spawn_function", "get_spawn_function"); ADD_SIGNAL(MethodInfo("despawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("spawned", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); diff --git a/modules/multiplayer/multiplayer_synchronizer.cpp b/modules/multiplayer/multiplayer_synchronizer.cpp index e3c31352a7..9c2d281f72 100644 --- a/modules/multiplayer/multiplayer_synchronizer.cpp +++ b/modules/multiplayer/multiplayer_synchronizer.cpp @@ -157,7 +157,7 @@ Error MultiplayerSynchronizer::get_state(const List<NodePath> &p_properties, Obj bool valid = false; const Object *obj = _get_prop_target(p_obj, prop); ERR_FAIL_COND_V(!obj, FAILED); - r_variant.write[i] = obj->get(prop.get_concatenated_subnames(), &valid); + r_variant.write[i] = obj->get_indexed(prop.get_subnames(), &valid); r_variant_ptrs.write[i] = &r_variant[i]; ERR_FAIL_COND_V_MSG(!valid, ERR_INVALID_DATA, vformat("Property '%s' not found.", prop)); i++; @@ -171,7 +171,7 @@ Error MultiplayerSynchronizer::set_state(const List<NodePath> &p_properties, Obj for (const NodePath &prop : p_properties) { Object *obj = _get_prop_target(p_obj, prop); ERR_FAIL_COND_V(!obj, FAILED); - obj->set(prop.get_concatenated_subnames(), p_state[i]); + obj->set_indexed(prop.get_subnames(), p_state[i]); i += 1; } return OK; @@ -433,11 +433,10 @@ List<NodePath> MultiplayerSynchronizer::get_delta_properties(uint64_t p_indexes) const List<NodePath> watch_props = replication_config->get_watch_properties(); int idx = 0; for (const NodePath &prop : watch_props) { - if ((p_indexes & (1ULL << idx)) == 0) { + if ((p_indexes & (1ULL << idx++)) == 0) { continue; } out.push_back(prop); - idx++; } return out; } diff --git a/modules/navigation/SCsub b/modules/navigation/SCsub index 18a8d550ec..46bcb0fba4 100644 --- a/modules/navigation/SCsub +++ b/modules/navigation/SCsub @@ -37,12 +37,10 @@ if env["builtin_recastnavigation"]: if env["builtin_rvo2_2d"]: thirdparty_dir = "#thirdparty/rvo2/rvo2_2d/" thirdparty_sources = [ - "Agent2d.cc", - "Obstacle2d.cc", - "KdTree2d.cc", - "Line.cc", - "RVOSimulator2d.cc", - "Vector2.cc", + "Agent2d.cpp", + "Obstacle2d.cpp", + "KdTree2d.cpp", + "RVOSimulator2d.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] @@ -56,11 +54,9 @@ if env["builtin_rvo2_2d"]: if env["builtin_rvo2_3d"]: thirdparty_dir = "#thirdparty/rvo2/rvo2_3d/" thirdparty_sources = [ - "Agent3d.cc", - "KdTree3d.cc", - "Plane.cc", - "RVOSimulator3d.cc", - "Vector3.cc", + "Agent3d.cpp", + "KdTree3d.cpp", + "RVOSimulator3d.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp index 0ec0c9545f..b73c5ca3e2 100644 --- a/modules/navigation/godot_navigation_server.cpp +++ b/modules/navigation/godot_navigation_server.cpp @@ -358,22 +358,9 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) { NavRegion *region = region_owner.get_or_null(p_region); ERR_FAIL_COND(region == nullptr); - if (region->get_map() != nullptr) { - if (region->get_map()->get_self() == p_map) { - return; // Pointless - } - - region->get_map()->remove_region(region); - region->set_map(nullptr); - } - - if (p_map.is_valid()) { - NavMap *map = map_owner.get_or_null(p_map); - ERR_FAIL_COND(map == nullptr); + NavMap *map = map_owner.get_or_null(p_map); - map->add_region(region); - region->set_map(map); - } + region->set_map(map); } COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) { @@ -459,10 +446,13 @@ COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navi region->set_mesh(p_navigation_mesh); } +#ifndef DISABLE_DEPRECATED void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) { ERR_FAIL_COND(p_navigation_mesh.is_null()); ERR_FAIL_COND(p_root_node == nullptr); + WARN_PRINT_ONCE("NavigationServer3D::region_bake_navigation_mesh() is deprecated due to core threading changes. To upgrade existing code, first create a NavigationMeshSourceGeometryData3D resource. Use this resource with method parse_source_geometry_data() to parse the SceneTree for nodes that should contribute to the navigation mesh baking. The SceneTree parsing needs to happen on the main thread. After the parsing is finished use the resource with method bake_from_source_geometry_data() to bake a navigation mesh.."); + #ifndef _3D_DISABLED NavigationMeshGenerator::get_singleton()->clear(p_navigation_mesh); Ref<NavigationMeshSourceGeometryData3D> source_geometry_data; @@ -471,6 +461,7 @@ void GodotNavigationServer::region_bake_navigation_mesh(Ref<NavigationMesh> p_na NavigationMeshGenerator::get_singleton()->bake_from_source_geometry_data(p_navigation_mesh, source_geometry_data); #endif } +#endif // DISABLE_DEPRECATED int GodotNavigationServer::region_get_connections_count(RID p_region) const { NavRegion *region = region_owner.get_or_null(p_region); @@ -506,22 +497,9 @@ COMMAND_2(link_set_map, RID, p_link, RID, p_map) { NavLink *link = link_owner.get_or_null(p_link); ERR_FAIL_COND(link == nullptr); - if (link->get_map() != nullptr) { - if (link->get_map()->get_self() == p_map) { - return; // Pointless - } - - link->get_map()->remove_link(link); - link->set_map(nullptr); - } - - if (p_map.is_valid()) { - NavMap *map = map_owner.get_or_null(p_map); - ERR_FAIL_COND(map == nullptr); + NavMap *map = map_owner.get_or_null(p_map); - map->add_link(link); - link->set_map(map); - } + link->set_map(map); } RID GodotNavigationServer::link_get_map(const RID p_link) const { @@ -673,27 +651,9 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) { NavAgent *agent = agent_owner.get_or_null(p_agent); ERR_FAIL_COND(agent == nullptr); - if (agent->get_map()) { - if (agent->get_map()->get_self() == p_map) { - return; // Pointless - } - - agent->get_map()->remove_agent(agent); - } - - agent->set_map(nullptr); - - if (p_map.is_valid()) { - NavMap *map = map_owner.get_or_null(p_map); - ERR_FAIL_COND(map == nullptr); - - agent->set_map(map); - map->add_agent(agent); + NavMap *map = map_owner.get_or_null(p_map); - if (agent->has_avoidance_callback()) { - map->set_agent_as_controlled(agent); - } - } + agent->set_map(map); } COMMAND_2(agent_set_paused, RID, p_agent, bool, p_paused) { @@ -875,23 +835,9 @@ COMMAND_2(obstacle_set_map, RID, p_obstacle, RID, p_map) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_obstacle); ERR_FAIL_COND(obstacle == nullptr); - if (obstacle->get_map()) { - if (obstacle->get_map()->get_self() == p_map) { - return; // Pointless - } - - obstacle->get_map()->remove_obstacle(obstacle); - } - - obstacle->set_map(nullptr); - - if (p_map.is_valid()) { - NavMap *map = map_owner.get_or_null(p_map); - ERR_FAIL_COND(map == nullptr); + NavMap *map = map_owner.get_or_null(p_map); - obstacle->set_map(map); - map->add_obstacle(obstacle); - } + obstacle->set_map(map); } RID GodotNavigationServer::obstacle_get_map(RID p_obstacle) const { @@ -1050,17 +996,16 @@ void GodotNavigationServer::internal_free_agent(RID p_object) { void GodotNavigationServer::internal_free_obstacle(RID p_object) { NavObstacle *obstacle = obstacle_owner.get_or_null(p_object); if (obstacle) { + NavAgent *obstacle_agent = obstacle->get_agent(); + if (obstacle_agent) { + RID _agent_rid = obstacle_agent->get_self(); + internal_free_agent(_agent_rid); + obstacle->set_agent(nullptr); + } if (obstacle->get_map() != nullptr) { obstacle->get_map()->remove_obstacle(obstacle); obstacle->set_map(nullptr); } - if (obstacle->get_agent()) { - if (obstacle->get_agent()->get_self() != RID()) { - RID _agent_rid = obstacle->get_agent()->get_self(); - obstacle->set_agent(nullptr); - internal_free_agent(_agent_rid); - } - } obstacle_owner.free(p_object); } } diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h index 6b394157bc..95aa88194e 100644 --- a/modules/navigation/godot_navigation_server.h +++ b/modules/navigation/godot_navigation_server.h @@ -154,7 +154,9 @@ public: virtual uint32_t region_get_navigation_layers(RID p_region) const override; COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform); COMMAND_2(region_set_navigation_mesh, RID, p_region, Ref<NavigationMesh>, p_navigation_mesh); +#ifndef DISABLE_DEPRECATED virtual void region_bake_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh, Node *p_root_node) override; +#endif // DISABLE_DEPRECATED virtual int region_get_connections_count(RID p_region) const override; virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const override; virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const override; diff --git a/modules/navigation/nav_agent.cpp b/modules/navigation/nav_agent.cpp index 19b73dc633..010bd2d7c0 100644 --- a/modules/navigation/nav_agent.cpp +++ b/modules/navigation/nav_agent.cpp @@ -90,8 +90,23 @@ void NavAgent::_update_rvo_agent_properties() { } void NavAgent::set_map(NavMap *p_map) { + if (map == p_map) { + return; + } + + if (map) { + map->remove_agent(this); + } + map = p_map; agent_dirty = true; + + if (map) { + map->add_agent(this); + if (avoidance_enabled) { + map->set_agent_as_controlled(this); + } + } } bool NavAgent::is_map_changed() { diff --git a/modules/navigation/nav_link.cpp b/modules/navigation/nav_link.cpp index 5607a3253e..d712987a46 100644 --- a/modules/navigation/nav_link.cpp +++ b/modules/navigation/nav_link.cpp @@ -36,8 +36,17 @@ void NavLink::set_map(NavMap *p_map) { if (map == p_map) { return; } + + if (map) { + map->remove_link(this); + } + map = p_map; link_dirty = true; + + if (map) { + map->add_link(this); + } } void NavLink::set_bidirectional(bool p_bidirectional) { diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 2595a02a61..3a1d412618 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -301,6 +301,46 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p } } + // Search all faces of start polygon as well. + bool closest_point_on_start_poly = false; + for (size_t point_id = 2; point_id < begin_poly->points.size(); point_id++) { + Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos); + Vector3 spoint = f.get_closest_point_to(p_destination); + real_t dpoint = spoint.distance_to(p_destination); + if (dpoint < end_d) { + end_point = spoint; + end_d = dpoint; + closest_point_on_start_poly = true; + } + } + + if (closest_point_on_start_poly) { + // No point to run PostProcessing when start and end convex polygon is the same. + if (r_path_types) { + r_path_types->resize(2); + r_path_types->write[0] = begin_poly->owner->get_type(); + r_path_types->write[1] = begin_poly->owner->get_type(); + } + + if (r_path_rids) { + r_path_rids->resize(2); + (*r_path_rids)[0] = begin_poly->owner->get_self(); + (*r_path_rids)[1] = begin_poly->owner->get_self(); + } + + if (r_path_owners) { + r_path_owners->resize(2); + r_path_owners->write[0] = begin_poly->owner->get_owner_id(); + r_path_owners->write[1] = begin_poly->owner->get_owner_id(); + } + + Vector<Vector3> path; + path.resize(2); + path.write[0] = begin_point; + path.write[1] = end_point; + return path; + } + // Reset open and navigation_polys gd::NavigationPoly np = navigation_polys[0]; navigation_polys.clear(); @@ -346,9 +386,44 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p } } - // If we did not find a route, return an empty path. + // We did not find a route but we have both a start polygon and an end polygon at this point. + // Usually this happens because there was not a single external or internal connected edge, e.g. our start polygon is an isolated, single convex polygon. if (!found_route) { - return Vector<Vector3>(); + end_d = FLT_MAX; + // Search all faces of the start polygon for the closest point to our target position. + for (size_t point_id = 2; point_id < begin_poly->points.size(); point_id++) { + Face3 f(begin_poly->points[0].pos, begin_poly->points[point_id - 1].pos, begin_poly->points[point_id].pos); + Vector3 spoint = f.get_closest_point_to(p_destination); + real_t dpoint = spoint.distance_to(p_destination); + if (dpoint < end_d) { + end_point = spoint; + end_d = dpoint; + } + } + + if (r_path_types) { + r_path_types->resize(2); + r_path_types->write[0] = begin_poly->owner->get_type(); + r_path_types->write[1] = begin_poly->owner->get_type(); + } + + if (r_path_rids) { + r_path_rids->resize(2); + (*r_path_rids)[0] = begin_poly->owner->get_self(); + (*r_path_rids)[1] = begin_poly->owner->get_self(); + } + + if (r_path_owners) { + r_path_owners->resize(2); + r_path_owners->write[0] = begin_poly->owner->get_owner_id(); + r_path_owners->write[1] = begin_poly->owner->get_owner_id(); + } + + Vector<Vector3> path; + path.resize(2); + path.write[0] = begin_point; + path.write[1] = end_point; + return path; } Vector<Vector3> path; @@ -1042,16 +1117,16 @@ void NavMap::_update_rvo_obstacles_tree_2d() { rvo_2d_obstacle->avoidance_layers_ = _obstacle_avoidance_layers; if (i != 0) { - rvo_2d_obstacle->previous_ = raw_obstacles.back(); - rvo_2d_obstacle->previous_->next_ = rvo_2d_obstacle; + rvo_2d_obstacle->prevObstacle_ = raw_obstacles.back(); + rvo_2d_obstacle->prevObstacle_->nextObstacle_ = rvo_2d_obstacle; } if (i == rvo_2d_vertices.size() - 1) { - rvo_2d_obstacle->next_ = raw_obstacles[obstacleNo]; - rvo_2d_obstacle->next_->previous_ = rvo_2d_obstacle; + rvo_2d_obstacle->nextObstacle_ = raw_obstacles[obstacleNo]; + rvo_2d_obstacle->nextObstacle_->prevObstacle_ = rvo_2d_obstacle; } - rvo_2d_obstacle->direction_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]); + rvo_2d_obstacle->unitDir_ = normalize(rvo_2d_vertices[(i == rvo_2d_vertices.size() - 1 ? 0 : i + 1)] - rvo_2d_vertices[i]); if (rvo_2d_vertices.size() == 2) { rvo_2d_obstacle->isConvex_ = true; @@ -1099,9 +1174,9 @@ void NavMap::_update_rvo_simulation() { } void NavMap::compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent) { - (*(agent + index))->get_rvo_agent_2d()->computeNeighbors(rvo_simulation_2d.kdTree_); - (*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(rvo_simulation_2d.timeStep_); - (*(agent + index))->get_rvo_agent_2d()->update(rvo_simulation_2d.timeStep_); + (*(agent + index))->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d); + (*(agent + index))->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d); + (*(agent + index))->get_rvo_agent_2d()->update(&rvo_simulation_2d); (*(agent + index))->update(); } @@ -1124,9 +1199,9 @@ void NavMap::step(real_t p_deltatime) { WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); } else { for (NavAgent *agent : active_2d_avoidance_agents) { - agent->get_rvo_agent_2d()->computeNeighbors(rvo_simulation_2d.kdTree_); - agent->get_rvo_agent_2d()->computeNewVelocity(rvo_simulation_2d.timeStep_); - agent->get_rvo_agent_2d()->update(rvo_simulation_2d.timeStep_); + agent->get_rvo_agent_2d()->computeNeighbors(&rvo_simulation_2d); + agent->get_rvo_agent_2d()->computeNewVelocity(&rvo_simulation_2d); + agent->get_rvo_agent_2d()->update(&rvo_simulation_2d); agent->update(); } } diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index bf4fec1ac8..9c0ce3e71e 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -36,10 +36,18 @@ void NavRegion::set_map(NavMap *p_map) { if (map == p_map) { return; } + + if (map) { + map->remove_region(this); + } + map = p_map; polygons_dirty = true; - if (!map) { - connections.clear(); + + connections.clear(); + + if (map) { + map->add_region(this); } } @@ -107,11 +115,11 @@ void NavRegion::update_polygons() { #ifdef DEBUG_ENABLED if (!Math::is_equal_approx(double(map->get_cell_size()), double(mesh->get_cell_size()))) { - ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_size` than the `cell_size` set on the navigation map."); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_size()), double(mesh->get_cell_size()))); } if (!Math::is_equal_approx(double(map->get_cell_height()), double(mesh->get_cell_height()))) { - ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a different `cell_height` than the `cell_height` set on the navigation map."); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_height()), double(mesh->get_cell_height()))); } if (map && Math::rad_to_deg(map->get_up().angle_to(transform.basis.get_column(1))) >= 90.0f) { diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp index 18c8256255..89afb4a8ea 100644 --- a/modules/navigation/navigation_mesh_generator.cpp +++ b/modules/navigation/navigation_mesh_generator.cpp @@ -738,6 +738,7 @@ void NavigationMeshGenerator::bake_from_source_geometry_data(Ref<NavigationMesh> nav_vertices.push_back(Vector3(v[0], v[1], v[2])); } p_navigation_mesh->set_vertices(nav_vertices); + p_navigation_mesh->clear_polygons(); for (int i = 0; i < detail_mesh->nmeshes; i++) { const unsigned int *detail_mesh_m = &detail_mesh->meshes[i * 4]; diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml index fb4e0d4f78..4c6cdfbf12 100644 --- a/modules/noise/doc_classes/FastNoiseLite.xml +++ b/modules/noise/doc_classes/FastNoiseLite.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="FastNoiseLite" inherits="Noise" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="FastNoiseLite" inherits="Noise" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Generates noise using the FastNoiseLite library. </brief_description> diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml index c075b5b629..dd232af1cc 100644 --- a/modules/noise/doc_classes/Noise.xml +++ b/modules/noise/doc_classes/Noise.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="Noise" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="Noise" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Abstract base class for noise generators. </brief_description> diff --git a/modules/noise/doc_classes/NoiseTexture2D.xml b/modules/noise/doc_classes/NoiseTexture2D.xml index 4d3e42f3d5..e25af794d4 100644 --- a/modules/noise/doc_classes/NoiseTexture2D.xml +++ b/modules/noise/doc_classes/NoiseTexture2D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NoiseTexture2D" inherits="Texture2D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="NoiseTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A texture filled with noise generated by a [Noise] object. </brief_description> diff --git a/modules/noise/doc_classes/NoiseTexture3D.xml b/modules/noise/doc_classes/NoiseTexture3D.xml index 519e4eb8ad..0ada6942ad 100644 --- a/modules/noise/doc_classes/NoiseTexture3D.xml +++ b/modules/noise/doc_classes/NoiseTexture3D.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="NoiseTexture3D" inherits="Texture3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="NoiseTexture3D" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A texture filled with noise generated by a [Noise] object. </brief_description> diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index a7176e0816..1b0c5cb9e3 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -32,8 +32,6 @@ #include "noise.h" -#include "core/core_string_names.h" - NoiseTexture2D::NoiseTexture2D() { noise = Ref<Noise>(); @@ -223,11 +221,11 @@ void NoiseTexture2D::set_noise(Ref<Noise> p_noise) { return; } if (noise.is_valid()) { - noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + noise->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } noise = p_noise; if (noise.is_valid()) { - noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + noise->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } _queue_update(); } @@ -347,11 +345,11 @@ void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) { return; } if (color_ramp.is_valid()) { - color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } color_ramp = p_gradient; if (color_ramp.is_valid()) { - color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture2D::_queue_update)); + color_ramp->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update)); } _queue_update(); } diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp index f6c67b0f2d..ed242e7faa 100644 --- a/modules/noise/noise_texture_3d.cpp +++ b/modules/noise/noise_texture_3d.cpp @@ -32,8 +32,6 @@ #include "noise.h" -#include "core/core_string_names.h" - NoiseTexture3D::NoiseTexture3D() { noise = Ref<Noise>(); @@ -214,11 +212,11 @@ void NoiseTexture3D::set_noise(Ref<Noise> p_noise) { return; } if (noise.is_valid()) { - noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + noise->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } noise = p_noise; if (noise.is_valid()) { - noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + noise->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } _queue_update(); } @@ -297,11 +295,11 @@ void NoiseTexture3D::set_color_ramp(const Ref<Gradient> &p_gradient) { return; } if (color_ramp.is_valid()) { - color_ramp->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } color_ramp = p_gradient; if (color_ramp.is_valid()) { - color_ramp->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture3D::_queue_update)); + color_ramp->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update)); } _queue_update(); } diff --git a/modules/noise/tests/test_noise_texture_2d.h b/modules/noise/tests/test_noise_texture_2d.h index e2ec39ef48..938e8fd6ab 100644 --- a/modules/noise/tests/test_noise_texture_2d.h +++ b/modules/noise/tests/test_noise_texture_2d.h @@ -210,7 +210,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mip noise_texture->set_generate_mipmaps(true); Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp)); MessageQueue::get_singleton()->flush(); } @@ -227,7 +227,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a normal map without mipmaps") noise_texture->set_generate_mipmaps(false); Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map)); MessageQueue::get_singleton()->flush(); } @@ -245,7 +245,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") { SUBCASE("Grayscale(L8) 16x16, with seamless blend skirt of 0.05") { noise_texture->set_seamless_blend_skirt(0.05); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale)); MessageQueue::get_singleton()->flush(); } @@ -257,7 +257,7 @@ TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") { gradient->set_points(points); noise_texture->set_color_ramp(gradient); noise_texture->set_seamless_blend_skirt(1.0); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba)); MessageQueue::get_singleton()->flush(); } } diff --git a/modules/noise/tests/test_noise_texture_3d.h b/modules/noise/tests/test_noise_texture_3d.h index a612f2920a..b708eac43b 100644 --- a/modules/noise/tests/test_noise_texture_3d.h +++ b/modules/noise/tests/test_noise_texture_3d.h @@ -194,7 +194,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mip noise_texture->set_depth(16); Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr())); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp)); MessageQueue::get_singleton()->flush(); } @@ -213,7 +213,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") { SUBCASE("Grayscale(L8) 16x16x16, with seamless blend skirt of 0.05") { noise_texture->set_seamless_blend_skirt(0.05); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale)); MessageQueue::get_singleton()->flush(); } @@ -225,7 +225,7 @@ TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") { gradient->set_points(points); noise_texture->set_color_ramp(gradient); noise_texture->set_seamless_blend_skirt(1.0); - noise_texture->connect("changed", callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba)); + noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba)); MessageQueue::get_singleton()->flush(); } } diff --git a/modules/ogg/doc_classes/OggPacketSequence.xml b/modules/ogg/doc_classes/OggPacketSequence.xml index 2f10bedf69..75dcf5a29f 100644 --- a/modules/ogg/doc_classes/OggPacketSequence.xml +++ b/modules/ogg/doc_classes/OggPacketSequence.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OggPacketSequence" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OggPacketSequence" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A sequence of Ogg packets. </brief_description> diff --git a/modules/ogg/doc_classes/OggPacketSequencePlayback.xml b/modules/ogg/doc_classes/OggPacketSequencePlayback.xml index 338de2d6da..e363e67ee4 100644 --- a/modules/ogg/doc_classes/OggPacketSequencePlayback.xml +++ b/modules/ogg/doc_classes/OggPacketSequencePlayback.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OggPacketSequencePlayback" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OggPacketSequencePlayback" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 0dd41675b6..d5abbe4c72 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -5,22 +5,13 @@ Import("env_modules") env_openxr = env_modules.Clone() -################################################# -# Add in our Khronos OpenXR loader +# Thirdparty source files thirdparty_obj = [] -thirdparty_dir = "#thirdparty/openxr" - -env_openxr.Prepend( - CPPPATH=[ - thirdparty_dir, - thirdparty_dir + "/include", - thirdparty_dir + "/src", - thirdparty_dir + "/src/common", - thirdparty_dir + "/src/external/jsoncpp/include", - ] -) +# Khronos OpenXR loader + +# Needs even for build against shared library, at least the defines used in public headers. if env["platform"] == "android": # may need to set OPENXR_ANDROID_VERSION_SUFFIX env_openxr.AppendUnique(CPPDEFINES=["XR_OS_ANDROID", "XR_USE_PLATFORM_ANDROID"]) @@ -41,43 +32,57 @@ elif env["platform"] == "windows": # may need to check and set: # - XR_USE_TIMESPEC -env_thirdparty = env_openxr.Clone() -env_thirdparty.disable_warnings() -env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"]) - -if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]: - env_thirdparty["CXXFLAGS"].remove("-fno-exceptions") -env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"]) - -# add in external jsoncpp dependency -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_reader.cpp") -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_value.cpp") -env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/src/external/jsoncpp/src/lib_json/json_writer.cpp") - -# add in load -if env["platform"] != "android": - # On Android the openxr_loader is provided by separate plugins for each device - # Build the engine using object files - khrloader_obj = [] - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c") - - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp") - - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp") - env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp") - env.modules_sources += khrloader_obj - -env.modules_sources += thirdparty_obj - -################################################# -# And include our module source +if env["builtin_openxr"]: + thirdparty_dir = "#thirdparty/openxr" + + env_openxr.Prepend( + CPPPATH=[ + thirdparty_dir, + thirdparty_dir + "/include", + thirdparty_dir + "/src", + thirdparty_dir + "/src/common", + thirdparty_dir + "/src/external/jsoncpp/include", + ] + ) + + env_thirdparty = env_openxr.Clone() + env_thirdparty.disable_warnings() + env_thirdparty.AppendUnique(CPPDEFINES=["DISABLE_STD_FILESYSTEM"]) + + if "-fno-exceptions" in env_thirdparty["CXXFLAGS"]: + env_thirdparty["CXXFLAGS"].remove("-fno-exceptions") + env_thirdparty.Append(CPPPATH=[thirdparty_dir + "/src/loader"]) + + # add in external jsoncpp dependency + thirdparty_jsoncpp_dir = thirdparty_dir + "/src/external/jsoncpp/src/lib_json/" + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_reader.cpp") + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_value.cpp") + env_thirdparty.add_source_files(thirdparty_obj, thirdparty_jsoncpp_dir + "json_writer.cpp") + + # add in load + if env["platform"] != "android": + # On Android the openxr_loader is provided by separate plugins for each device + # Build the engine using object files + khrloader_obj = [] + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c") + + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp") + + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/api_layer_interface.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_core.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_instance.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger_recorders.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/loader_logger.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/manifest_file.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/runtime_interface.cpp") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/loader/xr_generated_loader.cpp") + env.modules_sources += khrloader_obj + + env.modules_sources += thirdparty_obj + + +# Godot source files module_obj = [] diff --git a/modules/openxr/doc_classes/OpenXRAction.xml b/modules/openxr/doc_classes/OpenXRAction.xml index 2696be2465..6a3529e43e 100644 --- a/modules/openxr/doc_classes/OpenXRAction.xml +++ b/modules/openxr/doc_classes/OpenXRAction.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRAction" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRAction" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> An OpenXR action. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRActionMap.xml b/modules/openxr/doc_classes/OpenXRActionMap.xml index 64cd5ca494..b8711635e4 100644 --- a/modules/openxr/doc_classes/OpenXRActionMap.xml +++ b/modules/openxr/doc_classes/OpenXRActionMap.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRActionMap" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRActionMap" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Collection of [OpenXRActionSet] and [OpenXRInteractionProfile] resources for the OpenXR module. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRActionSet.xml b/modules/openxr/doc_classes/OpenXRActionSet.xml index 92e6f91e32..03dc33d743 100644 --- a/modules/openxr/doc_classes/OpenXRActionSet.xml +++ b/modules/openxr/doc_classes/OpenXRActionSet.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRActionSet" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRActionSet" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Collection of [OpenXRAction] resources that make up an action set. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml index 6e258a468d..cc7766507f 100644 --- a/modules/openxr/doc_classes/OpenXRHand.xml +++ b/modules/openxr/doc_classes/OpenXRHand.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRHand" inherits="Node3D" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRHand" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Node supporting finger tracking in OpenXR. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRIPBinding.xml b/modules/openxr/doc_classes/OpenXRIPBinding.xml index f3b14b3179..f274f0868e 100644 --- a/modules/openxr/doc_classes/OpenXRIPBinding.xml +++ b/modules/openxr/doc_classes/OpenXRIPBinding.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRIPBinding" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRIPBinding" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Defines a binding between an [OpenXRAction] and an XR input or output. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml index 8c55a3360b..a69bb875fa 100644 --- a/modules/openxr/doc_classes/OpenXRInteractionProfile.xml +++ b/modules/openxr/doc_classes/OpenXRInteractionProfile.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRInteractionProfile" inherits="Resource" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRInteractionProfile" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Suggested bindings object for OpenXR. </brief_description> diff --git a/modules/openxr/doc_classes/OpenXRInterface.xml b/modules/openxr/doc_classes/OpenXRInterface.xml index b6d52464c0..f0ca649fe4 100644 --- a/modules/openxr/doc_classes/OpenXRInterface.xml +++ b/modules/openxr/doc_classes/OpenXRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="OpenXRInterface" inherits="XRInterface" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="OpenXRInterface" inherits="XRInterface" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Our OpenXR interface. </brief_description> diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h index 920bfe74b7..31f8d23268 100644 --- a/modules/openxr/extensions/openxr_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_extension_wrapper.h @@ -36,8 +36,6 @@ #include "core/templates/hash_map.h" #include "core/templates/rid.h" -#include "thirdparty/openxr/src/common/xr_linear.h" - #include <openxr/openxr.h> class OpenXRAPI; diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp index 569895a620..f730f2bd2c 100644 --- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp +++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.cpp @@ -107,10 +107,6 @@ bool OpenXRFbPassthroughExtensionWrapper::is_passthrough_enabled() { return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && passthrough_layer != XR_NULL_HANDLE; } -bool OpenXRFbPassthroughExtensionWrapper::is_composition_passthrough_layer_ready() { - return fb_passthrough_ext && passthrough_handle != XR_NULL_HANDLE && composition_passthrough_layer.layerHandle != XR_NULL_HANDLE; -} - bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() { if (passthrough_handle == XR_NULL_HANDLE) { return false; @@ -128,6 +124,13 @@ bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() { } // Create the passthrough layer + XrPassthroughLayerCreateInfoFB passthrough_layer_config = { + XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, + nullptr, + passthrough_handle, + XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB, + XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, + }; result = xrCreatePassthroughLayerFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_layer_config, &passthrough_layer); if (!is_valid_passthrough_result(result, "Failed to create the passthrough layer")) { stop_passthrough(); @@ -140,14 +143,18 @@ bool OpenXRFbPassthroughExtensionWrapper::start_passthrough() { print_error("Main viewport doesn't have transparent background! Passthrough may not properly render."); } - composition_passthrough_layer.layerHandle = passthrough_layer; - return true; } void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession session) { if (fb_passthrough_ext) { // Create the passthrough feature and start it. + XrPassthroughCreateInfoFB passthrough_create_info = { + XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, + nullptr, + 0, + }; + XrResult result = xrCreatePassthroughFB(OpenXRAPI::get_singleton()->get_session(), &passthrough_create_info, &passthrough_handle); if (!OpenXRAPI::get_singleton()->xr_result(result, "Failed to create passthrough")) { passthrough_handle = XR_NULL_HANDLE; @@ -157,7 +164,8 @@ void OpenXRFbPassthroughExtensionWrapper::on_session_created(const XrSession ses } XrCompositionLayerBaseHeader *OpenXRFbPassthroughExtensionWrapper::get_composition_layer() { - if (is_composition_passthrough_layer_ready()) { + if (is_passthrough_enabled()) { + composition_passthrough_layer.layerHandle = passthrough_layer; return (XrCompositionLayerBaseHeader *)&composition_passthrough_layer; } else { return nullptr; @@ -169,8 +177,6 @@ void OpenXRFbPassthroughExtensionWrapper::stop_passthrough() { return; } - composition_passthrough_layer.layerHandle = XR_NULL_HANDLE; - XrResult result; if (passthrough_layer != XR_NULL_HANDLE) { // Destroy the layer diff --git a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h index 619313809d..045e424202 100644 --- a/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h +++ b/modules/openxr/extensions/openxr_fb_passthrough_extension_wrapper.h @@ -211,27 +211,12 @@ private: Viewport *get_main_viewport(); - bool is_composition_passthrough_layer_ready(); - static OpenXRFbPassthroughExtensionWrapper *singleton; bool fb_passthrough_ext = false; // required for any passthrough functionality bool fb_triangle_mesh_ext = false; // only use for projected passthrough - XrPassthroughCreateInfoFB passthrough_create_info = { - XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, - nullptr, - 0, - }; XrPassthroughFB passthrough_handle = XR_NULL_HANDLE; - - XrPassthroughLayerCreateInfoFB passthrough_layer_config = { - XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, - nullptr, - passthrough_handle, - XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB, - XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, - }; XrPassthroughLayerFB passthrough_layer = XR_NULL_HANDLE; XrCompositionLayerPassthroughFB composition_passthrough_layer = { diff --git a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp index 6fffa1ed07..65559afed0 100644 --- a/modules/openxr/extensions/openxr_hand_tracking_extension.cpp +++ b/modules/openxr/extensions/openxr_hand_tracking_extension.cpp @@ -134,6 +134,10 @@ void OpenXRHandTrackingExtension::on_process() { // process our hands const XrTime time = OpenXRAPI::get_singleton()->get_next_frame_time(); // This data will be used for the next frame we render + if (time == 0) { + // we don't have timing info yet, or we're skipping a frame... + return; + } XrResult result; diff --git a/modules/openxr/extensions/openxr_opengl_extension.cpp b/modules/openxr/extensions/openxr_opengl_extension.cpp index 39b5c61e8e..9038e9f458 100644 --- a/modules/openxr/extensions/openxr_opengl_extension.cpp +++ b/modules/openxr/extensions/openxr_opengl_extension.cpp @@ -278,8 +278,8 @@ bool OpenXROpenGLExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in } bool OpenXROpenGLExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { - XrMatrix4x4f matrix; - XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); + OpenXRUtil::XrMatrix4x4f matrix; + OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); for (int j = 0; j < 4; j++) { for (int i = 0; i < 4; i++) { diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp index 90c1c62050..9429d9e082 100644 --- a/modules/openxr/extensions/openxr_vulkan_extension.cpp +++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp @@ -233,9 +233,9 @@ void OpenXRVulkanExtension::get_usable_swapchain_formats(Vector<int64_t> &p_usab } void OpenXRVulkanExtension::get_usable_depth_formats(Vector<int64_t> &p_usable_swap_chains) { - p_usable_swap_chains.push_back(VK_FORMAT_R32_SFLOAT); p_usable_swap_chains.push_back(VK_FORMAT_D24_UNORM_S8_UINT); p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT_S8_UINT); + p_usable_swap_chains.push_back(VK_FORMAT_D32_SFLOAT); } bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, int64_t p_swapchain_format, uint32_t p_width, uint32_t p_height, uint32_t p_sample_count, uint32_t p_array_size, void **r_swapchain_graphics_data) { @@ -308,8 +308,8 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in format = RenderingDevice::DATA_FORMAT_B8G8R8A8_UINT; usage_flags |= RenderingDevice::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; break; - case VK_FORMAT_R32_SFLOAT: - format = RenderingDevice::DATA_FORMAT_R32_SFLOAT; + case VK_FORMAT_D32_SFLOAT: + format = RenderingDevice::DATA_FORMAT_D32_SFLOAT; usage_flags |= RenderingDevice::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; break; case VK_FORMAT_D24_UNORM_S8_UINT: @@ -381,8 +381,8 @@ bool OpenXRVulkanExtension::get_swapchain_image_data(XrSwapchain p_swapchain, in bool OpenXRVulkanExtension::create_projection_fov(const XrFovf p_fov, double p_z_near, double p_z_far, Projection &r_camera_matrix) { // Even though this is a Vulkan renderer we're using OpenGL coordinate systems - XrMatrix4x4f matrix; - XrMatrix4x4f_CreateProjectionFov(&matrix, GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); + OpenXRUtil::XrMatrix4x4f matrix; + OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(&matrix, OpenXRUtil::GRAPHICS_OPENGL, p_fov, (float)p_z_near, (float)p_z_far); for (int j = 0; j < 4; j++) { for (int i = 0; i < 4; i++) { diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp index 21d9203ead..4ab280f3c3 100644 --- a/modules/openxr/openxr_api.cpp +++ b/modules/openxr/openxr_api.cpp @@ -855,18 +855,19 @@ bool OpenXRAPI::create_swapchains() { } if (swapchain_format_to_use == 0) { - swapchain_format_to_use = usable_swapchain_formats[0]; // just use the first one and hope for the best... - print_line("Couldn't find usable depth swap chain format, using", get_swapchain_format_name(swapchain_format_to_use), "instead."); + print_line("Couldn't find usable depth swap chain format, depth buffer will not be submitted."); } else { print_verbose(String("Using depth swap chain format:") + get_swapchain_format_name(swapchain_format_to_use)); - } - if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) { - return false; - } + // Note, if VK_FORMAT_D32_SFLOAT is used here but we're using the forward+ renderer, we should probably output a warning. - depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count); - ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views"); + if (!create_swapchain(XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, swapchain_format_to_use, recommended_size.width, recommended_size.height, view_configuration_views[0].recommendedSwapchainSampleCount, view_count, swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain, &swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data)) { + return false; + } + + depth_views = (XrCompositionLayerDepthInfoKHR *)memalloc(sizeof(XrCompositionLayerDepthInfoKHR) * view_count); + ERR_FAIL_NULL_V_MSG(depth_views, false, "OpenXR Couldn't allocate memory for depth views"); + } } // We create our velocity swapchain if: @@ -1837,6 +1838,7 @@ RID OpenXRAPI::get_color_texture() { } RID OpenXRAPI::get_depth_texture() { + // Note, image will not be acquired if we didn't have a suitable swap chain format. if (submit_depth_buffer && swapchains[OPENXR_SWAPCHAIN_DEPTH].image_acquired) { return graphics_extension->get_texture(swapchains[OPENXR_SWAPCHAIN_DEPTH].swapchain_graphics_data, swapchains[OPENXR_SWAPCHAIN_DEPTH].image_index); } else { diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h index 96af2bfc49..9374cb7afa 100644 --- a/modules/openxr/openxr_api.h +++ b/modules/openxr/openxr_api.h @@ -48,8 +48,6 @@ #include "core/templates/vector.h" #include "servers/xr/xr_pose.h" -#include "thirdparty/openxr/src/common/xr_linear.h" - #include <openxr/openxr.h> // Note, OpenXR code that we wrote for our plugin makes use of C++20 notation for initializing structs which ensures zeroing out unspecified members. diff --git a/modules/openxr/openxr_util.cpp b/modules/openxr/openxr_util.cpp index 0c5cdd7113..1d44233337 100644 --- a/modules/openxr/openxr_util.cpp +++ b/modules/openxr/openxr_util.cpp @@ -32,6 +32,8 @@ #include <openxr/openxr_reflection.h> +#include <math.h> + #define XR_ENUM_CASE_STR(name, val) \ case name: \ return #name; @@ -75,3 +77,89 @@ String OpenXRUtil::make_xr_version_string(XrVersion p_version) { return version; } + +// Copied from OpenXR xr_linear.h private header, so we can still link against +// system-provided packages without relying on our `thirdparty` code. + +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2016 Oculus VR, LLC. +// +// SPDX-License-Identifier: Apache-2.0 + +// Creates a projection matrix based on the specified dimensions. +// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API. +// The far plane is placed at infinity if farZ <= nearZ. +// An infinite projection matrix is preferred for rasterization because, except for +// things *right* up against the near plane, it always provides better precision: +// "Tightening the Precision of Perspective Rendering" +// Paul Upchurch, Mathieu Desbrun +// Journal of Graphics Tools, Volume 16, Issue 1, 2012 +void OpenXRUtil::XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft, + const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, + const float nearZ, const float farZ) { + const float tanAngleWidth = tanAngleRight - tanAngleLeft; + + // Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan). + // Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal). + const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown); + + // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). + // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). + const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0; + + if (farZ <= nearZ) { + // place the far plane at infinity + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -1.0f; + result->m[14] = -(nearZ + offsetZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } else { + // normal projection + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -(farZ + offsetZ) / (farZ - nearZ); + result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } +} + +// Creates a projection matrix based on the specified FOV. +void OpenXRUtil::XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov, + const float nearZ, const float farZ) { + const float tanLeft = tanf(fov.angleLeft); + const float tanRight = tanf(fov.angleRight); + + const float tanDown = tanf(fov.angleDown); + const float tanUp = tanf(fov.angleUp); + + XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ); +} diff --git a/modules/openxr/openxr_util.h b/modules/openxr/openxr_util.h index 8ad68c0b02..3f36ab9fca 100644 --- a/modules/openxr/openxr_util.h +++ b/modules/openxr/openxr_util.h @@ -44,6 +44,27 @@ public: static String get_action_type_name(XrActionType p_action_type); static String get_environment_blend_mode_name(XrEnvironmentBlendMode p_blend_mode); static String make_xr_version_string(XrVersion p_version); + + // Copied from OpenXR xr_linear.h private header, so we can still link against + // system-provided packages without relying on our `thirdparty` code. + + // Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience. + typedef struct XrMatrix4x4f { + float m[16]; + } XrMatrix4x4f; + + typedef enum GraphicsAPI { + GRAPHICS_VULKAN, + GRAPHICS_OPENGL, + GRAPHICS_OPENGL_ES, + GRAPHICS_D3D + } GraphicsAPI; + + static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const float tanAngleLeft, + const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, + const float nearZ, const float farZ); + static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f *result, GraphicsAPI graphicsApi, const XrFovf fov, + const float nearZ, const float farZ); }; #endif // OPENXR_UTIL_H diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp index 69fbf87483..eee0de967e 100644 --- a/modules/raycast/raycast_occlusion_cull.cpp +++ b/modules/raycast/raycast_occlusion_cull.cpp @@ -355,41 +355,14 @@ void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_in // Embree requires the last element to be readable by a 16-byte SSE load instruction, so we add padding to be safe. occ_inst->xformed_vertices.resize(vertices_size + 1); - const Vector3 *read_ptr = occ->vertices.ptr(); - Vector3 *write_ptr = occ_inst->xformed_vertices.ptr(); - - if (vertices_size > 1024) { - TransformThreadData td; - td.xform = occ_inst->xform; - td.read = read_ptr; - td.write = write_ptr; - td.vertex_count = vertices_size; - td.thread_count = WorkerThreadPool::get_singleton()->get_thread_count(); - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &Scenario::_transform_vertices_thread, &td, td.thread_count, -1, true, SNAME("RaycastOcclusionCull")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); - - } else { - _transform_vertices_range(read_ptr, write_ptr, occ_inst->xform, 0, vertices_size); - } + for_range(0, vertices_size, vertices_size > 1024, SNAME("RaycastOcclusionCull"), [&](const int i) { + occ_inst->xformed_vertices[i] = occ_inst->xform.xform(occ->vertices[i]); + }); occ_inst->indices.resize(occ->indices.size()); memcpy(occ_inst->indices.ptr(), occ->indices.ptr(), occ->indices.size() * sizeof(int32_t)); } -void RaycastOcclusionCull::Scenario::_transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data) { - uint32_t vertex_total = p_data->vertex_count; - uint32_t total_threads = p_data->thread_count; - uint32_t from = p_thread * vertex_total / total_threads; - uint32_t to = (p_thread + 1 == total_threads) ? vertex_total : ((p_thread + 1) * vertex_total / total_threads); - _transform_vertices_range(p_data->read, p_data->write, p_data->xform, from, to); -} - -void RaycastOcclusionCull::Scenario::_transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to) { - for (int i = p_from; i < p_to; i++) { - p_write[i] = p_xform.xform(p_read[i]); - } -} - void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) { Scenario *scenario = (Scenario *)p_ud; int commit_idx = 1 - (scenario->current_scene_idx); diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h index c4e733b664..7a5346878b 100644 --- a/modules/raycast/raycast_occlusion_cull.h +++ b/modules/raycast/raycast_occlusion_cull.h @@ -121,14 +121,6 @@ private: const uint32_t *masks; }; - struct TransformThreadData { - uint32_t thread_count; - uint32_t vertex_count; - Transform3D xform; - const Vector3 *read; - Vector3 *write = nullptr; - }; - Thread *commit_thread = nullptr; bool commit_done = true; bool dirty = false; @@ -144,8 +136,6 @@ private: void _update_dirty_instance_thread(int p_idx, RID *p_instances); void _update_dirty_instance(int p_idx, RID *p_instances); - void _transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data); - void _transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to); static void _commit_scene(void *p_ud); bool update(); diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml index 79c5bfe1b0..5770e7155e 100644 --- a/modules/regex/doc_classes/RegEx.xml +++ b/modules/regex/doc_classes/RegEx.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegEx" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="RegEx" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Class for searching text for patterns using regular expressions. </brief_description> diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml index fab51f1e6b..82182f0de5 100644 --- a/modules/regex/doc_classes/RegExMatch.xml +++ b/modules/regex/doc_classes/RegExMatch.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="RegExMatch" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="RegExMatch" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Contains the results of a [RegEx] search. </brief_description> diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index ad7feeda49..7639155914 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -67,12 +67,22 @@ void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_colo } } -Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { +Ref<Image> ImageLoaderSVG::load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale) { + Ref<Image> img; + img.instantiate(); + + Error err = create_image_from_utf8_buffer(img, p_svg, p_size, p_scale, false); + ERR_FAIL_COND_V(err, Ref<Image>()); + + return img; +} + +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample) { ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0."); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); - tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true); + tvg::Result result = picture->load((const char *)p_buffer, p_buffer_size, "svg", true); if (result != tvg::Result::Success) { return ERR_INVALID_DATA; } @@ -142,6 +152,10 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const Pa return OK; } +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { + return create_image_from_utf8_buffer(p_image, p_buffer.ptr(), p_buffer.size(), p_scale, p_upsample); +} + Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) { if (p_color_map.size()) { _replace_color_property(p_color_map, "stop-color=\"", p_string); @@ -179,3 +193,7 @@ Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileacces } return OK; } + +ImageLoaderSVG::ImageLoaderSVG() { + Image::_svg_scalable_mem_loader_func = load_mem_svg; +} diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index e0d2849a62..90e9458c37 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -36,16 +36,22 @@ class ImageLoaderSVG : public ImageFormatLoader { static HashMap<Color, Color> forced_color_map; - void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string); + static void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string); + + static Ref<Image> load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale); public: static void set_forced_color_map(const HashMap<Color, Color> &p_color_map); - Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample); - Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map); + static Error create_image_from_utf8_buffer(Ref<Image> p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample); + static Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample); + + static Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map); virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override; virtual void get_recognized_extensions(List<String> *p_extensions) const override; + + ImageLoaderSVG(); }; #endif // IMAGE_LOADER_SVG_H diff --git a/modules/text_server_adv/doc_classes/TextServerAdvanced.xml b/modules/text_server_adv/doc_classes/TextServerAdvanced.xml index 3b2128b281..b493255917 100644 --- a/modules/text_server_adv/doc_classes/TextServerAdvanced.xml +++ b/modules/text_server_adv/doc_classes/TextServerAdvanced.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextServerAdvanced" inherits="TextServerExtension" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="TextServerAdvanced" inherits="TextServerExtension" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> An advanced text server with support for BiDi, complex text layout, and contextual OpenType features. Used in Godot by default. </brief_description> diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index 82fbae5669..af9dae84e3 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -227,7 +227,6 @@ if env["freetype_enabled"]: "compress.c", "crc32.c", "deflate.c", - "infback.c", "inffast.c", "inflate.c", "inftrees.c", diff --git a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension index c12fcfdfdf..bcf3661a8c 100644 --- a/modules/text_server_adv/gdextension_build/text_server_adv.gdextension +++ b/modules/text_server_adv/gdextension_build/text_server_adv.gdextension @@ -1,6 +1,7 @@ [configuration] entry_symbol = "textserver_advanced_init" +compatibility_minimum = 4.1 [libraries] diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp index 51a4d8171e..ba67fef70f 100644 --- a/modules/text_server_adv/register_types.cpp +++ b/modules/text_server_adv/register_types.cpp @@ -62,8 +62,8 @@ using namespace godot; extern "C" { -GDExtensionBool GDE_EXPORT textserver_advanced_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { - GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization); +GDExtensionBool GDE_EXPORT textserver_advanced_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { + GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); init_obj.register_initializer(&initialize_text_server_adv_module); init_obj.register_terminator(&uninitialize_text_server_adv_module); diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index eb0cb54caf..13d8a2c17a 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -52,6 +52,7 @@ using namespace godot; #include "core/object/worker_thread_pool.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "scene/resources/image_texture.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. @@ -2112,10 +2113,12 @@ Dictionary TextServerAdvanced::_font_get_ot_name_strings(const RID &p_font_rid) name = vformat("unknown_%d", names[i].name_id); } break; } - String text; unsigned int text_size = hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, nullptr, nullptr) + 1; - text.resize(text_size); - hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)text.ptrw()); + // @todo After godot-cpp#1141 is fixed, use text.resize() and write directly to text.wptr() instead of using a temporary buffer. + char32_t *buffer = memnew_arr(char32_t, text_size); + hb_ot_name_get_utf32(hb_face, names[i].name_id, names[i].language, &text_size, (uint32_t *)buffer); + String text(buffer); + memdelete_arr(buffer); if (!text.is_empty()) { Dictionary &id_string = names_for_lang[String(hb_language_to_string(names[i].language))]; id_string[name] = text; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index aba727edaa..44700e045b 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -85,7 +85,7 @@ using namespace godot; #include "core/object/worker_thread_pool.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" -#include "scene/resources/texture.h" +#include "scene/resources/image_texture.h" #include "servers/text/text_server_extension.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index c227f35561..2f48f1564c 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -121,7 +121,12 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin for (int i = 0; i < parser->get_attribute_count(); i++) { xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i)); } - xml_body += ">"; + + if (parser->is_empty()) { + xml_body += "/>"; + } else { + xml_body += ">"; + } } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { xml_body += parser->get_node_data(); } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { @@ -150,10 +155,21 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin } String xml_code_str = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"" + rtos(min_x) + " " + rtos(min_y) + " " + rtos(new_w) + " " + rtos(new_h) + "\">" + xml_body; +#ifndef GDEXTENSION gl_state.xml_code = xml_code_str.utf8(); +#else + CharString xml_code = xml_code_str.utf8(); + gl_state.xml_code_length = xml_code.length(); + gl_state.xml_code = memnew_arr(char, gl_state.xml_code_length); + memcpy(gl_state.xml_code, xml_code.get_data(), gl_state.xml_code_length); +#endif picture = tvg::Picture::gen(); +#ifndef GDEXTENSION result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); +#else + result = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); +#endif if (result != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); } @@ -241,7 +257,11 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { ERR_FAIL_COND_V_MSG(!gl_state.ready, FT_Err_Invalid_SVG_Document, "SVG glyph not ready."); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); +#ifndef GDEXTENSION tvg::Result res = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); +#else + tvg::Result res = picture->load(gl_state.xml_code, gl_state.xml_code_length, "svg+xml", false); +#endif if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); } diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h index 034fffb5e6..5e79c8e444 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -66,8 +66,22 @@ struct GL_State { float y = 0; float w = 0; float h = 0; +#ifndef GDEXTENSION CharString xml_code; +#else + // @todo After godot-cpp#1142 is fixed, use CharString just like when compiled as a Godot module. + char *xml_code = nullptr; + int xml_code_length = 0; +#endif tvg::Matrix m; + +#ifdef GDEXTENSION + ~GL_State() { + if (xml_code) { + memdelete_arr(xml_code); + } + } +#endif }; struct TVG_State { diff --git a/modules/text_server_fb/doc_classes/TextServerFallback.xml b/modules/text_server_fb/doc_classes/TextServerFallback.xml index ddd05fa445..a9a98f8e0d 100644 --- a/modules/text_server_fb/doc_classes/TextServerFallback.xml +++ b/modules/text_server_fb/doc_classes/TextServerFallback.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="TextServerFallback" inherits="TextServerExtension" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="TextServerFallback" inherits="TextServerExtension" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A fallback implementation of Godot's text server, without support for BiDi and complex text layout. </brief_description> diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 7b4c548a21..51a6ee06be 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -222,7 +222,6 @@ if env["freetype_enabled"]: "compress.c", "crc32.c", "deflate.c", - "infback.c", "inffast.c", "inflate.c", "inftrees.c", diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index d81b50779e..54311caaf9 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -83,7 +83,7 @@ using namespace godot; #include "core/object/worker_thread_pool.h" #include "core/templates/hash_map.h" #include "core/templates/rid_owner.h" -#include "scene/resources/texture.h" +#include "scene/resources/image_texture.h" #include "servers/text/text_server_extension.h" #include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg. diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index cd0ecd9b90..773b103c01 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -121,7 +121,12 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin for (int i = 0; i < parser->get_attribute_count(); i++) { xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i)); } - xml_body += ">"; + + if (parser->is_empty()) { + xml_body += "/>"; + } else { + xml_body += ">"; + } } else if (parser->get_node_type() == XMLParser::NODE_TEXT) { xml_body += parser->get_node_data(); } else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END) { diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml index e479963ca1..4dfc529cef 100644 --- a/modules/theora/doc_classes/VideoStreamTheora.xml +++ b/modules/theora/doc_classes/VideoStreamTheora.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="VideoStreamTheora" inherits="VideoStream" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="VideoStreamTheora" inherits="VideoStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> [VideoStream] resource for Ogg Theora videos. </brief_description> diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 6c961813b4..d964fd7627 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/os/os.h" +#include "scene/resources/image_texture.h" #ifdef _MSC_VER #pragma warning(push) diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h index 32adc28a88..21d4caef45 100644 --- a/modules/theora/video_stream_theora.h +++ b/modules/theora/video_stream_theora.h @@ -43,6 +43,8 @@ #include <theora/theoradec.h> #include <vorbis/codec.h> +class ImageTexture; + //#define THEORA_USE_THREAD_STREAMING class VideoStreamPlaybackTheora : public VideoStreamPlayback { diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml index a099a193bb..7eba3ad8ec 100644 --- a/modules/upnp/doc_classes/UPNP.xml +++ b/modules/upnp/doc_classes/UPNP.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="UPNP" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="UPNP" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Universal Plug and Play (UPnP) functions for network device discovery, querying and port forwarding. </brief_description> diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml index 98e301eb4c..a70ae1b9cc 100644 --- a/modules/upnp/doc_classes/UPNPDevice.xml +++ b/modules/upnp/doc_classes/UPNPDevice.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="UPNPDevice" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="UPNPDevice" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Universal Plug and Play (UPnP) device. </brief_description> diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp index fcd717cfec..b54335b724 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp @@ -33,6 +33,7 @@ #include "core/io/file_access.h" #include "core/variant/typed_array.h" +#include "modules/vorbis/resource_importer_ogg_vorbis.h" #include <ogg/ogg.h> int AudioStreamPlaybackOggVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) { @@ -520,6 +521,9 @@ bool AudioStreamOggVorbis::is_monophonic() const { } void AudioStreamOggVorbis::_bind_methods() { + ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_buffer", "buffer"), &AudioStreamOggVorbis::load_from_buffer); + ClassDB::bind_static_method("AudioStreamOggVorbis", D_METHOD("load_from_file", "path"), &AudioStreamOggVorbis::load_from_file); + ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOggVorbis::set_packet_sequence); ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOggVorbis::get_packet_sequence); @@ -549,3 +553,11 @@ void AudioStreamOggVorbis::_bind_methods() { AudioStreamOggVorbis::AudioStreamOggVorbis() {} AudioStreamOggVorbis::~AudioStreamOggVorbis() {} + +Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) { + return ResourceImporterOggVorbis::load_from_buffer(file_data); +} + +Ref<AudioStreamOggVorbis> AudioStreamOggVorbis::load_from_file(const String &p_path) { + return ResourceImporterOggVorbis::load_from_file(p_path); +} diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h index c76df7f84d..41ce942eec 100644 --- a/modules/vorbis/audio_stream_ogg_vorbis.h +++ b/modules/vorbis/audio_stream_ogg_vorbis.h @@ -125,6 +125,8 @@ protected: static void _bind_methods(); public: + static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path); + static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data); void set_loop(bool p_enable); virtual bool has_loop() const override; diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py index a231ef179d..9e10a58849 100644 --- a/modules/vorbis/config.py +++ b/modules/vorbis/config.py @@ -11,6 +11,7 @@ def get_doc_classes(): return [ "AudioStreamOggVorbis", "AudioStreamPlaybackOggVorbis", + "ResourceImporterOggVorbis", ] diff --git a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml index 4551d395df..7e3af6688a 100644 --- a/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamOggVorbis.xml @@ -1,11 +1,29 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamOggVorbis" inherits="AudioStream" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="AudioStreamOggVorbis" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> + A class representing an Ogg Vorbis audio stream. </brief_description> <description> + The AudioStreamOggVorbis class is a specialized [AudioStream] for handling Ogg Vorbis file formats. It offers functionality for loading and playing back Ogg Vorbis files, as well as managing looping and other playback properties. This class is part of the audio stream system, which also supports WAV files through the [AudioStreamWAV] class. </description> <tutorials> </tutorials> + <methods> + <method name="load_from_buffer" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + Creates a new AudioStreamOggVorbis instance from the given buffer. The buffer must contain Ogg Vorbis data. + </description> + </method> + <method name="load_from_file" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="path" type="String" /> + <description> + Creates a new AudioStreamOggVorbis instance from the given file path. The file must be in Ogg Vorbis format. + </description> + </method> + </methods> <members> <member name="bar_beats" type="int" setter="set_bar_beats" getter="get_bar_beats" default="4"> </member> @@ -14,7 +32,7 @@ <member name="bpm" type="float" setter="set_bpm" getter="get_bpm" default="0.0"> </member> <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false"> - If [code]true[/code], the stream will automatically loop when it reaches the end. + If [code]true[/code], the audio will play again from the specified [member loop_offset] once it is done playing. Useful for ambient sounds and background music. </member> <member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0"> Time in seconds at which the stream starts after being looped. diff --git a/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml b/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml index 6b43bd1171..54a60262a7 100644 --- a/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml +++ b/modules/vorbis/doc_classes/AudioStreamPlaybackOggVorbis.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="AudioStreamPlaybackOggVorbis" inherits="AudioStreamPlaybackResampled" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="AudioStreamPlaybackOggVorbis" inherits="AudioStreamPlaybackResampled" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml new file mode 100644 index 0000000000..10c87b899f --- /dev/null +++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="ResourceImporterOggVorbis" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + </brief_description> + <description> + </description> + <tutorials> + </tutorials> + <methods> + <method name="load_from_buffer" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="buffer" type="PackedByteArray" /> + <description> + This method loads audio data from a PackedByteArray buffer into an AudioStreamOggVorbis object. + </description> + </method> + <method name="load_from_file" qualifiers="static"> + <return type="AudioStreamOggVorbis" /> + <param index="0" name="path" type="String" /> + <description> + This method loads audio data from a file into an AudioStreamOggVorbis object. The file path is provided as a string. + </description> + </method> + </methods> + <members> + <member name="bar_beats" type="int" setter="" getter="" default="4"> + </member> + <member name="beat_count" type="int" setter="" getter="" default="0"> + </member> + <member name="bpm" type="float" setter="" getter="" default="0"> + </member> + <member name="loop" type="bool" setter="" getter="" default="false"> + If [code]true[/code], the audio will play again from the specified [member loop_offset] once it is done playing. Useful for ambient sounds and background music. + </member> + <member name="loop_offset" type="float" setter="" getter="" default="0"> + </member> + </members> +</class> diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp index e131ff6dc9..26af912999 100644 --- a/modules/vorbis/register_types.cpp +++ b/modules/vorbis/register_types.cpp @@ -31,7 +31,10 @@ #include "register_types.h" #include "audio_stream_ogg_vorbis.h" + +#ifdef TOOLS_ENABLED #include "resource_importer_ogg_vorbis.h" +#endif void initialize_vorbis_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { @@ -44,7 +47,11 @@ void initialize_vorbis_module(ModuleInitializationLevel p_level) { ogg_vorbis_importer.instantiate(); ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer); } + + // Required to document import options in the class reference. + GDREGISTER_CLASS(ResourceImporterOggVorbis); #endif + GDREGISTER_CLASS(AudioStreamOggVorbis); GDREGISTER_CLASS(AudioStreamPlaybackOggVorbis); } diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp index 8392750798..b42cd20589 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.cpp +++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp @@ -81,18 +81,50 @@ void ResourceImporterOggVorbis::get_import_options(const String &p_path, List<Im r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "bar_beats", PROPERTY_HINT_RANGE, "2,32,or_greater"), 4)); } -Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const String &p_path) { - Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(f.is_null(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'."); +#ifdef TOOLS_ENABLED + +bool ResourceImporterOggVorbis::has_advanced_options() const { + return true; +} + +void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) { + Ref<AudioStreamOggVorbis> ogg_stream = load_from_file(p_path); + if (ogg_stream.is_valid()) { + AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream); + } +} +#endif + +Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + bool loop = p_options["loop"]; + float loop_offset = p_options["loop_offset"]; + double bpm = p_options["bpm"]; + int beat_count = p_options["beat_count"]; + int bar_beats = p_options["bar_beats"]; - uint64_t len = f->get_length(); + Ref<AudioStreamOggVorbis> ogg_vorbis_stream = load_from_file(p_source_file); + if (ogg_vorbis_stream.is_null()) { + return ERR_CANT_OPEN; + } - Vector<uint8_t> file_data; - file_data.resize(len); - uint8_t *w = file_data.ptrw(); + ogg_vorbis_stream->set_loop(loop); + ogg_vorbis_stream->set_loop_offset(loop_offset); + ogg_vorbis_stream->set_bpm(bpm); + ogg_vorbis_stream->set_beat_count(beat_count); + ogg_vorbis_stream->set_bar_beats(bar_beats); + + return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr"); +} - f->get_buffer(w, len); +ResourceImporterOggVorbis::ResourceImporterOggVorbis() { +} + +void ResourceImporterOggVorbis::_bind_methods() { + ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_buffer", "buffer"), &ResourceImporterOggVorbis::load_from_buffer); + ClassDB::bind_static_method("ResourceImporterOggVorbis", D_METHOD("load_from_file", "path"), &ResourceImporterOggVorbis::load_from_file); +} +Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vector<uint8_t> &file_data) { Ref<AudioStreamOggVorbis> ogg_vorbis_stream; ogg_vorbis_stream.instantiate(); @@ -114,7 +146,7 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str err = ogg_sync_check(&sync_state); ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err)); while (ogg_sync_pageout(&sync_state, &page) != 1) { - if (cursor >= len) { + if (cursor >= size_t(file_data.size())) { done = true; break; } @@ -123,8 +155,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE); err = ogg_sync_check(&sync_state); ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err)); - ERR_FAIL_COND_V(cursor > len, Ref<AudioStreamOggVorbis>()); - size_t copy_size = len - cursor; + ERR_FAIL_COND_V(cursor > size_t(file_data.size()), Ref<AudioStreamOggVorbis>()); + size_t copy_size = file_data.size() - cursor; if (copy_size > OGG_SYNC_BUFFER_SIZE) { copy_size = OGG_SYNC_BUFFER_SIZE; } @@ -201,40 +233,8 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::import_ogg_vorbis(const Str return ogg_vorbis_stream; } -#ifdef TOOLS_ENABLED - -bool ResourceImporterOggVorbis::has_advanced_options() const { - return true; -} - -void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) { - Ref<AudioStreamOggVorbis> ogg_stream = import_ogg_vorbis(p_path); - if (ogg_stream.is_valid()) { - AudioStreamImportSettings::get_singleton()->edit(p_path, "oggvorbisstr", ogg_stream); - } -} -#endif - -Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { - bool loop = p_options["loop"]; - float loop_offset = p_options["loop_offset"]; - double bpm = p_options["bpm"]; - int beat_count = p_options["beat_count"]; - int bar_beats = p_options["bar_beats"]; - - Ref<AudioStreamOggVorbis> ogg_vorbis_stream = import_ogg_vorbis(p_source_file); - if (ogg_vorbis_stream.is_null()) { - return ERR_CANT_OPEN; - } - - ogg_vorbis_stream->set_loop(loop); - ogg_vorbis_stream->set_loop_offset(loop_offset); - ogg_vorbis_stream->set_bpm(bpm); - ogg_vorbis_stream->set_beat_count(beat_count); - ogg_vorbis_stream->set_bar_beats(bar_beats); - - return ResourceSaver::save(ogg_vorbis_stream, p_save_path + ".oggvorbisstr"); -} - -ResourceImporterOggVorbis::ResourceImporterOggVorbis() { +Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_file(const String &p_path) { + Vector<uint8_t> file_data = FileAccess::get_file_as_bytes(p_path); + ERR_FAIL_COND_V_MSG(file_data.is_empty(), Ref<AudioStreamOggVorbis>(), "Cannot open file '" + p_path + "'."); + return load_from_buffer(file_data); } diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h index 4874419834..59ae3378a0 100644 --- a/modules/vorbis/resource_importer_ogg_vorbis.h +++ b/modules/vorbis/resource_importer_ogg_vorbis.h @@ -42,16 +42,17 @@ class ResourceImporterOggVorbis : public ResourceImporter { OGG_SYNC_BUFFER_SIZE = 8192, }; -private: - // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0; - - static Ref<AudioStreamOggVorbis> import_ogg_vorbis(const String &p_path); +protected: + static void _bind_methods(); public: #ifdef TOOLS_ENABLED virtual bool has_advanced_options() const override; virtual void show_advanced_options(const String &p_path) override; #endif + + static Ref<AudioStreamOggVorbis> load_from_file(const String &p_path); + static Ref<AudioStreamOggVorbis> load_from_buffer(const Vector<uint8_t> &file_data); virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual String get_save_extension() const override; virtual String get_resource_type() const override; diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp index 92285e2eab..52289334f8 100644 --- a/modules/webp/resource_saver_webp.cpp +++ b/modules/webp/resource_saver_webp.cpp @@ -34,7 +34,7 @@ #include "core/io/file_access.h" #include "core/io/image.h" -#include "scene/resources/texture.h" +#include "scene/resources/image_texture.h" Error ResourceSaverWebP::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { Ref<ImageTexture> texture = p_resource; diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml index 1ab80a93cb..f24fdb4800 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCDataChannel" inherits="PacketPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebRTCDataChannel" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml index 7a69ceb8f6..b34fd68aee 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCDataChannelExtension" inherits="WebRTCDataChannel" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebRTCDataChannelExtension" inherits="WebRTCDataChannel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml index fa58bf5d58..63caa9e4b9 100644 --- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml +++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCMultiplayerPeer" inherits="MultiplayerPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebRTCMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI]. </brief_description> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 99a5381f0c..f8c015dd06 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCPeerConnection" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebRTCPeerConnection" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Interface to a WebRTC peer connection. </brief_description> diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml index dbe6033c4d..e90a36a76c 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebRTCPeerConnectionExtension" inherits="WebRTCPeerConnection" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebRTCPeerConnectionExtension" inherits="WebRTCPeerConnection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> </brief_description> <description> diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml index ff94d4452a..0978e1fcee 100644 --- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml +++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketMultiplayerPeer" inherits="MultiplayerPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebSocketMultiplayerPeer" inherits="MultiplayerPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Base class for WebSocket server and client. </brief_description> diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml index 61f0be22f6..4c2d0159a6 100644 --- a/modules/websocket/doc_classes/WebSocketPeer.xml +++ b/modules/websocket/doc_classes/WebSocketPeer.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebSocketPeer" inherits="PacketPeer" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebSocketPeer" inherits="PacketPeer" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> A WebSocket connection. </brief_description> diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml index 9591afa536..9e1167c09f 100644 --- a/modules/webxr/doc_classes/WebXRInterface.xml +++ b/modules/webxr/doc_classes/WebXRInterface.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="WebXRInterface" inherits="XRInterface" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="WebXRInterface" inherits="XRInterface" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> XR interface using WebXR. </brief_description> diff --git a/modules/zip/doc_classes/ZIPPacker.xml b/modules/zip/doc_classes/ZIPPacker.xml index 5d5bedb773..3be7d8b6df 100644 --- a/modules/zip/doc_classes/ZIPPacker.xml +++ b/modules/zip/doc_classes/ZIPPacker.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ZIPPacker" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="ZIPPacker" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Allows the creation of zip files. </brief_description> diff --git a/modules/zip/doc_classes/ZIPReader.xml b/modules/zip/doc_classes/ZIPReader.xml index 410dc6126f..0fa2672044 100644 --- a/modules/zip/doc_classes/ZIPReader.xml +++ b/modules/zip/doc_classes/ZIPReader.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" ?> -<class name="ZIPReader" inherits="RefCounted" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="ZIPReader" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Allows reading the content of a zip file. </brief_description> |