diff options
Diffstat (limited to 'modules')
44 files changed, 1210 insertions, 848 deletions
diff --git a/modules/betsy/bc6h.glsl b/modules/betsy/bc6h.glsl index 0d10d378fd..37e7591aea 100644 --- a/modules/betsy/bc6h.glsl +++ b/modules/betsy/bc6h.glsl @@ -1,7 +1,7 @@ #[versions] signed = "#define SIGNED"; -unsigned = ""; +unsigned = "#define QUALITY"; // The "Quality" preset causes artifacting on signed data, so for now it's exclusive to unsigned. #[compute] #version 450 @@ -10,10 +10,6 @@ unsigned = ""; #include "UavCrossPlatform_piece_all.glsl" #VERSION_DEFINES -#define QUALITY - -//SIGNED macro is WIP -//#define SIGNED float3 f32tof16(float3 value) { return float3(packHalf2x16(float2(value.x, 0.0)), @@ -48,11 +44,59 @@ params; const float HALF_MAX = 65504.0f; const uint PATTERN_NUM = 32u; +#ifdef SIGNED +const float HALF_MIN = -65504.0f; +#else +const float HALF_MIN = 0.0f; +#endif + +#ifdef SIGNED +// https://github.com/godotengine/godot/pull/96377#issuecomment-2323488254 +// https://github.com/godotengine/godot/pull/96377#issuecomment-2323450950 +bool isNegative(float a) { + return a < 0.0f; +} + +float CalcSignlessMSLE(float a, float b) { + float err = log2((b + 1.0f) / (a + 1.0f)); + err = err * err; + return err; +} + +float CrossCalcMSLE(float a, float b) { + float result = 0.0f; + result += CalcSignlessMSLE(0.0f, abs(a)); + result += CalcSignlessMSLE(0.0f, abs(b)); + return result; +} + +float CalcMSLE(float3 a, float3 b) { + float result = 0.0f; + if (isNegative(a.x) != isNegative(b.x)) { + result += CrossCalcMSLE(a.x, b.x); + } else { + result += CalcSignlessMSLE(abs(a.x), abs(b.x)); + } + if (isNegative(a.y) != isNegative(b.y)) { + result += CrossCalcMSLE(a.y, b.y); + } else { + result += CalcSignlessMSLE(abs(a.y), abs(b.y)); + } + if (isNegative(a.z) != isNegative(b.z)) { + result += CrossCalcMSLE(a.z, b.z); + } else { + result += CalcSignlessMSLE(abs(a.z), abs(b.z)); + } + + return result; +} +#else float CalcMSLE(float3 a, float3 b) { float3 err = log2((b + 1.0f) / (a + 1.0f)); err = err * err; return err.x + err.y + err.z; } +#endif uint PatternFixupID(uint i) { uint ret = 15u; @@ -176,11 +220,6 @@ float3 Unquantize10(float3 x) { float3 FinishUnquantize(float3 endpoint0Unq, float3 endpoint1Unq, float weight) { float3 comp = (endpoint0Unq * (64.0f - weight) + endpoint1Unq * weight + 32.0f) * (31.0f / 2048.0f); - /*float3 signVal; - signVal.x = comp.x >= 0.0f ? 0.0f : 0x8000; - signVal.y = comp.y >= 0.0f ? 0.0f : 0x8000; - signVal.z = comp.z >= 0.0f ? 0.0f : 0x8000;*/ - //return f16tof32( uint3( signVal + abs( comp ) ) ); return f16tof32(uint3(comp)); } #endif @@ -207,6 +246,7 @@ uint ComputeIndex4(float texelPos, float endPoint0Pos, float endPoint1Pos) { return uint(clamp(r * 14.93333f + 0.03333f + 0.5f, 0.0f, 15.0f)); } +// This adds a bitflag to quantized values that signifies whether they are negative. void SignExtend(inout float3 v1, uint mask, uint signFlag) { int3 v = int3(v1); v.x = (v.x & int(mask)) | (v.x < 0 ? int(signFlag) : 0); @@ -215,6 +255,7 @@ void SignExtend(inout float3 v1, uint mask, uint signFlag) { v1 = v; } +// Encodes a block with mode 11 (2x 10-bit endpoints). void EncodeP1(inout uint4 block, inout float blockMSLE, float3 texels[16]) { // compute endpoints (min/max RGB bbox) float3 blockMin = texels[0]; @@ -250,6 +291,12 @@ void EncodeP1(inout uint4 block, inout float blockMSLE, float3 texels[16]) { float endPoint0Pos = f32tof16(dot(blockMin, blockDir)); float endPoint1Pos = f32tof16(dot(blockMax, blockDir)); +#ifdef SIGNED + int maxVal10 = 0x1FF; + endpoint0 = clamp(endpoint0, -maxVal10, maxVal10); + endpoint1 = clamp(endpoint1, -maxVal10, maxVal10); +#endif + // check if endpoint swap is required float fixupTexelPos = f32tof16(dot(texels[0], blockDir)); uint fixupIndex = ComputeIndex4(fixupTexelPos, endPoint0Pos, endPoint1Pos); @@ -276,6 +323,11 @@ void EncodeP1(inout uint4 block, inout float blockMSLE, float3 texels[16]) { msle += CalcMSLE(texels[i], texelUnc); } +#ifdef SIGNED + SignExtend(endpoint0, 0x1FF, 0x200); + SignExtend(endpoint1, 0x1FF, 0x200); +#endif + // encode block for mode 11 blockMSLE = msle; block.x = 0x03; @@ -316,11 +368,12 @@ float DistToLineSq(float3 PointOnLine, float3 LineDirection, float3 Point) { return dot(x, x); } +// Gets the deviation from the source data of a particular pattern (smaller is better). float EvaluateP2Pattern(uint pattern, float3 texels[16]) { float3 p0BlockMin = float3(HALF_MAX, HALF_MAX, HALF_MAX); - float3 p0BlockMax = float3(0.0f, 0.0f, 0.0f); + float3 p0BlockMax = float3(HALF_MIN, HALF_MIN, HALF_MIN); float3 p1BlockMin = float3(HALF_MAX, HALF_MAX, HALF_MAX); - float3 p1BlockMax = float3(0.0f, 0.0f, 0.0f); + float3 p1BlockMax = float3(HALF_MIN, HALF_MIN, HALF_MIN); for (uint i = 0; i < 16; ++i) { uint paletteID = Pattern(pattern, i); @@ -350,11 +403,12 @@ float EvaluateP2Pattern(uint pattern, float3 texels[16]) { return sqDistanceFromLine; } +// Encodes a block with either mode 2 (7-bit base, 3x 6-bit delta), or mode 6 (9-bit base, 3x 5-bit delta). Both use pattern encoding. void EncodeP2Pattern(inout uint4 block, inout float blockMSLE, uint pattern, float3 texels[16]) { float3 p0BlockMin = float3(HALF_MAX, HALF_MAX, HALF_MAX); - float3 p0BlockMax = float3(0.0f, 0.0f, 0.0f); + float3 p0BlockMax = float3(HALF_MIN, HALF_MIN, HALF_MIN); float3 p1BlockMin = float3(HALF_MAX, HALF_MAX, HALF_MAX); - float3 p1BlockMax = float3(0.0f, 0.0f, 0.0f); + float3 p1BlockMax = float3(HALF_MIN, HALF_MIN, HALF_MIN); for (uint i = 0u; i < 16u; ++i) { uint paletteID = Pattern(pattern, i); @@ -430,6 +484,13 @@ void EncodeP2Pattern(inout uint4 block, inout float blockMSLE, uint pattern, flo endpoint952 = clamp(endpoint952, -maxVal95, maxVal95); endpoint953 = clamp(endpoint953, -maxVal95, maxVal95); +#ifdef SIGNED + int maxVal7 = 0x3F; + int maxVal9 = 0xFF; + endpoint760 = clamp(endpoint760, -maxVal7, maxVal7); + endpoint950 = clamp(endpoint950, -maxVal9, maxVal9); +#endif + float3 endpoint760Unq = Unquantize7(endpoint760); float3 endpoint761Unq = Unquantize7(endpoint760 + endpoint761); float3 endpoint762Unq = Unquantize7(endpoint760 + endpoint762); @@ -465,6 +526,11 @@ void EncodeP2Pattern(inout uint4 block, inout float blockMSLE, uint pattern, flo SignExtend(endpoint952, 0xF, 0x10); SignExtend(endpoint953, 0xF, 0x10); +#ifdef SIGNED + SignExtend(endpoint760, 0x3F, 0x40); + SignExtend(endpoint950, 0xFF, 0x100); +#endif + // encode block float p2MSLE = min(msle76, msle95); if (p2MSLE < blockMSLE) { @@ -637,7 +703,7 @@ void main() { float bestScore = EvaluateP2Pattern(0, texels); uint bestPattern = 0; - for (uint i = 1u; i < 32u; ++i) { + for (uint i = 1u; i < PATTERN_NUM; ++i) { float score = EvaluateP2Pattern(i, texels); if (score < bestScore) { diff --git a/modules/betsy/image_compress_betsy.cpp b/modules/betsy/image_compress_betsy.cpp index 7f723826d1..c17651da45 100644 --- a/modules/betsy/image_compress_betsy.cpp +++ b/modules/betsy/image_compress_betsy.cpp @@ -36,6 +36,9 @@ #if defined(VULKAN_ENABLED) #include "drivers/vulkan/rendering_context_driver_vulkan.h" #endif +#if defined(METAL_ENABLED) +#include "drivers/metal/rendering_context_driver_metal.h" +#endif #include "bc6h.glsl.gen.h" @@ -66,10 +69,16 @@ Error _compress_betsy(BetsyFormat p_format, Image *r_img) { if (rd == nullptr) { #if defined(RD_ENABLED) -#if defined(VULKAN_ENABLED) - rcd = memnew(RenderingContextDriverVulkan); +#if defined(METAL_ENABLED) + rcd = memnew(RenderingContextDriverMetal); rd = memnew(RenderingDevice); #endif +#if defined(VULKAN_ENABLED) + if (rcd == nullptr) { + rcd = memnew(RenderingContextDriverVulkan); + rd = memnew(RenderingDevice); + } +#endif #endif if (rcd != nullptr && rd != nullptr) { err = rcd->initialize(); diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp index 4e1a00cad6..8d4d0234da 100644 --- a/modules/fbx/fbx_document.cpp +++ b/modules/fbx/fbx_document.cpp @@ -875,7 +875,7 @@ Error FBXDocument::_parse_meshes(Ref<FBXState> p_state) { const int material = int(fbx_material->typed_id); ERR_FAIL_INDEX_V(material, p_state->materials.size(), ERR_FILE_CORRUPT); Ref<Material> mat3d = p_state->materials[material]; - ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(mat3d.is_null(), ERR_FILE_CORRUPT); Ref<BaseMaterial3D> base_material = mat3d; if (has_vertex_color && base_material.is_valid()) { @@ -891,7 +891,7 @@ Error FBXDocument::_parse_meshes(Ref<FBXState> p_state) { } mat = mat3d; } - ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(mat.is_null(), ERR_FILE_CORRUPT); mat_name = mat->get_name(); } import_mesh->add_surface(primitive, array, morphs, @@ -1056,7 +1056,7 @@ GLTFImageIndex FBXDocument::_parse_image_save_image(Ref<FBXState> p_state, const } Error FBXDocument::_parse_images(Ref<FBXState> p_state, const String &p_base_path) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); const ufbx_scene *fbx_scene = p_state->scene.get(); for (int texture_i = 0; texture_i < static_cast<int>(fbx_scene->texture_files.count); texture_i++) { @@ -2118,7 +2118,6 @@ Error FBXDocument::_parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) { Ref<FBXState> state = p_state; ERR_FAIL_COND_V(state.is_null(), nullptr); - ERR_FAIL_NULL_V(state, nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); p_state->set_bake_fps(p_bake_fps); GLTFNodeIndex fbx_root = state->root_nodes.write[0]; @@ -2246,7 +2245,7 @@ Error FBXDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint3 Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); - ERR_FAIL_NULL_V(file, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_OPEN); String base_path = p_base_path; if (base_path.is_empty()) { base_path = p_path.get_base_dir(); diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp index d74d316704..b31ae878ce 100644 --- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp +++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp @@ -71,7 +71,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve bool GDScriptEditorTranslationParserPlugin::_is_constant_string(const GDScriptParser::ExpressionNode *p_expression) { ERR_FAIL_NULL_V(p_expression, false); - return p_expression->is_constant && (p_expression->reduced_value.get_type() == Variant::STRING || p_expression->reduced_value.get_type() == Variant::STRING_NAME); + return p_expression->is_constant && p_expression->reduced_value.is_string(); } void GDScriptEditorTranslationParserPlugin::_traverse_class(const GDScriptParser::ClassNode *p_class) { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 36c5ad0937..e3f2a61090 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -954,7 +954,8 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { if (E) { if (likely(top->valid) && E->value.getter) { Callable::CallError ce; - r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce); + const Variant ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce); + r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant(); return true; } r_ret = top->static_variables[E->value.index]; @@ -1727,10 +1728,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { if (E) { if (likely(script->valid) && E->value.getter) { Callable::CallError err; - r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); - if (err.error == Callable::CallError::CALL_OK) { - return true; - } + const Variant ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); + r_ret = (err.error == Callable::CallError::CALL_OK) ? ret : Variant(); + return true; } r_ret = members[E->value.index]; return true; @@ -1752,7 +1752,8 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { if (E) { if (likely(sptr->valid) && E->value.getter) { Callable::CallError ce; - r_ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce); + const Variant ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce); + r_ret = (ce.error == Callable::CallError::CALL_OK) ? ret : Variant(); return true; } r_ret = sptr->static_variables[E->value.index]; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 342ff073af..db7bef2f07 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -1997,7 +1997,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, } // Look for valid indexing in other types - if (!found && (index.value.get_type() == Variant::STRING || index.value.get_type() == Variant::NODE_PATH)) { + if (!found && (index.value.is_string() || index.value.get_type() == Variant::NODE_PATH)) { StringName id = index.value; found = _guess_identifier_type_from_base(c, base, id, r_type); } else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) { diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 2162a727b3..2de5811bca 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -150,7 +150,7 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) : function(p_function) { - ERR_FAIL_NULL(p_script.ptr()); + ERR_FAIL_COND(p_script.is_null()); ERR_FAIL_NULL(p_function); script = p_script; captures = p_captures; @@ -282,7 +282,7 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) : function(p_function) { - ERR_FAIL_NULL(p_self.ptr()); + ERR_FAIL_COND(p_self.is_null()); ERR_FAIL_NULL(p_function); reference = p_self; object = p_self.ptr(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a185eff4d9..92f9c5fa11 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -4809,9 +4809,9 @@ String GDScriptParser::DataType::to_string() const { return class_type->fqcn; case SCRIPT: { if (is_meta_type) { - return script_type != nullptr ? script_type->get_class_name().operator String() : ""; + return script_type.is_valid() ? script_type->get_class_name().operator String() : ""; } - String name = script_type != nullptr ? script_type->get_name() : ""; + String name = script_type.is_valid() ? script_type->get_name() : ""; if (!name.is_empty()) { return name; } diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index 3e1de628d2..59dd983ed2 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -232,7 +232,7 @@ struct GDScriptUtilityFunctionsDefinitions { static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { VALIDATE_ARG_COUNT(1); - if (p_args[0]->get_type() != Variant::STRING) { + if (!p_args[0]->is_string()) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 0; r_error.expected = Variant::STRING; diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp index 03d830741b..b636dbe580 100644 --- a/modules/gdscript/language_server/gdscript_language_protocol.cpp +++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp @@ -196,7 +196,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) { ERR_FAIL_COND_V_MSG(!clients.has(latest_client_id), ret.to_json(), vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id)); Ref<LSPeer> peer = clients.get(latest_client_id); - if (peer != nullptr) { + if (peer.is_valid()) { String msg = Variant(request).to_json_string(); msg = format_output(msg); (*peer)->res_queue.push_back(msg.utf8()); @@ -298,7 +298,7 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia } ERR_FAIL_COND(!clients.has(p_client_id)); Ref<LSPeer> peer = clients.get(p_client_id); - ERR_FAIL_NULL(peer); + ERR_FAIL_COND(peer.is_null()); Dictionary message = make_notification(p_method, p_params); String msg = Variant(message).to_json_string(); @@ -319,7 +319,7 @@ void GDScriptLanguageProtocol::request_client(const String &p_method, const Vari } ERR_FAIL_COND(!clients.has(p_client_id)); Ref<LSPeer> peer = clients.get(p_client_id); - ERR_FAIL_NULL(peer); + ERR_FAIL_COND(peer.is_null()); Dictionary message = make_request(p_method, p_params, next_server_id); next_server_id++; diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp index 2224bb0040..fa5f279db9 100644 --- a/modules/gdscript/language_server/gdscript_text_document.cpp +++ b/modules/gdscript/language_server/gdscript_text_document.cpp @@ -309,7 +309,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) { params.load(p_params["data"]); symbol = GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_symbol(params, item.label, item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function); - } else if (data.get_type() == Variant::STRING) { + } else if (data.is_string()) { String query = data; Vector<String> param_symbols = query.split(SYMBOL_SEPERATOR, false); diff --git a/modules/gdscript/language_server/godot_lsp.h b/modules/gdscript/language_server/godot_lsp.h index 13c2693390..bdf339f5fe 100644 --- a/modules/gdscript/language_server/godot_lsp.h +++ b/modules/gdscript/language_server/godot_lsp.h @@ -1064,7 +1064,7 @@ struct CompletionItem { } if (p_dict.has("documentation")) { Variant doc = p_dict["documentation"]; - if (doc.get_type() == Variant::STRING) { + if (doc.is_string()) { documentation.value = doc; } else if (doc.get_type() == Variant::DICTIONARY) { Dictionary v = doc; diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_capture_reassignment.out b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_capture_reassignment.out index 8d953818eb..e6a1cab77d 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/confusable_capture_reassignment.out +++ b/modules/gdscript/tests/scripts/analyzer/warnings/confusable_capture_reassignment.out @@ -15,5 +15,5 @@ GDTEST_OK >> Line: 16 >> CONFUSABLE_CAPTURE_REASSIGNMENT >> Reassigning lambda capture does not modify the outer local variable "array_assign". -lambda 2 2 12 (2, 0) [2] [2] { "x": 2 } -outer 2 1 1 (1, 0) [1] [2] { "x": 2 } +lambda 2 2 12 (2, 0) [2] [2] { &"x": 2 } +outer 2 1 1 (1, 0) [1] [2] { &"x": 2 } diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out index 553d40d953..a8ef52583d 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out @@ -1,2 +1,2 @@ GDTEST_OK -{ "a": 1, "b": 2, "with spaces": 3, "2": 4 } +{ &"a": 1, &"b": 2, &"with spaces": 3, &"2": 4 } diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out index cf79845f53..4e404e1d26 100644 --- a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out +++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out @@ -1,2 +1,2 @@ GDTEST_OK -{ "hello": { "world": { "is": "beautiful" } } } +{ "hello": { &"world": { "is": "beautiful" } } } diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd index 94bac1974f..de5eaabb79 100644 --- a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd +++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.gd @@ -7,11 +7,11 @@ func test(): stringname_dict[&"abc"] = 24 print("String key is TYPE_STRING: ", typeof(string_dict.keys()[0]) == TYPE_STRING) - print("StringName key is TYPE_STRING: ", typeof(stringname_dict.keys()[0]) == TYPE_STRING) + print("StringName key is TYPE_STRING_NAME: ", typeof(stringname_dict.keys()[0]) == TYPE_STRING_NAME) print("StringName gets String: ", string_dict.get(&"abc")) print("String gets StringName: ", stringname_dict.get("abc")) stringname_dict[&"abc"] = 42 - # They compare equal because StringName keys are converted to String. + # They compare equal because StringName keys are considered equivalent to String keys. print("String Dictionary == StringName Dictionary: ", string_dict == stringname_dict) diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out index ab5b89d55c..a1461912bf 100644 --- a/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out +++ b/modules/gdscript/tests/scripts/runtime/features/dictionary_string_stringname_equivalent.out @@ -1,6 +1,6 @@ GDTEST_OK String key is TYPE_STRING: true -StringName key is TYPE_STRING: true +StringName key is TYPE_STRING_NAME: true StringName gets String: 42 String gets StringName: 24 String Dictionary == StringName Dictionary: true diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp index 9fdd6034a9..c6540ebb22 100644 --- a/modules/gltf/extensions/gltf_document_extension.cpp +++ b/modules/gltf/extensions/gltf_document_extension.cpp @@ -56,7 +56,7 @@ void GLTFDocumentExtension::_bind_methods() { // Import process. Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_import_preflight, p_state, p_extensions, err); return err; @@ -69,16 +69,16 @@ Vector<String> GLTFDocumentExtension::get_supported_extensions() { } Error GLTFDocumentExtension::parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_gltf_node.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_parse_node_extensions, p_state, p_gltf_node, p_extensions, err); return err; } Error GLTFDocumentExtension::parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(r_image, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(r_image.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_parse_image_data, p_state, p_image_data, p_mime_type, r_image, err); return err; @@ -91,31 +91,31 @@ String GLTFDocumentExtension::get_image_file_extension() { } Error GLTFDocumentExtension::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(r_gltf_texture, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(r_gltf_texture.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_parse_texture_json, p_state, p_texture_json, r_gltf_texture, err); return err; } Node3D *GLTFDocumentExtension::generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) { - ERR_FAIL_NULL_V(p_state, nullptr); - ERR_FAIL_NULL_V(p_gltf_node, nullptr); + ERR_FAIL_COND_V(p_state.is_null(), nullptr); + ERR_FAIL_COND_V(p_gltf_node.is_null(), nullptr); Node3D *ret_node = nullptr; GDVIRTUAL_CALL(_generate_scene_node, p_state, p_gltf_node, p_scene_parent, ret_node); return ret_node; } Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_import_post_parse, p_state, err); return err; } Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_gltf_node.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err); @@ -124,7 +124,7 @@ Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_import_post, p_state, p_root, err); return err; @@ -139,14 +139,14 @@ Error GLTFDocumentExtension::export_preflight(Ref<GLTFState> p_state, Node *p_ro } void GLTFDocumentExtension::convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) { - ERR_FAIL_NULL(p_state); - ERR_FAIL_NULL(p_gltf_node); + ERR_FAIL_COND(p_state.is_null()); + ERR_FAIL_COND(p_gltf_node.is_null()); ERR_FAIL_NULL(p_scene_node); GDVIRTUAL_CALL(_convert_scene_node, p_state, p_gltf_node, p_scene_node); } Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_export_preserialize, p_state, err); return err; @@ -160,38 +160,38 @@ Vector<String> GLTFDocumentExtension::get_saveable_image_formats() { PackedByteArray GLTFDocumentExtension::serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality) { PackedByteArray ret; - ERR_FAIL_NULL_V(p_state, ret); - ERR_FAIL_NULL_V(p_image, ret); + ERR_FAIL_COND_V(p_state.is_null(), ret); + ERR_FAIL_COND_V(p_image.is_null(), ret); GDVIRTUAL_CALL(_serialize_image_to_bytes, p_state, p_image, p_image_dict, p_image_format, p_lossy_quality, ret); return ret; } Error GLTFDocumentExtension::save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_file_path, const String &p_image_format, float p_lossy_quality) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_image.is_null(), ERR_INVALID_PARAMETER); Error ret = OK; GDVIRTUAL_CALL(_save_image_at_path, p_state, p_image, p_file_path, p_image_format, p_lossy_quality, ret); return ret; } Error GLTFDocumentExtension::serialize_texture_json(Ref<GLTFState> p_state, Dictionary p_texture_json, Ref<GLTFTexture> p_gltf_texture, const String &p_image_format) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_gltf_texture, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_gltf_texture.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_serialize_texture_json, p_state, p_texture_json, p_gltf_texture, p_image_format, err); return err; } Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_gltf_node.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err); return err; } Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); Error err = OK; GDVIRTUAL_CALL(_export_post, p_state, err); return err; diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp index 64117349e0..cde30bce18 100644 --- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp +++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp @@ -45,7 +45,7 @@ void GLTFDocumentExtensionConvertImporterMesh::_copy_meta(Object *p_src_object, Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_state, Node *p_root) { ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); List<Node *> queue; queue.push_back(p_root); List<Node *> delete_queue; diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.cpp b/modules/gltf/extensions/physics/gltf_physics_shape.cpp index 0340eb11b5..0f2246ce18 100644 --- a/modules/gltf/extensions/physics/gltf_physics_shape.cpp +++ b/modules/gltf/extensions/physics/gltf_physics_shape.cpp @@ -229,7 +229,7 @@ Ref<GLTFPhysicsShape> GLTFPhysicsShape::from_resource(const Ref<Shape3D> &p_shap } Ref<Shape3D> GLTFPhysicsShape::to_resource(bool p_cache_shapes) { - if (!p_cache_shapes || _shape_cache == nullptr) { + if (!p_cache_shapes || _shape_cache.is_null()) { if (shape_type == "box") { Ref<BoxShape3D> box; box.instantiate(); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 69973a34dd..4653df7afe 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -299,8 +299,8 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> p_state) { } Error GLTFDocument::_parse_glb(Ref<FileAccess> p_file, Ref<GLTFState> p_state) { - ERR_FAIL_NULL_V(p_file, ERR_INVALID_PARAMETER); - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_file.is_null(), ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_file->get_position() != 0, ERR_FILE_CANT_READ); uint32_t magic = p_file->get_32(); ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF @@ -3282,7 +3282,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) { const int material = p["material"]; ERR_FAIL_INDEX_V(material, p_state->materials.size(), ERR_FILE_CORRUPT); Ref<Material> mat3d = p_state->materials[material]; - ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(mat3d.is_null(), ERR_FILE_CORRUPT); Ref<BaseMaterial3D> base_material = mat3d; if (has_vertex_color && base_material.is_valid()) { @@ -3298,7 +3298,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) { } mat = mat3d; } - ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT); + ERR_FAIL_COND_V(mat.is_null(), ERR_FILE_CORRUPT); mat_name = mat->get_name(); } import_mesh->add_surface(primitive, array, morphs, @@ -3601,7 +3601,7 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector< } Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_path) { - ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER); if (!p_state->json.has("images")) { return OK; } @@ -6967,14 +6967,14 @@ Dictionary _serialize_texture_transform_uv(Vector2 p_offset, Vector2 p_scale) { } Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) { - ERR_FAIL_NULL_V(p_material, Dictionary()); + ERR_FAIL_COND_V(p_material.is_null(), Dictionary()); Vector3 offset = p_material->get_uv1_offset(); Vector3 scale = p_material->get_uv1_scale(); return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); } Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) { - ERR_FAIL_NULL_V(p_material, Dictionary()); + ERR_FAIL_COND_V(p_material.is_null(), Dictionary()); Vector3 offset = p_material->get_uv2_offset(); Vector3 scale = p_material->get_uv2_scale(); return _serialize_texture_transform_uv(Vector2(offset.x, offset.y), Vector2(scale.x, scale.y)); @@ -7345,7 +7345,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) { Ref<GLTFState> state = p_state; - ERR_FAIL_NULL_V(state, PackedByteArray()); + ERR_FAIL_COND_V(state.is_null(), PackedByteArray()); // For buffers, set the state filename to an empty string, but // don't touch the base path, in case the user set it manually. state->filename = ""; @@ -7357,7 +7357,7 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) { Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) { Ref<GLTFState> state = p_state; - ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(state.is_null(), ERR_INVALID_PARAMETER); state->base_path = p_path.get_base_dir(); state->filename = p_path.get_file(); Error err = _serialize(state); @@ -7373,7 +7373,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) { Ref<GLTFState> state = p_state; - ERR_FAIL_NULL_V(state, nullptr); + ERR_FAIL_COND_V(state.is_null(), nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); Error err = OK; p_state->set_bake_fps(p_bake_fps); @@ -7491,7 +7491,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint Error err; Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN); - ERR_FAIL_NULL_V(file, ERR_FILE_CANT_OPEN); + ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_OPEN); String base_path = p_base_path; if (base_path.is_empty()) { base_path = p_path.get_base_dir(); @@ -7508,7 +7508,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint } Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) { - ERR_FAIL_NULL_V(p_state, ERR_PARSE_ERROR); + ERR_FAIL_COND_V(p_state.is_null(), ERR_PARSE_ERROR); if (p_state->json.has("extensionsUsed")) { Vector<String> ext_array = p_state->json["extensionsUsed"]; p_state->extensions_used = ext_array; diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 47a5b23895..89495e2c83 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -44,6 +44,9 @@ #if defined(VULKAN_ENABLED) #include "drivers/vulkan/rendering_context_driver_vulkan.h" #endif +#if defined(METAL_ENABLED) +#include "drivers/metal/rendering_context_driver_metal.h" +#endif //uncomment this if you want to see textures from all the process saved //#define DEBUG_TEXTURES @@ -1043,10 +1046,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d RenderingDevice *rd = RenderingServer::get_singleton()->create_local_rendering_device(); if (rd == nullptr) { #if defined(RD_ENABLED) -#if defined(VULKAN_ENABLED) - rcd = memnew(RenderingContextDriverVulkan); +#if defined(METAL_ENABLED) + rcd = memnew(RenderingContextDriverMetal); rd = memnew(RenderingDevice); #endif +#if defined(VULKAN_ENABLED) + if (rcd == nullptr) { + rcd = memnew(RenderingContextDriverVulkan); + rd = memnew(RenderingDevice); + } +#endif #endif if (rcd != nullptr && rd != nullptr) { err = rcd->initialize(); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 6d561c1566..177859f270 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2796,7 +2796,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const if (GDMonoCache::godot_api_cache_updated) { GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr); - ERR_FAIL_NULL_V_MSG(scr, Ref<Resource>(), "Could not create C# script '" + real_path + "'."); + ERR_FAIL_COND_V_MSG(scr.is_null(), Ref<Resource>(), "Could not create C# script '" + real_path + "'."); } else { scr = Ref<CSharpScript>(memnew(CSharpScript)); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs index ebb2677361..d7877fa5fc 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs @@ -265,11 +265,6 @@ namespace GodotTools.Build success = Publish(buildInfo); } - if (!success) - { - ShowBuildErrorDialog("Failed to publish .NET project"); - } - return success; } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index ede0600ac1..a5f24fb67b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -75,7 +75,19 @@ namespace GodotTools.Export }; } - private string? _maybeLastExportError; + private void AddExceptionMessage(EditorExportPlatform platform, Exception exception) + { + string? exceptionMessage = exception.Message; + if (string.IsNullOrEmpty(exceptionMessage)) + { + exceptionMessage = $"Exception thrown: {exception.GetType().Name}"; + } + + platform.AddMessage(EditorExportPlatform.ExportMessageType.Error, "Export .NET Project", exceptionMessage); + + // We also print exceptions as we receive them to stderr. + Console.Error.WriteLine(exception); + } // With this method we can override how a file is exported in the PCK public override void _ExportFile(string path, string type, string[] features) @@ -92,8 +104,8 @@ namespace GodotTools.Export if (!ProjectContainsDotNet()) { - _maybeLastExportError = $"This project contains C# files but no solution file was found at the following path: {GodotSharpDirs.ProjectSlnPath}\n" + - "A solution file is required for projects with C# files. Please ensure that the solution file exists in the specified location and try again."; + GetExportPlatform().AddMessage(EditorExportPlatform.ExportMessageType.Error, "Export .NET Project", $"This project contains C# files but no solution file was found at the following path: {GodotSharpDirs.ProjectSlnPath}\n" + + "A solution file is required for projects with C# files. Please ensure that the solution file exists in the specified location and try again."); throw new InvalidOperationException($"{path} is a C# file but no solution file exists."); } @@ -124,16 +136,7 @@ namespace GodotTools.Export } catch (Exception e) { - _maybeLastExportError = e.Message; - - // 'maybeLastExportError' cannot be null or empty if there was an error, so we - // must consider the possibility of exceptions being thrown without a message. - if (string.IsNullOrEmpty(_maybeLastExportError)) - _maybeLastExportError = $"Exception thrown: {e.GetType().Name}"; - - GD.PushError($"Failed to export project: {_maybeLastExportError}"); - Console.Error.WriteLine(e); - // TODO: Do something on error once _ExportBegin supports failing. + AddExceptionMessage(GetExportPlatform(), e); } } @@ -144,7 +147,9 @@ namespace GodotTools.Export if (!ProjectContainsDotNet()) return; - if (!DeterminePlatformFromFeatures(features, out string? platform)) + string osName = GetExportPlatform().GetOsName(); + + if (!TryDeterminePlatformFromOSName(osName, out string? platform)) throw new NotSupportedException("Target platform not supported."); if (!new[] { OS.Platforms.Windows, OS.Platforms.LinuxBSD, OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS } @@ -445,25 +450,22 @@ namespace GodotTools.Export Directory.Delete(folder, recursive: true); } _tempFolders.Clear(); - - // TODO: The following is just a workaround until the export plugins can be made to abort with errors - - // We check for empty as well, because it's set to empty after hot-reloading - if (!string.IsNullOrEmpty(_maybeLastExportError)) - { - string lastExportError = _maybeLastExportError; - _maybeLastExportError = null; - - GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project"); - } } - private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, [NotNullWhen(true)] out string? platform) + /// <summary> + /// Tries to determine the platform from the export preset's platform OS name. + /// </summary> + /// <param name="osName">Name of the export operating system.</param> + /// <param name="platform">Platform name for the recognized supported platform.</param> + /// <returns> + /// <see langword="true"/> when the platform OS name is recognized as a supported platform, + /// <see langword="false"/> otherwise. + /// </returns> + private static bool TryDeterminePlatformFromOSName(string osName, [NotNullWhen(true)] out string? platform) { - foreach (var feature in features) + if (OS.PlatformFeatureMap.TryGetValue(osName, out platform)) { - if (OS.PlatformFeatureMap.TryGetValue(feature, out platform)) - return true; + return true; } platform = null; diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index 80e44011be..3935854a29 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -194,7 +194,7 @@ private: if (!has_data) { // 3. Extract the data to a temporary location to load from there. Ref<DirAccess> da = DirAccess::create_for_path(packed_path); - ERR_FAIL_NULL(da); + ERR_FAIL_COND(da.is_null()); ERR_FAIL_COND(da->copy_dir(packed_path, data_dir_root) != OK); } api_assemblies_dir = data_dir_root; diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp index d5d4b465d8..212fd1ef6b 100644 --- a/modules/multiplayer/editor/editor_network_profiler.cpp +++ b/modules/multiplayer/editor/editor_network_profiler.cpp @@ -227,10 +227,10 @@ void EditorNetworkProfiler::add_sync_frame_data(const SyncInfo &p_frame) { sync_data[p_frame.synchronizer].outgoing_syncs += p_frame.outgoing_syncs; } SyncInfo &info = sync_data[p_frame.synchronizer]; - if (info.incoming_syncs) { + if (p_frame.incoming_syncs) { info.incoming_size = p_frame.incoming_size / p_frame.incoming_syncs; } - if (info.outgoing_syncs) { + if (p_frame.outgoing_syncs) { info.outgoing_size = p_frame.outgoing_size / p_frame.outgoing_syncs; } } diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp index 1463598ddc..592bb18a71 100644 --- a/modules/multiplayer/scene_rpc_interface.cpp +++ b/modules/multiplayer/scene_rpc_interface.cpp @@ -82,7 +82,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no Array names = config.keys(); names.sort(); // Ensure ID order for (int i = 0; i < names.size(); i++) { - ERR_CONTINUE(names[i].get_type() != Variant::STRING && names[i].get_type() != Variant::STRING_NAME); + ERR_CONTINUE(!names[i].is_string()); String name = names[i].operator String(); ERR_CONTINUE(config[name].get_type() != Variant::DICTIONARY); ERR_CONTINUE(!config[name].operator Dictionary().has("rpc_mode")); diff --git a/modules/navigation/3d/nav_mesh_queries_3d.cpp b/modules/navigation/3d/nav_mesh_queries_3d.cpp new file mode 100644 index 0000000000..70207f86ce --- /dev/null +++ b/modules/navigation/3d/nav_mesh_queries_3d.cpp @@ -0,0 +1,715 @@ +/**************************************************************************/ +/* nav_mesh_queries_3d.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef _3D_DISABLED + +#include "nav_mesh_queries_3d.h" + +#include "../nav_base.h" + +#include "core/math/geometry_3d.h" + +#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a))) + +#define APPEND_METADATA(poly) \ + if (r_path_types) { \ + r_path_types->push_back(poly->owner->get_type()); \ + } \ + if (r_path_rids) { \ + r_path_rids->push_back(poly->owner->get_self()); \ + } \ + if (r_path_owners) { \ + r_path_owners->push_back(poly->owner->get_owner_id()); \ + } + +Vector3 NavMeshQueries3D::polygons_get_random_point(const LocalVector<gd::Polygon> &p_polygons, uint32_t p_navigation_layers, bool p_uniformly) { + const LocalVector<gd::Polygon> ®ion_polygons = p_polygons; + + if (region_polygons.is_empty()) { + return Vector3(); + } + + if (p_uniformly) { + real_t accumulated_area = 0; + RBMap<real_t, uint32_t> region_area_map; + + for (uint32_t rp_index = 0; rp_index < region_polygons.size(); rp_index++) { + const gd::Polygon ®ion_polygon = region_polygons[rp_index]; + real_t polyon_area = region_polygon.surface_area; + + if (polyon_area == 0.0) { + continue; + } + region_area_map[accumulated_area] = rp_index; + accumulated_area += polyon_area; + } + if (region_area_map.is_empty() || accumulated_area == 0) { + // All polygons have no real surface / no area. + return Vector3(); + } + + real_t region_area_map_pos = Math::random(real_t(0), accumulated_area); + + RBMap<real_t, uint32_t>::Iterator region_E = region_area_map.find_closest(region_area_map_pos); + ERR_FAIL_COND_V(!region_E, Vector3()); + uint32_t rrp_polygon_index = region_E->value; + ERR_FAIL_UNSIGNED_INDEX_V(rrp_polygon_index, region_polygons.size(), Vector3()); + + const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; + + real_t accumulated_polygon_area = 0; + RBMap<real_t, uint32_t> polygon_area_map; + + for (uint32_t rpp_index = 2; rpp_index < rr_polygon.points.size(); rpp_index++) { + real_t face_area = Face3(rr_polygon.points[0].pos, rr_polygon.points[rpp_index - 1].pos, rr_polygon.points[rpp_index].pos).get_area(); + + if (face_area == 0.0) { + continue; + } + polygon_area_map[accumulated_polygon_area] = rpp_index; + accumulated_polygon_area += face_area; + } + if (polygon_area_map.is_empty() || accumulated_polygon_area == 0) { + // All faces have no real surface / no area. + return Vector3(); + } + + real_t polygon_area_map_pos = Math::random(real_t(0), accumulated_polygon_area); + + RBMap<real_t, uint32_t>::Iterator polygon_E = polygon_area_map.find_closest(polygon_area_map_pos); + ERR_FAIL_COND_V(!polygon_E, Vector3()); + uint32_t rrp_face_index = polygon_E->value; + ERR_FAIL_UNSIGNED_INDEX_V(rrp_face_index, rr_polygon.points.size(), Vector3()); + + const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); + + Vector3 face_random_position = face.get_random_point_inside(); + return face_random_position; + + } else { + uint32_t rrp_polygon_index = Math::random(int(0), region_polygons.size() - 1); + + const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; + + uint32_t rrp_face_index = Math::random(int(2), rr_polygon.points.size() - 1); + + const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); + + Vector3 face_random_position = face.get_random_point_inside(); + return face_random_position; + } +} + +Vector<Vector3> NavMeshQueries3D::polygons_get_path(const LocalVector<gd::Polygon> &p_polygons, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners, const Vector3 &p_map_up, uint32_t p_link_polygons_size) { + // Clear metadata outputs. + if (r_path_types) { + r_path_types->clear(); + } + if (r_path_rids) { + r_path_rids->clear(); + } + if (r_path_owners) { + r_path_owners->clear(); + } + + // Find the start poly and the end poly on this map. + const gd::Polygon *begin_poly = nullptr; + const gd::Polygon *end_poly = nullptr; + Vector3 begin_point; + Vector3 end_point; + real_t begin_d = FLT_MAX; + real_t end_d = FLT_MAX; + // Find the initial poly and the end poly on this map. + for (const gd::Polygon &p : p_polygons) { + // Only consider the polygon if it in a region with compatible layers. + if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) { + continue; + } + + // For each face check the distance between the origin/destination + for (size_t point_id = 2; point_id < p.points.size(); point_id++) { + const Face3 face(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); + + Vector3 point = face.get_closest_point_to(p_origin); + real_t distance_to_point = point.distance_to(p_origin); + if (distance_to_point < begin_d) { + begin_d = distance_to_point; + begin_poly = &p; + begin_point = point; + } + + point = face.get_closest_point_to(p_destination); + distance_to_point = point.distance_to(p_destination); + if (distance_to_point < end_d) { + end_d = distance_to_point; + end_poly = &p; + end_point = point; + } + } + } + + // Check for trivial cases + if (!begin_poly || !end_poly) { + return Vector<Vector3>(); + } + if (begin_poly == end_poly) { + if (r_path_types) { + r_path_types->resize(2); + r_path_types->write[0] = begin_poly->owner->get_type(); + r_path_types->write[1] = end_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] = end_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] = end_poly->owner->get_owner_id(); + } + + Vector<Vector3> path; + path.resize(2); + path.write[0] = begin_point; + path.write[1] = end_point; + return path; + } + + // List of all reachable navigation polys. + LocalVector<gd::NavigationPoly> navigation_polys; + navigation_polys.resize(p_polygons.size() + p_link_polygons_size); + + // Initialize the matching navigation polygon. + gd::NavigationPoly &begin_navigation_poly = navigation_polys[begin_poly->id]; + begin_navigation_poly.poly = begin_poly; + begin_navigation_poly.entry = begin_point; + begin_navigation_poly.back_navigation_edge_pathway_start = begin_point; + begin_navigation_poly.back_navigation_edge_pathway_end = begin_point; + + // Heap of polygons to travel next. + gd::Heap<gd::NavigationPoly *, gd::NavPolyTravelCostGreaterThan, gd::NavPolyHeapIndexer> + traversable_polys; + traversable_polys.reserve(p_polygons.size() * 0.25); + + // This is an implementation of the A* algorithm. + int least_cost_id = begin_poly->id; + int prev_least_cost_id = -1; + bool found_route = false; + + const gd::Polygon *reachable_end = nullptr; + real_t distance_to_reachable_end = FLT_MAX; + bool is_reachable = true; + + while (true) { + // Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance. + for (const gd::Edge &edge : navigation_polys[least_cost_id].poly->edges) { + // Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon. + for (int connection_index = 0; connection_index < edge.connections.size(); connection_index++) { + const gd::Edge::Connection &connection = edge.connections[connection_index]; + + // Only consider the connection to another polygon if this polygon is in a region with compatible layers. + if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) { + continue; + } + + const gd::NavigationPoly &least_cost_poly = navigation_polys[least_cost_id]; + real_t poly_enter_cost = 0.0; + real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost(); + + if (prev_least_cost_id != -1 && navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self()) { + poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost(); + } + prev_least_cost_id = least_cost_id; + + Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end }; + const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, pathway); + const real_t new_traveled_distance = least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost + poly_enter_cost + least_cost_poly.traveled_distance; + + // Check if the neighbor polygon has already been processed. + gd::NavigationPoly &neighbor_poly = navigation_polys[connection.polygon->id]; + if (neighbor_poly.poly != nullptr) { + // If the neighbor polygon hasn't been traversed yet and the new path leading to + // it is shorter, update the polygon. + if (neighbor_poly.traversable_poly_index < traversable_polys.size() && + new_traveled_distance < neighbor_poly.traveled_distance) { + neighbor_poly.back_navigation_poly_id = least_cost_id; + neighbor_poly.back_navigation_edge = connection.edge; + neighbor_poly.back_navigation_edge_pathway_start = connection.pathway_start; + neighbor_poly.back_navigation_edge_pathway_end = connection.pathway_end; + neighbor_poly.traveled_distance = new_traveled_distance; + neighbor_poly.distance_to_destination = + new_entry.distance_to(end_point) * + neighbor_poly.poly->owner->get_travel_cost(); + neighbor_poly.entry = new_entry; + + // Update the priority of the polygon in the heap. + traversable_polys.shift(neighbor_poly.traversable_poly_index); + } + } else { + // Initialize the matching navigation polygon. + neighbor_poly.poly = connection.polygon; + neighbor_poly.back_navigation_poly_id = least_cost_id; + neighbor_poly.back_navigation_edge = connection.edge; + neighbor_poly.back_navigation_edge_pathway_start = connection.pathway_start; + neighbor_poly.back_navigation_edge_pathway_end = connection.pathway_end; + neighbor_poly.traveled_distance = new_traveled_distance; + neighbor_poly.distance_to_destination = + new_entry.distance_to(end_point) * + neighbor_poly.poly->owner->get_travel_cost(); + neighbor_poly.entry = new_entry; + + // Add the polygon to the heap of polygons to traverse next. + traversable_polys.push(&neighbor_poly); + } + } + } + + // When the heap of traversable polygons is empty at this point it means the end polygon is + // unreachable. + if (traversable_polys.is_empty()) { + // Thus use the further reachable polygon + ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons"); + is_reachable = false; + if (reachable_end == nullptr) { + // The path is not found and there is not a way out. + break; + } + + // Set as end point the furthest reachable point. + end_poly = reachable_end; + end_d = FLT_MAX; + for (size_t point_id = 2; point_id < end_poly->points.size(); point_id++) { + Face3 f(end_poly->points[0].pos, end_poly->points[point_id - 1].pos, end_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; + } + } + + // 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; + } + + for (gd::NavigationPoly &nav_poly : navigation_polys) { + nav_poly.poly = nullptr; + } + navigation_polys[begin_poly->id].poly = begin_poly; + + least_cost_id = begin_poly->id; + prev_least_cost_id = -1; + + reachable_end = nullptr; + + continue; + } + + // Pop the polygon with the lowest travel cost from the heap of traversable polygons. + least_cost_id = traversable_polys.pop()->poly->id; + + // Store the farthest reachable end polygon in case our goal is not reachable. + if (is_reachable) { + real_t distance = navigation_polys[least_cost_id].entry.distance_to(p_destination); + if (distance_to_reachable_end > distance) { + distance_to_reachable_end = distance; + reachable_end = navigation_polys[least_cost_id].poly; + } + } + + // Check if we reached the end + if (navigation_polys[least_cost_id].poly == end_poly) { + found_route = true; + break; + } + } + + // 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) { + 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; + // Optimize the path. + if (p_optimize) { + // Set the apex poly/point to the end point + gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id]; + + Vector3 back_pathway[2] = { apex_poly->back_navigation_edge_pathway_start, apex_poly->back_navigation_edge_pathway_end }; + const Vector3 back_edge_closest_point = Geometry3D::get_closest_point_to_segment(end_point, back_pathway); + if (end_point.is_equal_approx(back_edge_closest_point)) { + // The end point is basically on top of the last crossed edge, funneling around the corners would at best do nothing. + // At worst it would add an unwanted path point before the last point due to precision issues so skip to the next polygon. + if (apex_poly->back_navigation_poly_id != -1) { + apex_poly = &navigation_polys[apex_poly->back_navigation_poly_id]; + } + } + + Vector3 apex_point = end_point; + + gd::NavigationPoly *left_poly = apex_poly; + Vector3 left_portal = apex_point; + gd::NavigationPoly *right_poly = apex_poly; + Vector3 right_portal = apex_point; + + gd::NavigationPoly *p = apex_poly; + + path.push_back(end_point); + APPEND_METADATA(end_poly); + + while (p) { + // Set left and right points of the pathway between polygons. + Vector3 left = p->back_navigation_edge_pathway_start; + Vector3 right = p->back_navigation_edge_pathway_end; + if (THREE_POINTS_CROSS_PRODUCT(apex_point, left, right).dot(p_map_up) < 0) { + SWAP(left, right); + } + + bool skip = false; + if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(p_map_up) >= 0) { + //process + if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(p_map_up) > 0) { + left_poly = p; + left_portal = left; + } else { + clip_path(navigation_polys, path, apex_poly, right_portal, right_poly, r_path_types, r_path_rids, r_path_owners, p_map_up); + + apex_point = right_portal; + p = right_poly; + left_poly = p; + apex_poly = p; + left_portal = apex_point; + right_portal = apex_point; + + path.push_back(apex_point); + APPEND_METADATA(apex_poly->poly); + skip = true; + } + } + + if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(p_map_up) <= 0) { + //process + if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(p_map_up) < 0) { + right_poly = p; + right_portal = right; + } else { + clip_path(navigation_polys, path, apex_poly, left_portal, left_poly, r_path_types, r_path_rids, r_path_owners, p_map_up); + + apex_point = left_portal; + p = left_poly; + right_poly = p; + apex_poly = p; + right_portal = apex_point; + left_portal = apex_point; + + path.push_back(apex_point); + APPEND_METADATA(apex_poly->poly); + } + } + + // Go to the previous polygon. + if (p->back_navigation_poly_id != -1) { + p = &navigation_polys[p->back_navigation_poly_id]; + } else { + // The end + p = nullptr; + } + } + + // If the last point is not the begin point, add it to the list. + if (path[path.size() - 1] != begin_point) { + path.push_back(begin_point); + APPEND_METADATA(begin_poly); + } + + path.reverse(); + if (r_path_types) { + r_path_types->reverse(); + } + if (r_path_rids) { + r_path_rids->reverse(); + } + if (r_path_owners) { + r_path_owners->reverse(); + } + + } else { + path.push_back(end_point); + APPEND_METADATA(end_poly); + + // Add mid points + int np_id = least_cost_id; + while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -1) { + if (navigation_polys[np_id].back_navigation_edge != -1) { + int prev = navigation_polys[np_id].back_navigation_edge; + int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); + Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; + + path.push_back(point); + APPEND_METADATA(navigation_polys[np_id].poly); + } else { + path.push_back(navigation_polys[np_id].entry); + APPEND_METADATA(navigation_polys[np_id].poly); + } + + np_id = navigation_polys[np_id].back_navigation_poly_id; + } + + path.push_back(begin_point); + APPEND_METADATA(begin_poly); + + path.reverse(); + if (r_path_types) { + r_path_types->reverse(); + } + if (r_path_rids) { + r_path_rids->reverse(); + } + if (r_path_owners) { + r_path_owners->reverse(); + } + } + + // Ensure post conditions (path arrays MUST match in size). + CRASH_COND(r_path_types && path.size() != r_path_types->size()); + CRASH_COND(r_path_rids && path.size() != r_path_rids->size()); + CRASH_COND(r_path_owners && path.size() != r_path_owners->size()); + + return path; +} + +Vector3 NavMeshQueries3D::polygons_get_closest_point_to_segment(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) { + bool use_collision = p_use_collision; + Vector3 closest_point; + real_t closest_point_distance = FLT_MAX; + + for (const gd::Polygon &polygon : p_polygons) { + // For each face check the distance to the segment. + for (size_t point_id = 2; point_id < polygon.points.size(); point_id += 1) { + const Face3 face(polygon.points[0].pos, polygon.points[point_id - 1].pos, polygon.points[point_id].pos); + Vector3 intersection_point; + if (face.intersects_segment(p_from, p_to, &intersection_point)) { + const real_t d = p_from.distance_to(intersection_point); + if (!use_collision) { + closest_point = intersection_point; + use_collision = true; + closest_point_distance = d; + } else if (closest_point_distance > d) { + closest_point = intersection_point; + closest_point_distance = d; + } + } + // If segment does not itersect face, check the distance from segment's endpoints. + else if (!use_collision) { + const Vector3 p_from_closest = face.get_closest_point_to(p_from); + const real_t d_p_from = p_from.distance_to(p_from_closest); + if (closest_point_distance > d_p_from) { + closest_point = p_from_closest; + closest_point_distance = d_p_from; + } + + const Vector3 p_to_closest = face.get_closest_point_to(p_to); + const real_t d_p_to = p_to.distance_to(p_to_closest); + if (closest_point_distance > d_p_to) { + closest_point = p_to_closest; + closest_point_distance = d_p_to; + } + } + } + // Finally, check for a case when shortest distance is between some point located on a face's edge and some point located on a line segment. + if (!use_collision) { + for (size_t point_id = 0; point_id < polygon.points.size(); point_id += 1) { + Vector3 a, b; + + Geometry3D::get_closest_points_between_segments( + p_from, + p_to, + polygon.points[point_id].pos, + polygon.points[(point_id + 1) % polygon.points.size()].pos, + a, + b); + + const real_t d = a.distance_to(b); + if (d < closest_point_distance) { + closest_point_distance = d; + closest_point = b; + } + } + } + } + + return closest_point; +} + +Vector3 NavMeshQueries3D::polygons_get_closest_point(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point) { + gd::ClosestPointQueryResult cp = polygons_get_closest_point_info(p_polygons, p_point); + return cp.point; +} + +Vector3 NavMeshQueries3D::polygons_get_closest_point_normal(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point) { + gd::ClosestPointQueryResult cp = polygons_get_closest_point_info(p_polygons, p_point); + return cp.normal; +} + +gd::ClosestPointQueryResult NavMeshQueries3D::polygons_get_closest_point_info(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point) { + gd::ClosestPointQueryResult result; + real_t closest_point_distance_squared = FLT_MAX; + + for (const gd::Polygon &polygon : p_polygons) { + for (size_t point_id = 2; point_id < polygon.points.size(); point_id += 1) { + const Face3 face(polygon.points[0].pos, polygon.points[point_id - 1].pos, polygon.points[point_id].pos); + const Vector3 closest_point_on_face = face.get_closest_point_to(p_point); + const real_t distance_squared_to_point = closest_point_on_face.distance_squared_to(p_point); + if (distance_squared_to_point < closest_point_distance_squared) { + result.point = closest_point_on_face; + result.normal = face.get_plane().normal; + result.owner = polygon.owner->get_self(); + closest_point_distance_squared = distance_squared_to_point; + } + } + } + + return result; +} + +RID NavMeshQueries3D::polygons_get_closest_point_owner(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point) { + gd::ClosestPointQueryResult cp = polygons_get_closest_point_info(p_polygons, p_point); + return cp.owner; +} + +void NavMeshQueries3D::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners, const Vector3 &p_map_up) { + Vector3 from = path[path.size() - 1]; + + if (from.is_equal_approx(p_to_point)) { + return; + } + + Plane cut_plane; + cut_plane.normal = (from - p_to_point).cross(p_map_up); + if (cut_plane.normal == Vector3()) { + return; + } + cut_plane.normal.normalize(); + cut_plane.d = cut_plane.normal.dot(from); + + while (from_poly != p_to_poly) { + Vector3 pathway_start = from_poly->back_navigation_edge_pathway_start; + Vector3 pathway_end = from_poly->back_navigation_edge_pathway_end; + + ERR_FAIL_COND(from_poly->back_navigation_poly_id == -1); + from_poly = &p_navigation_polys[from_poly->back_navigation_poly_id]; + + if (!pathway_start.is_equal_approx(pathway_end)) { + Vector3 inters; + if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) { + if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) { + path.push_back(inters); + APPEND_METADATA(from_poly->poly); + } + } + } + } +} + +#endif // _3D_DISABLED diff --git a/modules/navigation/3d/nav_mesh_queries_3d.h b/modules/navigation/3d/nav_mesh_queries_3d.h new file mode 100644 index 0000000000..109bb2f971 --- /dev/null +++ b/modules/navigation/3d/nav_mesh_queries_3d.h @@ -0,0 +1,54 @@ +/**************************************************************************/ +/* nav_mesh_queries_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NAV_MESH_QUERIES_3D_H +#define NAV_MESH_QUERIES_3D_H + +#ifndef _3D_DISABLED + +#include "../nav_map.h" + +class NavMeshQueries3D { +public: + static Vector3 polygons_get_random_point(const LocalVector<gd::Polygon> &p_polygons, uint32_t p_navigation_layers, bool p_uniformly); + + static Vector<Vector3> polygons_get_path(const LocalVector<gd::Polygon> &p_polygons, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners, const Vector3 &p_map_up, uint32_t p_link_polygons_size); + static Vector3 polygons_get_closest_point_to_segment(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision); + static Vector3 polygons_get_closest_point(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point); + static Vector3 polygons_get_closest_point_normal(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point); + static gd::ClosestPointQueryResult polygons_get_closest_point_info(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point); + static RID polygons_get_closest_point_owner(const LocalVector<gd::Polygon> &p_polygons, const Vector3 &p_point); + + static void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners, const Vector3 &p_map_up); +}; + +#endif // _3D_DISABLED + +#endif // NAV_MESH_QUERIES_3D_H diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 0c91e8dea3..dd77e81b45 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -35,25 +35,13 @@ #include "nav_obstacle.h" #include "nav_region.h" +#include "3d/nav_mesh_queries_3d.h" + #include "core/config/project_settings.h" #include "core/object/worker_thread_pool.h" #include <Obstacle2d.h> -#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a))) - -// Helper macro -#define APPEND_METADATA(poly) \ - if (r_path_types) { \ - r_path_types->push_back(poly->owner->get_type()); \ - } \ - if (r_path_rids) { \ - r_path_rids->push_back(poly->owner->get_self()); \ - } \ - if (r_path_owners) { \ - r_path_owners->push_back(poly->owner->get_owner_id()); \ - } - #ifdef DEBUG_ENABLED #define NAVMAP_ITERATION_ZERO_ERROR_MSG() \ ERR_PRINT_ONCE("NavigationServer navigation map query failed because it was made before first map synchronization.\n\ @@ -142,462 +130,9 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p return Vector<Vector3>(); } - // Clear metadata outputs. - if (r_path_types) { - r_path_types->clear(); - } - if (r_path_rids) { - r_path_rids->clear(); - } - if (r_path_owners) { - r_path_owners->clear(); - } - - // Find the start poly and the end poly on this map. - const gd::Polygon *begin_poly = nullptr; - const gd::Polygon *end_poly = nullptr; - Vector3 begin_point; - Vector3 end_point; - real_t begin_d = FLT_MAX; - real_t end_d = FLT_MAX; - // Find the initial poly and the end poly on this map. - for (const gd::Polygon &p : polygons) { - // Only consider the polygon if it in a region with compatible layers. - if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) { - continue; - } - - // For each face check the distance between the origin/destination - for (size_t point_id = 2; point_id < p.points.size(); point_id++) { - const Face3 face(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); - - Vector3 point = face.get_closest_point_to(p_origin); - real_t distance_to_point = point.distance_to(p_origin); - if (distance_to_point < begin_d) { - begin_d = distance_to_point; - begin_poly = &p; - begin_point = point; - } - - point = face.get_closest_point_to(p_destination); - distance_to_point = point.distance_to(p_destination); - if (distance_to_point < end_d) { - end_d = distance_to_point; - end_poly = &p; - end_point = point; - } - } - } - - // Check for trivial cases - if (!begin_poly || !end_poly) { - return Vector<Vector3>(); - } - if (begin_poly == end_poly) { - if (r_path_types) { - r_path_types->resize(2); - r_path_types->write[0] = begin_poly->owner->get_type(); - r_path_types->write[1] = end_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] = end_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] = end_poly->owner->get_owner_id(); - } - - Vector<Vector3> path; - path.resize(2); - path.write[0] = begin_point; - path.write[1] = end_point; - return path; - } - - // List of all reachable navigation polys. - LocalVector<gd::NavigationPoly> navigation_polys; - navigation_polys.reserve(polygons.size() * 0.75); - - // Add the start polygon to the reachable navigation polygons. - gd::NavigationPoly begin_navigation_poly = gd::NavigationPoly(begin_poly); - begin_navigation_poly.self_id = 0; - begin_navigation_poly.entry = begin_point; - begin_navigation_poly.back_navigation_edge_pathway_start = begin_point; - begin_navigation_poly.back_navigation_edge_pathway_end = begin_point; - navigation_polys.push_back(begin_navigation_poly); - - // List of polygon IDs to visit. - List<uint32_t> to_visit; - to_visit.push_back(0); - - // This is an implementation of the A* algorithm. - int least_cost_id = 0; - int prev_least_cost_id = -1; - bool found_route = false; - - const gd::Polygon *reachable_end = nullptr; - real_t reachable_d = FLT_MAX; - bool is_reachable = true; - - while (true) { - // Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance. - for (const gd::Edge &edge : navigation_polys[least_cost_id].poly->edges) { - // Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon. - for (int connection_index = 0; connection_index < edge.connections.size(); connection_index++) { - const gd::Edge::Connection &connection = edge.connections[connection_index]; - - // Only consider the connection to another polygon if this polygon is in a region with compatible layers. - if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) { - continue; - } - - const gd::NavigationPoly &least_cost_poly = navigation_polys[least_cost_id]; - real_t poly_enter_cost = 0.0; - real_t poly_travel_cost = least_cost_poly.poly->owner->get_travel_cost(); - - if (prev_least_cost_id != -1 && (navigation_polys[prev_least_cost_id].poly->owner->get_self() != least_cost_poly.poly->owner->get_self())) { - poly_enter_cost = least_cost_poly.poly->owner->get_enter_cost(); - } - prev_least_cost_id = least_cost_id; - - Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end }; - const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly.entry, pathway); - const real_t new_distance = (least_cost_poly.entry.distance_to(new_entry) * poly_travel_cost) + poly_enter_cost + least_cost_poly.traveled_distance; - - int64_t already_visited_polygon_index = navigation_polys.find(gd::NavigationPoly(connection.polygon)); - - if (already_visited_polygon_index != -1) { - // Polygon already visited, check if we can reduce the travel cost. - gd::NavigationPoly &avp = navigation_polys[already_visited_polygon_index]; - if (new_distance < avp.traveled_distance) { - avp.back_navigation_poly_id = least_cost_id; - avp.back_navigation_edge = connection.edge; - avp.back_navigation_edge_pathway_start = connection.pathway_start; - avp.back_navigation_edge_pathway_end = connection.pathway_end; - avp.traveled_distance = new_distance; - avp.entry = new_entry; - } - } else { - // Add the neighbor polygon to the reachable ones. - gd::NavigationPoly new_navigation_poly = gd::NavigationPoly(connection.polygon); - new_navigation_poly.self_id = navigation_polys.size(); - new_navigation_poly.back_navigation_poly_id = least_cost_id; - new_navigation_poly.back_navigation_edge = connection.edge; - new_navigation_poly.back_navigation_edge_pathway_start = connection.pathway_start; - new_navigation_poly.back_navigation_edge_pathway_end = connection.pathway_end; - new_navigation_poly.traveled_distance = new_distance; - new_navigation_poly.entry = new_entry; - navigation_polys.push_back(new_navigation_poly); - - // Add the neighbor polygon to the polygons to visit. - to_visit.push_back(navigation_polys.size() - 1); - } - } - } - - // Removes the least cost polygon from the list of polygons to visit so we can advance. - to_visit.erase(least_cost_id); - - // When the list of polygons to visit is empty at this point it means the End Polygon is not reachable - if (to_visit.size() == 0) { - // Thus use the further reachable polygon - ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons"); - is_reachable = false; - if (reachable_end == nullptr) { - // The path is not found and there is not a way out. - break; - } - - // Set as end point the furthest reachable point. - end_poly = reachable_end; - end_d = FLT_MAX; - for (size_t point_id = 2; point_id < end_poly->points.size(); point_id++) { - Face3 f(end_poly->points[0].pos, end_poly->points[point_id - 1].pos, end_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; - } - } - - // 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(); - navigation_polys.push_back(np); - to_visit.clear(); - to_visit.push_back(0); - least_cost_id = 0; - prev_least_cost_id = -1; - - reachable_end = nullptr; - - continue; - } - - // Find the polygon with the minimum cost from the list of polygons to visit. - least_cost_id = -1; - real_t least_cost = FLT_MAX; - for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) { - gd::NavigationPoly *np = &navigation_polys[element->get()]; - real_t cost = np->traveled_distance; - cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost()); - if (cost < least_cost) { - least_cost_id = np->self_id; - least_cost = cost; - } - } - - ERR_BREAK(least_cost_id == -1); - - // Stores the further reachable end polygon, in case our goal is not reachable. - if (is_reachable) { - real_t d = navigation_polys[least_cost_id].entry.distance_to(p_destination); - if (reachable_d > d) { - reachable_d = d; - reachable_end = navigation_polys[least_cost_id].poly; - } - } - - // Check if we reached the end - if (navigation_polys[least_cost_id].poly == end_poly) { - found_route = true; - break; - } - } - - // 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) { - 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; - // Optimize the path. - if (p_optimize) { - // Set the apex poly/point to the end point - gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id]; - - Vector3 back_pathway[2] = { apex_poly->back_navigation_edge_pathway_start, apex_poly->back_navigation_edge_pathway_end }; - const Vector3 back_edge_closest_point = Geometry3D::get_closest_point_to_segment(end_point, back_pathway); - if (end_point.is_equal_approx(back_edge_closest_point)) { - // The end point is basically on top of the last crossed edge, funneling around the corners would at best do nothing. - // At worst it would add an unwanted path point before the last point due to precision issues so skip to the next polygon. - if (apex_poly->back_navigation_poly_id != -1) { - apex_poly = &navigation_polys[apex_poly->back_navigation_poly_id]; - } - } - - Vector3 apex_point = end_point; - - gd::NavigationPoly *left_poly = apex_poly; - Vector3 left_portal = apex_point; - gd::NavigationPoly *right_poly = apex_poly; - Vector3 right_portal = apex_point; - - gd::NavigationPoly *p = apex_poly; - - path.push_back(end_point); - APPEND_METADATA(end_poly); - - while (p) { - // Set left and right points of the pathway between polygons. - Vector3 left = p->back_navigation_edge_pathway_start; - Vector3 right = p->back_navigation_edge_pathway_end; - if (THREE_POINTS_CROSS_PRODUCT(apex_point, left, right).dot(up) < 0) { - SWAP(left, right); - } - - bool skip = false; - if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(up) >= 0) { - //process - if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(up) > 0) { - left_poly = p; - left_portal = left; - } else { - clip_path(navigation_polys, path, apex_poly, right_portal, right_poly, r_path_types, r_path_rids, r_path_owners); - - apex_point = right_portal; - p = right_poly; - left_poly = p; - apex_poly = p; - left_portal = apex_point; - right_portal = apex_point; - - path.push_back(apex_point); - APPEND_METADATA(apex_poly->poly); - skip = true; - } - } - - if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(up) <= 0) { - //process - if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(up) < 0) { - right_poly = p; - right_portal = right; - } else { - clip_path(navigation_polys, path, apex_poly, left_portal, left_poly, r_path_types, r_path_rids, r_path_owners); - - apex_point = left_portal; - p = left_poly; - right_poly = p; - apex_poly = p; - right_portal = apex_point; - left_portal = apex_point; - - path.push_back(apex_point); - APPEND_METADATA(apex_poly->poly); - } - } - - // Go to the previous polygon. - if (p->back_navigation_poly_id != -1) { - p = &navigation_polys[p->back_navigation_poly_id]; - } else { - // The end - p = nullptr; - } - } - - // If the last point is not the begin point, add it to the list. - if (path[path.size() - 1] != begin_point) { - path.push_back(begin_point); - APPEND_METADATA(begin_poly); - } - - path.reverse(); - if (r_path_types) { - r_path_types->reverse(); - } - if (r_path_rids) { - r_path_rids->reverse(); - } - if (r_path_owners) { - r_path_owners->reverse(); - } - - } else { - path.push_back(end_point); - APPEND_METADATA(end_poly); - - // Add mid points - int np_id = least_cost_id; - while (np_id != -1 && navigation_polys[np_id].back_navigation_poly_id != -1) { - if (navigation_polys[np_id].back_navigation_edge != -1) { - int prev = navigation_polys[np_id].back_navigation_edge; - int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size(); - Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5; - - path.push_back(point); - APPEND_METADATA(navigation_polys[np_id].poly); - } else { - path.push_back(navigation_polys[np_id].entry); - APPEND_METADATA(navigation_polys[np_id].poly); - } - - np_id = navigation_polys[np_id].back_navigation_poly_id; - } - - path.push_back(begin_point); - APPEND_METADATA(begin_poly); - - path.reverse(); - if (r_path_types) { - r_path_types->reverse(); - } - if (r_path_rids) { - r_path_rids->reverse(); - } - if (r_path_owners) { - r_path_owners->reverse(); - } - } - - // Ensure post conditions (path arrays MUST match in size). - CRASH_COND(r_path_types && path.size() != r_path_types->size()); - CRASH_COND(r_path_rids && path.size() != r_path_rids->size()); - CRASH_COND(r_path_owners && path.size() != r_path_owners->size()); - - return path; + return NavMeshQueries3D::polygons_get_path( + polygons, p_origin, p_destination, p_optimize, p_navigation_layers, + r_path_types, r_path_rids, r_path_owners, up, link_polygons.size()); } Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const { @@ -607,66 +142,7 @@ Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector return Vector3(); } - bool use_collision = p_use_collision; - Vector3 closest_point; - real_t closest_point_d = FLT_MAX; - - for (const gd::Polygon &p : polygons) { - // For each face check the distance to the segment - for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) { - const Face3 f(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); - Vector3 inters; - if (f.intersects_segment(p_from, p_to, &inters)) { - const real_t d = p_from.distance_to(inters); - if (use_collision == false) { - closest_point = inters; - use_collision = true; - closest_point_d = d; - } else if (closest_point_d > d) { - closest_point = inters; - closest_point_d = d; - } - } - // If segment does not itersect face, check the distance from segment's endpoints. - else if (!use_collision) { - const Vector3 p_from_closest = f.get_closest_point_to(p_from); - const real_t d_p_from = p_from.distance_to(p_from_closest); - if (closest_point_d > d_p_from) { - closest_point = p_from_closest; - closest_point_d = d_p_from; - } - - const Vector3 p_to_closest = f.get_closest_point_to(p_to); - const real_t d_p_to = p_to.distance_to(p_to_closest); - if (closest_point_d > d_p_to) { - closest_point = p_to_closest; - closest_point_d = d_p_to; - } - } - } - // Finally, check for a case when shortest distance is between some point located on a face's edge and some point located on a line segment. - if (!use_collision) { - for (size_t point_id = 0; point_id < p.points.size(); point_id += 1) { - Vector3 a, b; - - Geometry3D::get_closest_points_between_segments( - p_from, - p_to, - p.points[point_id].pos, - p.points[(point_id + 1) % p.points.size()].pos, - a, - b); - - const real_t d = a.distance_to(b); - if (d < closest_point_d) { - closest_point_d = d; - closest_point = b; - } - } - } - } - - return closest_point; + return NavMeshQueries3D::polygons_get_closest_point_to_segment(polygons, p_from, p_to, p_use_collision); } Vector3 NavMap::get_closest_point(const Vector3 &p_point) const { @@ -675,8 +151,8 @@ Vector3 NavMap::get_closest_point(const Vector3 &p_point) const { NAVMAP_ITERATION_ZERO_ERROR_MSG(); return Vector3(); } - gd::ClosestPointQueryResult cp = get_closest_point_info(p_point); - return cp.point; + + return NavMeshQueries3D::polygons_get_closest_point(polygons, p_point); } Vector3 NavMap::get_closest_point_normal(const Vector3 &p_point) const { @@ -685,8 +161,8 @@ Vector3 NavMap::get_closest_point_normal(const Vector3 &p_point) const { NAVMAP_ITERATION_ZERO_ERROR_MSG(); return Vector3(); } - gd::ClosestPointQueryResult cp = get_closest_point_info(p_point); - return cp.normal; + + return NavMeshQueries3D::polygons_get_closest_point_normal(polygons, p_point); } RID NavMap::get_closest_point_owner(const Vector3 &p_point) const { @@ -695,32 +171,14 @@ RID NavMap::get_closest_point_owner(const Vector3 &p_point) const { NAVMAP_ITERATION_ZERO_ERROR_MSG(); return RID(); } - gd::ClosestPointQueryResult cp = get_closest_point_info(p_point); - return cp.owner; + + return NavMeshQueries3D::polygons_get_closest_point_owner(polygons, p_point); } gd::ClosestPointQueryResult NavMap::get_closest_point_info(const Vector3 &p_point) const { RWLockRead read_lock(map_rwlock); - gd::ClosestPointQueryResult result; - real_t closest_point_ds = FLT_MAX; - - for (const gd::Polygon &p : polygons) { - // For each face check the distance to the point - for (size_t point_id = 2; point_id < p.points.size(); point_id += 1) { - const Face3 f(p.points[0].pos, p.points[point_id - 1].pos, p.points[point_id].pos); - const Vector3 inters = f.get_closest_point_to(p_point); - const real_t ds = inters.distance_squared_to(p_point); - if (ds < closest_point_ds) { - result.point = inters; - result.normal = f.get_plane().normal; - result.owner = p.owner->get_self(); - closest_point_ds = ds; - } - } - } - - return result; + return NavMeshQueries3D::polygons_get_closest_point_info(polygons, p_point); } void NavMap::add_region(NavRegion *p_region) { @@ -943,29 +401,30 @@ void NavMap::sync() { } // Resize the polygon count. - int count = 0; + int polygon_count = 0; for (const NavRegion *region : regions) { if (!region->get_enabled()) { continue; } - count += region->get_polygons().size(); + polygon_count += region->get_polygons().size(); } - polygons.resize(count); + polygons.resize(polygon_count); // Copy all region polygons in the map. - count = 0; + polygon_count = 0; for (const NavRegion *region : regions) { if (!region->get_enabled()) { continue; } const LocalVector<gd::Polygon> &polygons_source = region->get_polygons(); for (uint32_t n = 0; n < polygons_source.size(); n++) { - polygons[count + n] = polygons_source[n]; + polygons[polygon_count] = polygons_source[n]; + polygons[polygon_count].id = polygon_count; + polygon_count++; } - count += region->get_polygons().size(); } - _new_pm_polygon_count = polygons.size(); + _new_pm_polygon_count = polygon_count; // Group all edges per key. HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections; @@ -1136,6 +595,7 @@ void NavMap::sync() { // If we have both a start and end point, then create a synthetic polygon to route through. if (closest_start_polygon && closest_end_polygon) { gd::Polygon &new_polygon = link_polygons[link_poly_idx++]; + new_polygon.id = polygon_count++; new_polygon.owner = link; new_polygon.edges.clear(); @@ -1391,39 +851,6 @@ void NavMap::dispatch_callbacks() { } } -void NavMap::clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const { - Vector3 from = path[path.size() - 1]; - - if (from.is_equal_approx(p_to_point)) { - return; - } - Plane cut_plane; - cut_plane.normal = (from - p_to_point).cross(up); - if (cut_plane.normal == Vector3()) { - return; - } - cut_plane.normal.normalize(); - cut_plane.d = cut_plane.normal.dot(from); - - while (from_poly != p_to_poly) { - Vector3 pathway_start = from_poly->back_navigation_edge_pathway_start; - Vector3 pathway_end = from_poly->back_navigation_edge_pathway_end; - - ERR_FAIL_COND(from_poly->back_navigation_poly_id == -1); - from_poly = &p_navigation_polys[from_poly->back_navigation_poly_id]; - - if (!pathway_start.is_equal_approx(pathway_end)) { - Vector3 inters; - if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) { - if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) { - path.push_back(inters); - APPEND_METADATA(from_poly->poly); - } - } - } - } -} - void NavMap::_update_merge_rasterizer_cell_dimensions() { merge_rasterizer_cell_size = cell_size * merge_rasterizer_cell_scale; merge_rasterizer_cell_height = cell_height * merge_rasterizer_cell_scale; diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index c18ee7155b..b9120c04d9 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -232,7 +232,6 @@ private: void compute_single_avoidance_step_2d(uint32_t index, NavAgent **agent); void compute_single_avoidance_step_3d(uint32_t index, NavAgent **agent); - void clip_path(const LocalVector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly, Vector<int32_t> *r_path_types, TypedArray<RID> *r_path_rids, Vector<int64_t> *r_path_owners) const; void _update_rvo_simulation(); void _update_rvo_obstacles_tree_2d(); void _update_rvo_agents_tree_2d(); diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 85510bd416..7a44adecbc 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -32,6 +32,8 @@ #include "nav_map.h" +#include "3d/nav_mesh_queries_3d.h" + void NavRegion::set_map(NavMap *p_map) { if (map == p_map) { return; @@ -108,81 +110,7 @@ Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniform return Vector3(); } - const LocalVector<gd::Polygon> ®ion_polygons = get_polygons(); - - if (region_polygons.is_empty()) { - return Vector3(); - } - - if (p_uniformly) { - real_t accumulated_area = 0; - RBMap<real_t, uint32_t> region_area_map; - - for (uint32_t rp_index = 0; rp_index < region_polygons.size(); rp_index++) { - const gd::Polygon ®ion_polygon = region_polygons[rp_index]; - real_t polyon_area = region_polygon.surface_area; - - if (polyon_area == 0.0) { - continue; - } - region_area_map[accumulated_area] = rp_index; - accumulated_area += polyon_area; - } - if (region_area_map.is_empty() || accumulated_area == 0) { - // All polygons have no real surface / no area. - return Vector3(); - } - - real_t region_area_map_pos = Math::random(real_t(0), accumulated_area); - - RBMap<real_t, uint32_t>::Iterator region_E = region_area_map.find_closest(region_area_map_pos); - ERR_FAIL_COND_V(!region_E, Vector3()); - uint32_t rrp_polygon_index = region_E->value; - ERR_FAIL_UNSIGNED_INDEX_V(rrp_polygon_index, region_polygons.size(), Vector3()); - - const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; - - real_t accumulated_polygon_area = 0; - RBMap<real_t, uint32_t> polygon_area_map; - - for (uint32_t rpp_index = 2; rpp_index < rr_polygon.points.size(); rpp_index++) { - real_t face_area = Face3(rr_polygon.points[0].pos, rr_polygon.points[rpp_index - 1].pos, rr_polygon.points[rpp_index].pos).get_area(); - - if (face_area == 0.0) { - continue; - } - polygon_area_map[accumulated_polygon_area] = rpp_index; - accumulated_polygon_area += face_area; - } - if (polygon_area_map.is_empty() || accumulated_polygon_area == 0) { - // All faces have no real surface / no area. - return Vector3(); - } - - real_t polygon_area_map_pos = Math::random(real_t(0), accumulated_polygon_area); - - RBMap<real_t, uint32_t>::Iterator polygon_E = polygon_area_map.find_closest(polygon_area_map_pos); - ERR_FAIL_COND_V(!polygon_E, Vector3()); - uint32_t rrp_face_index = polygon_E->value; - ERR_FAIL_UNSIGNED_INDEX_V(rrp_face_index, rr_polygon.points.size(), Vector3()); - - const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); - - Vector3 face_random_position = face.get_random_point_inside(); - return face_random_position; - - } else { - uint32_t rrp_polygon_index = Math::random(int(0), region_polygons.size() - 1); - - const gd::Polygon &rr_polygon = region_polygons[rrp_polygon_index]; - - uint32_t rrp_face_index = Math::random(int(2), rr_polygon.points.size() - 1); - - const Face3 face(rr_polygon.points[0].pos, rr_polygon.points[rrp_face_index - 1].pos, rr_polygon.points[rrp_face_index].pos); - - Vector3 face_random_position = face.get_random_point_inside(); - return face_random_position; - } + return NavMeshQueries3D::polygons_get_random_point(get_polygons(), p_navigation_layers, p_uniformly); } bool NavRegion::sync() { diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index c3939e9979..ba4c44b748 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -98,6 +98,9 @@ struct Edge { }; struct Polygon { + /// Id of the polygon in the map. + uint32_t id = UINT32_MAX; + /// Navigation region or link that contains this polygon. const NavBase *owner = nullptr; @@ -111,9 +114,11 @@ struct Polygon { }; struct NavigationPoly { - uint32_t self_id = 0; /// This poly. - const Polygon *poly; + const Polygon *poly = nullptr; + + /// Index in the heap of traversable polygons. + uint32_t traversable_poly_index = UINT32_MAX; /// Those 4 variables are used to travel the path backwards. int back_navigation_poly_id = -1; @@ -123,20 +128,44 @@ struct NavigationPoly { /// The entry position of this poly. Vector3 entry; - /// The distance to the destination. + /// The distance traveled until now (g cost). real_t traveled_distance = 0.0; + /// The distance to the destination (h cost). + real_t distance_to_destination = 0.0; - NavigationPoly() { poly = nullptr; } + /// The total travel cost (f cost). + real_t total_travel_cost() const { + return traveled_distance + distance_to_destination; + } - NavigationPoly(const Polygon *p_poly) : - poly(p_poly) {} + bool operator==(const NavigationPoly &p_other) const { + return poly == p_other.poly; + } - bool operator==(const NavigationPoly &other) const { - return poly == other.poly; + bool operator!=(const NavigationPoly &p_other) const { + return !(*this == p_other); } +}; + +struct NavPolyTravelCostGreaterThan { + // Returns `true` if the travel cost of `a` is higher than that of `b`. + bool operator()(const NavigationPoly *p_poly_a, const NavigationPoly *p_poly_b) const { + real_t f_cost_a = p_poly_a->total_travel_cost(); + real_t h_cost_a = p_poly_a->distance_to_destination; + real_t f_cost_b = p_poly_b->total_travel_cost(); + real_t h_cost_b = p_poly_b->distance_to_destination; - bool operator!=(const NavigationPoly &other) const { - return !operator==(other); + if (f_cost_a != f_cost_b) { + return f_cost_a > f_cost_b; + } else { + return h_cost_a > h_cost_b; + } + } +}; + +struct NavPolyHeapIndexer { + void operator()(NavigationPoly *p_poly, uint32_t p_heap_index) const { + p_poly->traversable_poly_index = p_heap_index; } }; @@ -146,6 +175,129 @@ struct ClosestPointQueryResult { RID owner; }; +template <typename T> +struct NoopIndexer { + void operator()(const T &p_value, uint32_t p_index) {} +}; + +/** + * A max-heap implementation that notifies of element index changes. + */ +template <typename T, typename LessThan = Comparator<T>, typename Indexer = NoopIndexer<T>> +class Heap { + LocalVector<T> _buffer; + + LessThan _less_than; + Indexer _indexer; + +public: + void reserve(uint32_t p_size) { + _buffer.reserve(p_size); + } + + uint32_t size() const { + return _buffer.size(); + } + + bool is_empty() const { + return _buffer.is_empty(); + } + + void push(const T &p_element) { + _buffer.push_back(p_element); + _indexer(p_element, _buffer.size() - 1); + _shift_up(_buffer.size() - 1); + } + + T pop() { + ERR_FAIL_COND_V_MSG(_buffer.is_empty(), T(), "Can't pop an empty heap."); + T value = _buffer[0]; + _indexer(value, UINT32_MAX); + if (_buffer.size() > 1) { + _buffer[0] = _buffer[_buffer.size() - 1]; + _indexer(_buffer[0], 0); + _buffer.remove_at(_buffer.size() - 1); + _shift_down(0); + } else { + _buffer.remove_at(_buffer.size() - 1); + } + return value; + } + + /** + * Update the position of the element in the heap if necessary. + */ + void shift(uint32_t p_index) { + ERR_FAIL_UNSIGNED_INDEX_MSG(p_index, _buffer.size(), "Heap element index is out of range."); + if (!_shift_up(p_index)) { + _shift_down(p_index); + } + } + + void clear() { + for (const T &value : _buffer) { + _indexer(value, UINT32_MAX); + } + _buffer.clear(); + } + + Heap() {} + + Heap(const LessThan &p_less_than) : + _less_than(p_less_than) {} + + Heap(const Indexer &p_indexer) : + _indexer(p_indexer) {} + + Heap(const LessThan &p_less_than, const Indexer &p_indexer) : + _less_than(p_less_than), _indexer(p_indexer) {} + +private: + bool _shift_up(uint32_t p_index) { + T value = _buffer[p_index]; + uint32_t current_index = p_index; + uint32_t parent_index = (current_index - 1) / 2; + while (current_index > 0 && _less_than(_buffer[parent_index], value)) { + _buffer[current_index] = _buffer[parent_index]; + _indexer(_buffer[current_index], current_index); + current_index = parent_index; + parent_index = (current_index - 1) / 2; + } + if (current_index != p_index) { + _buffer[current_index] = value; + _indexer(value, current_index); + return true; + } else { + return false; + } + } + + bool _shift_down(uint32_t p_index) { + T value = _buffer[p_index]; + uint32_t current_index = p_index; + uint32_t child_index = 2 * current_index + 1; + while (child_index < _buffer.size()) { + if (child_index + 1 < _buffer.size() && + _less_than(_buffer[child_index], _buffer[child_index + 1])) { + child_index++; + } + if (_less_than(_buffer[child_index], value)) { + break; + } + _buffer[current_index] = _buffer[child_index]; + _indexer(_buffer[current_index], current_index); + current_index = child_index; + child_index = 2 * current_index + 1; + } + if (current_index != p_index) { + _buffer[current_index] = value; + _indexer(value, current_index); + return true; + } else { + return false; + } + } +}; } // namespace gd #endif // NAV_UTILS_H diff --git a/modules/noise/noise_texture_2d.cpp b/modules/noise/noise_texture_2d.cpp index 0960b2ad36..b55b1141e1 100644 --- a/modules/noise/noise_texture_2d.cpp +++ b/modules/noise/noise_texture_2d.cpp @@ -194,6 +194,9 @@ Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradi void NoiseTexture2D::_update_texture() { bool use_thread = true; +#ifndef THREADS_ENABLED + use_thread = false; +#endif if (first_time) { use_thread = false; first_time = false; diff --git a/modules/noise/noise_texture_3d.cpp b/modules/noise/noise_texture_3d.cpp index 9047491344..e3cca8a09f 100644 --- a/modules/noise/noise_texture_3d.cpp +++ b/modules/noise/noise_texture_3d.cpp @@ -187,6 +187,9 @@ Ref<Image> NoiseTexture3D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradi void NoiseTexture3D::_update_texture() { bool use_thread = true; +#ifndef THREADS_ENABLED + use_thread = false; +#endif if (first_time) { use_thread = false; first_time = false; diff --git a/modules/noise/tests/test_noise_texture_2d.h b/modules/noise/tests/test_noise_texture_2d.h index 938e8fd6ab..0d18d66e74 100644 --- a/modules/noise/tests/test_noise_texture_2d.h +++ b/modules/noise/tests/test_noise_texture_2d.h @@ -135,7 +135,7 @@ TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") { noise_texture->set_noise(noise); CHECK(noise_texture->get_noise() == noise); noise_texture->set_noise(nullptr); - CHECK(noise_texture->get_noise() == nullptr); + CHECK(noise_texture->get_noise().is_null()); noise_texture->set_width(8); noise_texture->set_height(4); @@ -190,7 +190,7 @@ TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") { noise_texture->set_color_ramp(gradient); CHECK(noise_texture->get_color_ramp() == gradient); noise_texture->set_color_ramp(nullptr); - CHECK(noise_texture->get_color_ramp() == nullptr); + CHECK(noise_texture->get_color_ramp().is_null()); } TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") { diff --git a/modules/noise/tests/test_noise_texture_3d.h b/modules/noise/tests/test_noise_texture_3d.h index b708eac43b..434cd20a08 100644 --- a/modules/noise/tests/test_noise_texture_3d.h +++ b/modules/noise/tests/test_noise_texture_3d.h @@ -133,7 +133,7 @@ TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") { noise_texture->set_noise(noise); CHECK(noise_texture->get_noise() == noise); noise_texture->set_noise(nullptr); - CHECK(noise_texture->get_noise() == nullptr); + CHECK(noise_texture->get_noise().is_null()); noise_texture->set_width(8); noise_texture->set_height(4); @@ -174,7 +174,7 @@ TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") { noise_texture->set_color_ramp(gradient); CHECK(noise_texture->get_color_ramp() == gradient); noise_texture->set_color_ramp(nullptr); - CHECK(noise_texture->get_color_ramp() == nullptr); + CHECK(noise_texture->get_color_ramp().is_null()); } TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") { diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 85c0b9ecad..9c366408a0 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -54,8 +54,8 @@ int RegExMatch::_find(const Variant &p_name) const { return -1; } return i; - } else if (p_name.get_type() == Variant::STRING || p_name.get_type() == Variant::STRING_NAME) { - HashMap<String, int>::ConstIterator found = names.find((String)p_name); + } else if (p_name.is_string()) { + HashMap<String, int>::ConstIterator found = names.find(p_name); if (found) { return found->value; } diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h index af58e2487b..7e8e456341 100644 --- a/modules/regex/tests/test_regex.h +++ b/modules/regex/tests/test_regex.h @@ -80,32 +80,32 @@ TEST_CASE("[RegEx] Searching") { REQUIRE(re.is_valid()); Ref<RegExMatch> match = re.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == "ea"); match = re.search(s, 1, 2); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == "e"); match = re.search(s, 2, 4); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == "a"); match = re.search(s, 3, 5); - CHECK(match == nullptr); + CHECK(match.is_null()); match = re.search(s, 6, 2); - CHECK(match == nullptr); + CHECK(match.is_null()); const Array all_results = re.search_all(s); CHECK(all_results.size() == 2); match = all_results[0]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == "ea"); match = all_results[1]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == "i"); CHECK(re.compile(numerics) == OK); CHECK(re.is_valid()); - CHECK(re.search(s) == nullptr); + CHECK(re.search(s).is_null()); CHECK(re.search_all(s).size() == 0); } @@ -168,7 +168,7 @@ TEST_CASE("[RegEx] Uninitialized use") { RegEx re; ERR_PRINT_OFF; - CHECK(re.search(s) == nullptr); + CHECK(re.search(s).is_null()); CHECK(re.search_all(s).size() == 0); CHECK(re.sub(s, "") == ""); CHECK(re.get_group_count() == 0); @@ -237,10 +237,10 @@ TEST_CASE("[RegEx] Invalid end position") { const Array all_results = re.search_all(s, 0, 10); CHECK(all_results.size() == 2); match = all_results[0]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == String("o")); match = all_results[1]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == String("o")); CHECK(re.sub(s, "", true, 0, 10) == "Gdt"); @@ -251,7 +251,7 @@ TEST_CASE("[RegEx] Get match string list") { RegEx re("(Go)(dot)"); Ref<RegExMatch> match = re.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); PackedStringArray result; result.append("Godot"); result.append("Go"); @@ -265,14 +265,14 @@ TEST_CASE("[RegEx] Match start and end positions") { RegEx re1("pattern"); REQUIRE(re1.is_valid()); Ref<RegExMatch> match = re1.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 6); CHECK(match->get_end(0) == 13); RegEx re2("(?<vowel>[aeiou])"); REQUIRE(re2.is_valid()); match = re2.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start("vowel") == 2); CHECK(match->get_end("vowel") == 3); } @@ -307,7 +307,7 @@ TEST_CASE("[RegEx] Simple lookahead") { RegEx re("o(?=t)"); REQUIRE(re.is_valid()); Ref<RegExMatch> match = re.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 3); CHECK(match->get_end(0) == 4); } @@ -325,12 +325,12 @@ TEST_CASE("[RegEx] Lookahead groups empty matches") { CHECK(all_results.size() == 2); match = all_results[0]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == String("")); CHECK(match->get_string(1) == String("12")); match = all_results[1]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_string(0) == String("")); CHECK(match->get_string(1) == String("2")); } @@ -341,7 +341,7 @@ TEST_CASE("[RegEx] Simple lookbehind") { RegEx re("(?<=d)o"); REQUIRE(re.is_valid()); Ref<RegExMatch> match = re.search(s); - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 3); CHECK(match->get_end(0) == 4); } @@ -355,22 +355,22 @@ TEST_CASE("[RegEx] Simple lookbehind search all") { CHECK(all_results.size() == 4); Ref<RegExMatch> match = all_results[0]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 1); CHECK(match->get_end(0) == 2); match = all_results[1]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 3); CHECK(match->get_end(0) == 4); match = all_results[2]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 7); CHECK(match->get_end(0) == 8); match = all_results[3]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 9); CHECK(match->get_end(0) == 10); } @@ -386,7 +386,7 @@ TEST_CASE("[RegEx] Lookbehind groups empty matches") { CHECK(all_results.size() == 3); match = all_results[0]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 2); CHECK(match->get_end(0) == 2); CHECK(match->get_start(1) == 1); @@ -395,7 +395,7 @@ TEST_CASE("[RegEx] Lookbehind groups empty matches") { CHECK(match->get_string(1) == String("b")); match = all_results[1]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 6); CHECK(match->get_end(0) == 6); CHECK(match->get_start(1) == 5); @@ -404,7 +404,7 @@ TEST_CASE("[RegEx] Lookbehind groups empty matches") { CHECK(match->get_string(1) == String("b")); match = all_results[2]; - REQUIRE(match != nullptr); + REQUIRE(match.is_valid()); CHECK(match->get_start(0) == 8); CHECK(match->get_end(0) == 8); CHECK(match->get_start(1) == 7); diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 4bf09d3c84..3322300dda 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -5980,7 +5980,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source int32_t value = values[i]; if (value >= 0) { hb_feature_t feature; - if (keys[i].get_type() == Variant::STRING) { + if (keys[i].is_string()) { feature.tag = _name_to_tag(keys[i]); } else { feature.tag = keys[i]; diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp index 70f0ea346b..6bdb261b50 100644 --- a/modules/upnp/upnp.cpp +++ b/modules/upnp/upnp.cpp @@ -229,14 +229,14 @@ Ref<UPNPDevice> UPNP::get_device(int index) const { } void UPNP::add_device(Ref<UPNPDevice> device) { - ERR_FAIL_NULL(device); + ERR_FAIL_COND(device.is_null()); devices.push_back(device); } void UPNP::set_device(int index, Ref<UPNPDevice> device) { ERR_FAIL_INDEX(index, devices.size()); - ERR_FAIL_NULL(device); + ERR_FAIL_COND(device.is_null()); devices.set(index, device); } @@ -257,7 +257,7 @@ Ref<UPNPDevice> UPNP::get_gateway() const { for (int i = 0; i < devices.size(); i++) { Ref<UPNPDevice> dev = get_device(i); - if (dev != nullptr && dev->is_valid_gateway()) { + if (dev.is_valid() && dev->is_valid_gateway()) { return dev; } } @@ -292,7 +292,7 @@ bool UPNP::is_discover_ipv6() const { String UPNP::query_external_address() const { Ref<UPNPDevice> dev = get_gateway(); - if (dev == nullptr) { + if (dev.is_null()) { return ""; } @@ -302,7 +302,7 @@ String UPNP::query_external_address() const { int UPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { Ref<UPNPDevice> dev = get_gateway(); - if (dev == nullptr) { + if (dev.is_null()) { return UPNP_RESULT_NO_GATEWAY; } @@ -312,7 +312,7 @@ int UPNP::add_port_mapping(int port, int port_internal, String desc, String prot int UPNP::delete_port_mapping(int port, String proto) const { Ref<UPNPDevice> dev = get_gateway(); - if (dev == nullptr) { + if (dev.is_null()) { return UPNP_RESULT_NO_GATEWAY; } diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp index e96d9da7a9..4e182f9787 100644 --- a/modules/zip/zip_packer.cpp +++ b/modules/zip/zip_packer.cpp @@ -48,7 +48,7 @@ Error ZIPPacker::close() { Error err = zipClose(zf, nullptr) == ZIP_OK ? OK : FAILED; if (err == OK) { - DEV_ASSERT(fa == nullptr); + DEV_ASSERT(fa.is_null()); zf = nullptr; } diff --git a/modules/zip/zip_reader.cpp b/modules/zip/zip_reader.cpp index 123d1e5d46..76f48edb69 100644 --- a/modules/zip/zip_reader.cpp +++ b/modules/zip/zip_reader.cpp @@ -48,7 +48,7 @@ Error ZIPReader::close() { Error err = unzClose(uzf) == UNZ_OK ? OK : FAILED; if (err == OK) { - DEV_ASSERT(fa == nullptr); + DEV_ASSERT(fa.is_null()); uzf = nullptr; } |