diff --git a/godot/.clang-format b/godot/.clang-format index a6822dc2..46923aae 100644 --- a/godot/.clang-format +++ b/godot/.clang-format @@ -1,6 +1,6 @@ # Commented out parameters are those with the same value as base LLVM style. # We can uncomment them if we want to change their value, or enforce the -# chosen value in case the base style changes (last sync: Clang 19.1.0). +# chosen value in case the base style changes (last sync: Clang 17.0.6). BasedOnStyle: LLVM AccessModifierOffset: -4 AlignAfterOpenBracket: DontAlign @@ -10,56 +10,30 @@ AlignAfterOpenBracket: DontAlign # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false -# AlignFunctionPointers: false # PadOperators: true # AlignConsecutiveBitFields: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false -# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveDeclarations: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false -# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveMacros: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false # AlignCompound: false -# AlignFunctionPointers: false # PadOperators: false # AlignConsecutiveShortCaseStatements: # Enabled: false # AcrossEmptyLines: false # AcrossComments: false -# AlignCaseArrows: false # AlignCaseColons: false -# AlignConsecutiveTableGenBreakingDAGArgColons: -# Enabled: false -# AcrossEmptyLines: false -# AcrossComments: false -# AlignCompound: false -# AlignFunctionPointers: false -# PadOperators: false -# AlignConsecutiveTableGenCondOperatorColons: -# Enabled: false -# AcrossEmptyLines: false -# AcrossComments: false -# AlignCompound: false -# AlignFunctionPointers: false -# PadOperators: false -# AlignConsecutiveTableGenDefinitionColons: -# Enabled: false -# AcrossEmptyLines: false -# AcrossComments: false -# AlignCompound: false -# AlignFunctionPointers: false -# PadOperators: false # AlignEscapedNewlines: Right AlignOperands: DontAlign AlignTrailingComments: @@ -67,17 +41,17 @@ AlignTrailingComments: OverEmptyLines: 0 # AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false -# AllowBreakBeforeNoexceptSpecifier: Never # AllowShortBlocksOnASingleLine: Never -# AllowShortCaseExpressionOnASingleLine: true # AllowShortCaseLabelsOnASingleLine: false -# AllowShortCompoundRequirementOnASingleLine: true # AllowShortEnumsOnASingleLine: true # AllowShortFunctionsOnASingleLine: All # AllowShortIfStatementsOnASingleLine: Never # AllowShortLambdasOnASingleLine: All # AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None # AlwaysBreakBeforeMultilineStrings: false +# AlwaysBreakTemplateDeclarations: MultiLine # AttributeMacros: # - __capability # BinPackArguments: true @@ -102,10 +76,8 @@ AllowAllParametersOfDeclarationOnNextLine: false # SplitEmptyFunction: true # SplitEmptyRecord: true # SplitEmptyNamespace: true -# BreakAdjacentStringLiterals: true -# BreakAfterAttributes: Leave +# BreakAfterAttributes: Never # BreakAfterJavaFieldAnnotations: false -# BreakAfterReturnType: None # BreakArrays: true # BreakBeforeBinaryOperators: None # BreakBeforeBraces: Attach @@ -113,10 +85,8 @@ AllowAllParametersOfDeclarationOnNextLine: false # BreakBeforeInlineASMColon: OnlyMultiline # BreakBeforeTernaryOperators: true BreakConstructorInitializers: AfterColon -# BreakFunctionDefinitionParameters: false # BreakInheritanceList: BeforeColon # BreakStringLiterals: true -# BreakTemplateDeclarations: MultiLine ColumnLimit: 0 # CommentPragmas: '^ IWYU pragma:' # CompactNamespaces: false @@ -174,16 +144,13 @@ JavaImportGroups: - javax # JavaScriptQuotes: Leave # JavaScriptWrapImports: true -KeepEmptyLines: - AtEndOfFile: false - AtStartOfBlock: false - AtStartOfFile: false +# KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: false # LambdaBodyIndentation: Signature # Language: Cpp # LineEnding: DeriveLF # MacroBlockBegin: '' # MacroBlockEnd: '' -# MainIncludeChar: Quote # MaxEmptyLinesToKeep: 1 # NamespaceIndentation: None # ObjCBinPackProtocolList: Auto @@ -198,7 +165,6 @@ PackConstructorInitializers: NextLine # PenaltyBreakComment: 300 # PenaltyBreakFirstLessLess: 120 # PenaltyBreakOpenParenthesis: 0 -# PenaltyBreakScopeResolution: 500 # PenaltyBreakString: 1000 # PenaltyBreakTemplateDeclaration: 10 # PenaltyExcessCharacter: 1000000 @@ -215,7 +181,6 @@ RemoveSemicolon: true # RequiresExpressionIndentation: OuterScope # SeparateDefinitionBlocks: Leave # ShortNamespaceLines: 1 -# SkipMacroDefinitionBody: false # SortIncludes: CaseSensitive # SortJavaStaticImport: Before # SortUsingDeclarations: LexicographicNumeric @@ -229,6 +194,7 @@ RemoveSemicolon: true # SpaceBeforeCtorInitializerColon: true # SpaceBeforeInheritanceColon: true # SpaceBeforeJsonColon: false +# SpaceBeforeParens: ControlStatements # SpaceBeforeParensOptions: # AfterControlStatements: true # AfterForeachMacros: true @@ -236,7 +202,6 @@ RemoveSemicolon: true # AfterFunctionDefinitionName: false # AfterIfMacros: true # AfterOverloadedOperator: false -# AfterPlacementOperator: true # AfterRequiresInClause: false # AfterRequiresInExpression: false # BeforeNonEmptyParentheses: false @@ -251,7 +216,6 @@ SpacesInLineCommentPrefix: Maximum: -1 # SpacesInParens: Never # SpacesInParensOptions: -# ExceptDoubleParentheses: false # InConditionalStatements: false # InCStyleCasts: false # InEmptyParentheses: false @@ -264,7 +228,6 @@ Standard: c++20 # - Q_UNUSED # - QT_REQUIRE_VERSION TabWidth: 4 -# TableGenBreakInsideDAGArg: DontBreak UseTab: Always # VerilogBreakBetweenInstancePorts: true # WhitespaceSensitiveMacros: diff --git a/godot/.github/actions/godot-deps/action.yml b/godot/.github/actions/godot-deps/action.yml index bd9a1f55..3344323f 100644 --- a/godot/.github/actions/godot-deps/action.yml +++ b/godot/.github/actions/godot-deps/action.yml @@ -10,7 +10,7 @@ inputs: default: x64 scons-version: description: The SCons version to use. - default: 4.8.0 + default: 4.8.1 runs: using: composite diff --git a/godot/.gitrepo b/godot/.gitrepo index f50b074b..f9e8186a 100644 --- a/godot/.gitrepo +++ b/godot/.gitrepo @@ -5,8 +5,8 @@ ; [subrepo] remote = https://github.com/V-Sekai/godot.git - branch = groups-4.4 - commit = 9304241ff66512b3e58ad91bb1b146daefa5c267 - parent = e5bf4e5c918ec7b8fd163d6a9c21a41c7a0bf60b + branch = groups-4.3 + commit = f06e61e56804a88cd81cb0403e9bcf8a54ce452d + parent = bab056e74f03091e9f62902cd30366c5afde19f6 method = merge cmdver = 0.4.9 diff --git a/godot/COPYRIGHT.txt b/godot/COPYRIGHT.txt index 2ece06ee..5cc6f797 100644 --- a/godot/COPYRIGHT.txt +++ b/godot/COPYRIGHT.txt @@ -186,7 +186,7 @@ License: MPL-2.0 Files: ./thirdparty/clipper2/ Comment: Clipper2 -Copyright: 2010-2023, Angus Johnson +Copyright: 2010-2024, Angus Johnson License: BSL-1.0 Files: ./thirdparty/cvtt/ @@ -334,6 +334,11 @@ Comment: WebP codec Copyright: 2010, Google Inc. License: BSD-3-clause +Files: ./thirdparty/manifold/ +Comment: Manifold +Copyright: 2020-2024, The Manifold Authors +License: Apache-2.0 + Files: ./thirdparty/mbedtls/ Comment: Mbed TLS Copyright: The Mbed TLS Contributors diff --git a/godot/core/extension/gdextension_interface.cpp b/godot/core/extension/gdextension_interface.cpp index 203f7960..85d53c31 100644 --- a/godot/core/extension/gdextension_interface.cpp +++ b/godot/core/extension/gdextension_interface.cpp @@ -700,6 +700,91 @@ static GDExtensionTypeFromVariantConstructorFunc gdextension_get_variant_to_type ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type"); } +static GDExtensionVariantGetInternalPtrFunc gdextension_variant_get_ptr_internal_getter(GDExtensionVariantType p_type) { + switch (p_type) { + case GDEXTENSION_VARIANT_TYPE_BOOL: + return reinterpret_cast(static_cast(VariantInternal::get_bool)); + case GDEXTENSION_VARIANT_TYPE_INT: + return reinterpret_cast(static_cast(VariantInternal::get_int)); + case GDEXTENSION_VARIANT_TYPE_FLOAT: + return reinterpret_cast(static_cast(VariantInternal::get_float)); + case GDEXTENSION_VARIANT_TYPE_STRING: + return reinterpret_cast(static_cast(VariantInternal::get_string)); + case GDEXTENSION_VARIANT_TYPE_VECTOR2: + return reinterpret_cast(static_cast(VariantInternal::get_vector2)); + case GDEXTENSION_VARIANT_TYPE_VECTOR2I: + return reinterpret_cast(static_cast(VariantInternal::get_vector2i)); + case GDEXTENSION_VARIANT_TYPE_RECT2: + return reinterpret_cast(static_cast(VariantInternal::get_rect2)); + case GDEXTENSION_VARIANT_TYPE_RECT2I: + return reinterpret_cast(static_cast(VariantInternal::get_rect2i)); + case GDEXTENSION_VARIANT_TYPE_VECTOR3: + return reinterpret_cast(static_cast(VariantInternal::get_vector3)); + case GDEXTENSION_VARIANT_TYPE_VECTOR3I: + return reinterpret_cast(static_cast(VariantInternal::get_vector3i)); + case GDEXTENSION_VARIANT_TYPE_TRANSFORM2D: + return reinterpret_cast(static_cast(VariantInternal::get_transform2d)); + case GDEXTENSION_VARIANT_TYPE_VECTOR4: + return reinterpret_cast(static_cast(VariantInternal::get_vector4)); + case GDEXTENSION_VARIANT_TYPE_VECTOR4I: + return reinterpret_cast(static_cast(VariantInternal::get_vector4i)); + case GDEXTENSION_VARIANT_TYPE_PLANE: + return reinterpret_cast(static_cast(VariantInternal::get_plane)); + case GDEXTENSION_VARIANT_TYPE_QUATERNION: + return reinterpret_cast(static_cast(VariantInternal::get_quaternion)); + case GDEXTENSION_VARIANT_TYPE_AABB: + return reinterpret_cast(static_cast(VariantInternal::get_aabb)); + case GDEXTENSION_VARIANT_TYPE_BASIS: + return reinterpret_cast(static_cast(VariantInternal::get_basis)); + case GDEXTENSION_VARIANT_TYPE_TRANSFORM3D: + return reinterpret_cast(static_cast(VariantInternal::get_transform)); + case GDEXTENSION_VARIANT_TYPE_PROJECTION: + return reinterpret_cast(static_cast(VariantInternal::get_projection)); + case GDEXTENSION_VARIANT_TYPE_COLOR: + return reinterpret_cast(static_cast(VariantInternal::get_color)); + case GDEXTENSION_VARIANT_TYPE_STRING_NAME: + return reinterpret_cast(static_cast(VariantInternal::get_string_name)); + case GDEXTENSION_VARIANT_TYPE_NODE_PATH: + return reinterpret_cast(static_cast(VariantInternal::get_node_path)); + case GDEXTENSION_VARIANT_TYPE_RID: + return reinterpret_cast(static_cast(VariantInternal::get_rid)); + case GDEXTENSION_VARIANT_TYPE_OBJECT: + return reinterpret_cast(static_cast(VariantInternal::get_object)); + case GDEXTENSION_VARIANT_TYPE_CALLABLE: + return reinterpret_cast(static_cast(VariantInternal::get_callable)); + case GDEXTENSION_VARIANT_TYPE_SIGNAL: + return reinterpret_cast(static_cast(VariantInternal::get_signal)); + case GDEXTENSION_VARIANT_TYPE_DICTIONARY: + return reinterpret_cast(static_cast(VariantInternal::get_dictionary)); + case GDEXTENSION_VARIANT_TYPE_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_BYTE_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_byte_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_INT32_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_int32_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_INT64_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_int64_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT32_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_float32_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_FLOAT64_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_float64_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_STRING_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_string_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR2_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_vector2_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR3_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_vector3_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_COLOR_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_color_array)); + case GDEXTENSION_VARIANT_TYPE_PACKED_VECTOR4_ARRAY: + return reinterpret_cast(static_cast(VariantInternal::get_vector4_array)); + case GDEXTENSION_VARIANT_TYPE_NIL: + case GDEXTENSION_VARIANT_TYPE_VARIANT_MAX: + ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type."); + } + ERR_FAIL_V_MSG(nullptr, "Getting Variant get internal pointer function with invalid type."); +} + // ptrcalls static GDExtensionPtrOperatorEvaluator gdextension_variant_get_ptr_operator_evaluator(GDExtensionVariantOperator p_operator, GDExtensionVariantType p_type_a, GDExtensionVariantType p_type_b) { return (GDExtensionPtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b)); @@ -1625,6 +1710,7 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(variant_can_convert_strict); REGISTER_INTERFACE_FUNC(get_variant_from_type_constructor); REGISTER_INTERFACE_FUNC(get_variant_to_type_constructor); + REGISTER_INTERFACE_FUNC(variant_get_ptr_internal_getter); REGISTER_INTERFACE_FUNC(variant_get_ptr_operator_evaluator); REGISTER_INTERFACE_FUNC(variant_get_ptr_builtin_method); REGISTER_INTERFACE_FUNC(variant_get_ptr_constructor); diff --git a/godot/core/extension/gdextension_interface.h b/godot/core/extension/gdextension_interface.h index 374dbfd0..8268afc3 100644 --- a/godot/core/extension/gdextension_interface.h +++ b/godot/core/extension/gdextension_interface.h @@ -198,6 +198,7 @@ typedef struct { typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionUninitializedVariantPtr, GDExtensionTypePtr); typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionUninitializedTypePtr, GDExtensionVariantPtr); +typedef void *(*GDExtensionVariantGetInternalPtrFunc)(GDExtensionVariantPtr); typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result); typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count); typedef void (*GDExtensionPtrConstructor)(GDExtensionUninitializedTypePtr p_base, const GDExtensionConstTypePtr *p_args); @@ -1383,6 +1384,23 @@ typedef GDExtensionVariantFromTypeConstructorFunc (*GDExtensionInterfaceGetVaria */ typedef GDExtensionTypeFromVariantConstructorFunc (*GDExtensionInterfaceGetVariantToTypeConstructor)(GDExtensionVariantType p_type); +/** + * @name variant_get_ptr_internal_getter + * @since 4.4 + * + * Provides a function pointer for retrieving a pointer to a variant's internal value. + * Access to a variant's internal value can be used to modify it in-place, or to retrieve its value without the overhead of variant conversion functions. + * It is recommended to cache the getter for all variant types in a function table to avoid retrieval overhead upon use. + * + * @note Each function assumes the variant's type has already been determined and matches the function. + * Invoking the function with a variant of a mismatched type has undefined behavior, and may lead to a segmentation fault. + * + * @param p_type The Variant type. + * + * @return A pointer to a type-specific function that returns a pointer to the internal value of a variant. Check the implementation of this function (gdextension_variant_get_ptr_internal_getter) for pointee type info of each variant type. + */ +typedef GDExtensionVariantGetInternalPtrFunc (*GDExtensionInterfaceGetVariantGetInternalPtrFunc)(GDExtensionVariantType p_type); + /** * @name variant_get_ptr_operator_evaluator * @since 4.1 diff --git a/godot/core/input/input_map.cpp b/godot/core/input/input_map.cpp index abd2c80c..2c056c4f 100644 --- a/godot/core/input/input_map.cpp +++ b/godot/core/input/input_map.cpp @@ -519,12 +519,15 @@ const HashMap>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_completion_query", inputs); inputs = List>(); - inputs.push_back(InputEventKey::create_reference(Key::ENTER)); - inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); + inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB)); + inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER)); + inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER)); default_builtin_cache.insert("ui_text_completion_accept", inputs); inputs = List>(); inputs.push_back(InputEventKey::create_reference(Key::TAB)); + inputs.push_back(InputEventKey::create_reference(Key::ENTER)); + inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); default_builtin_cache.insert("ui_text_completion_replace", inputs); // Newlines @@ -534,7 +537,6 @@ const HashMap>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_newline", inputs); inputs = List>(); - inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL)); inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_newline_blank", inputs); diff --git a/godot/core/math/projection.cpp b/godot/core/math/projection.cpp index 4a0faef0..20638826 100644 --- a/godot/core/math/projection.cpp +++ b/godot/core/math/projection.cpp @@ -912,14 +912,10 @@ void Projection::set_light_atlas_rect(const Rect2 &p_rect) { } Projection::operator String() const { - String str; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - str += String((j > 0) ? ", " : "\n") + rtos(columns[i][j]); - } - } - - return str; + return "[X: " + columns[0].operator String() + + ", Y: " + columns[1].operator String() + + ", Z: " + columns[2].operator String() + + ", W: " + columns[3].operator String() + "]"; } real_t Projection::get_aspect() const { diff --git a/godot/core/math/vector4.cpp b/godot/core/math/vector4.cpp index b6b914f3..8ac2c4bf 100644 --- a/godot/core/math/vector4.cpp +++ b/godot/core/math/vector4.cpp @@ -213,7 +213,7 @@ Vector4 Vector4::clampf(real_t p_min, real_t p_max) const { } Vector4::operator String() const { - return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")"; + return "(" + String::num_real(x, true) + ", " + String::num_real(y, true) + ", " + String::num_real(z, true) + ", " + String::num_real(w, true) + ")"; } static_assert(sizeof(Vector4) == 4 * sizeof(real_t)); diff --git a/godot/core/os/os.cpp b/godot/core/os/os.cpp index 59a0579c..1e9dbd2a 100644 --- a/godot/core/os/os.cpp +++ b/godot/core/os/os.cpp @@ -536,9 +536,14 @@ bool OS::has_feature(const String &p_feature) { return true; } - if (has_server_feature_callback && has_server_feature_callback(p_feature)) { - return true; + if (has_server_feature_callback) { + return has_server_feature_callback(p_feature); + } +#ifdef DEBUG_ENABLED + else if (is_stdout_verbose()) { + WARN_PRINT_ONCE("Server features cannot be checked before RenderingServer has been created. If you are checking a server feature, consider moving your OS::has_feature call after INITIALIZATION_LEVEL_SERVERS."); } +#endif if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) { return true; diff --git a/godot/core/register_core_types.cpp b/godot/core/register_core_types.cpp index 3a578d01..685ba9d3 100644 --- a/godot/core/register_core_types.cpp +++ b/godot/core/register_core_types.cpp @@ -313,17 +313,28 @@ void register_core_settings() { GLOBAL_DEF("threading/worker_pool/low_priority_thread_ratio", 0.3); } +void register_early_core_singletons() { + GDREGISTER_CLASS(core_bind::Engine); + Engine::get_singleton()->add_singleton(Engine::Singleton("Engine", core_bind::Engine::get_singleton())); + + GDREGISTER_CLASS(ProjectSettings); + Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); + + GDREGISTER_CLASS(core_bind::OS); + Engine::get_singleton()->add_singleton(Engine::Singleton("OS", core_bind::OS::get_singleton())); + + GDREGISTER_CLASS(Time); + Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton())); +} + void register_core_singletons() { OS::get_singleton()->benchmark_begin_measure("Core", "Register Singletons"); - GDREGISTER_CLASS(ProjectSettings); GDREGISTER_ABSTRACT_CLASS(IP); GDREGISTER_CLASS(core_bind::Geometry2D); GDREGISTER_CLASS(core_bind::Geometry3D); GDREGISTER_CLASS(core_bind::ResourceLoader); GDREGISTER_CLASS(core_bind::ResourceSaver); - GDREGISTER_CLASS(core_bind::OS); - GDREGISTER_CLASS(core_bind::Engine); GDREGISTER_CLASS(core_bind::special::ClassDB); GDREGISTER_CLASS(core_bind::Marshalls); GDREGISTER_CLASS(TranslationServer); @@ -331,23 +342,18 @@ void register_core_singletons() { GDREGISTER_CLASS(InputMap); GDREGISTER_CLASS(Expression); GDREGISTER_CLASS(core_bind::EngineDebugger); - GDREGISTER_CLASS(Time); - Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton(), "IP")); Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry2D", core_bind::Geometry2D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("Geometry3D", core_bind::Geometry3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceLoader", core_bind::ResourceLoader::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceSaver", core_bind::ResourceSaver::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("OS", core_bind::OS::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("Engine", core_bind::Engine::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ClassDB", _classdb)); Engine::get_singleton()->add_singleton(Engine::Singleton("Marshalls", core_bind::Marshalls::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("TranslationServer", TranslationServer::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("Input", Input::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("InputMap", InputMap::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", core_bind::EngineDebugger::get_singleton())); - Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool)); diff --git a/godot/core/register_core_types.h b/godot/core/register_core_types.h index eba56927..b8db5c5d 100644 --- a/godot/core/register_core_types.h +++ b/godot/core/register_core_types.h @@ -34,6 +34,7 @@ void register_core_types(); void register_core_settings(); void register_core_extensions(); +void register_early_core_singletons(); void register_core_singletons(); void unregister_core_types(); void unregister_core_extensions(); diff --git a/godot/doc/classes/@GlobalScope.xml b/godot/doc/classes/@GlobalScope.xml index 06aec6a2..182848db 100644 --- a/godot/doc/classes/@GlobalScope.xml +++ b/godot/doc/classes/@GlobalScope.xml @@ -2143,49 +2143,49 @@ Space key. - ! key. + Exclamation mark ([code]![/code]) key. - " key. + Double quotation mark ([code]"[/code]) key. - - # key. + + Number sign or [i]hash[/i] ([code]#[/code]) key. - $ key. + Dollar sign ([code]$[/code]) key. - % key. + Percent sign ([code]%[/code]) key. - & key. + Ampersand ([code]&[/code]) key. - ' key. + Apostrophe ([code]'[/code]) key. - - ( key. + + Left parenthesis ([code]([/code]) key. - - ) key. + + Right parenthesis ([code])[/code]) key. - * key. + Asterisk ([code]*[/code]) key. - + key. + Plus ([code]+[/code]) key. - , key. + Comma ([code],[/code]) key. - - - key. + + Minus ([code]-[/code]) key. - - . key. + + Period ([code].[/code]) key. - / key. + Slash ([code]/[/code]) key. Number 0 key. @@ -2218,25 +2218,25 @@ Number 9 key. - : key. + Colon ([code]:[/code]) key. - ; key. + Semicolon ([code];[/code]) key. - < key. + Less-than sign ([code]<[/code]) key. - = key. + Equal sign ([code]=[/code]) key. - > key. + Greater-than sign ([code]>[/code]) key. - ? key. + Question mark ([code]?[/code]) key. - - @ key. + + At sign ([code]@[/code]) key. A key. @@ -2316,41 +2316,41 @@ Z key. - - [ key. + + Left bracket ([code][lb][/code]) key. - \ key. + Backslash ([code]\[/code]) key. - - ] key. + + Right bracket ([code][rb][/code]) key. - - ^ key. + + Caret ([code]^[/code]) key. - - _ key. + + Underscore ([code]_[/code]) key. - - ` key. + + Backtick ([code]`[/code]) key. - - { key. + + Left brace ([code]{[/code]) key. - - | key. + + Vertical bar or [i]pipe[/i] ([code]|[/code]) key. - - } key. + + Right brace ([code]}[/code]) key. - ~ key. + Tilde ([code]~[/code]) key. - ¥ key. + Yen symbol ([code]¥[/code]) key. - - § key. + + Section sign ([code]§[/code]) key. Key Code mask. diff --git a/godot/doc/classes/AnimationNode.xml b/godot/doc/classes/AnimationNode.xml index 3b7cd9ad..3e3c4f25 100644 --- a/godot/doc/classes/AnimationNode.xml +++ b/godot/doc/classes/AnimationNode.xml @@ -152,6 +152,12 @@ Gets the value of a parameter. Parameters are custom local memory used for your animation nodes, given a resource can be reused in multiple trees. + + + + Returns the object id of the [AnimationTree] that owns this node. This method should only be called from within the [method AnimationNodeExtension._process] method, and will return an invalid id otherwise. + + @@ -159,6 +165,12 @@ Returns whether the given path is filtered. + + + + Returns whether this animation node is being processed in test-only mode. + + diff --git a/godot/doc/classes/AnimationNodeExtension.xml b/godot/doc/classes/AnimationNodeExtension.xml index 1e4c86e5..462387f7 100644 --- a/godot/doc/classes/AnimationNodeExtension.xml +++ b/godot/doc/classes/AnimationNodeExtension.xml @@ -1,10 +1,10 @@ - Base class for extending [AnimationRootNode]s from GDScript, C# or C++. + Base class for extending [AnimationRootNode]s from GDScript, C#, or C++. - [AnimationNodeExtension] exposes the APIs of [AnimationRootNode] to allow users to extend it from GDScript, C# or C++. This class is not meant to be used directly, but to be extended by other classes. It is used to create custom nodes for the [AnimationTree] system. + [AnimationNodeExtension] exposes the APIs of [AnimationRootNode] to allow users to extend it from GDScript, C#, or C++. This class is not meant to be used directly, but to be extended by other classes. It is used to create custom nodes for the [AnimationTree] system. @@ -15,17 +15,11 @@ A version of the [method AnimationNode._process] method that is meant to be overridden by custom nodes. It returns a [PackedFloat32Array] with the processed animation data. - The [PackedFloat64Array] parameter contains the playback information, containing the following values (in order): playback time and delta, start and end times, whether a seek was requested, whether the seek request was externally requested, the current [enum Animation.LoopedFlag] (encoded as a float), and the current blend weight. - The function must return a [PackedFloat32Array] of the node's time info, containing the following values (in order): animation length, time position, delta, [enum Animation.LoopMode] (encoded as a float), whether the animation is about to end and whether the animation is infinite. All values must be included in the returned array. + The [PackedFloat64Array] parameter contains the playback information, containing the following values (in order): playback time and delta, start and end times, whether a seek was requested, whether the seek request was externally requested, the current [enum Animation.LoopedFlag] (encoded as a float), and the current blend weight. + The function must return a [PackedFloat32Array] of the node's time info, containing the following values (in order): animation length, time position, delta, [enum Animation.LoopMode] (encoded as a float), whether the animation is about to end and whether the animation is infinite. All values must be included in the returned array. - - - - Returns the [AnimationTree] that owns this node. This method should only be called from within the [method AnimationNodeExtension._process] method, and will return null otherwise. - - - + diff --git a/godot/doc/classes/CollisionShape3D.xml b/godot/doc/classes/CollisionShape3D.xml index a4e0ed0b..69a7dd2b 100644 --- a/godot/doc/classes/CollisionShape3D.xml +++ b/godot/doc/classes/CollisionShape3D.xml @@ -29,6 +29,12 @@ + + The collision shape color that is displayed in the editor, or in the running project if [b]Debug > Visible Collision Shapes[/b] is checked at the top of the editor. If this is reset to its default value of [code]Color(0, 0, 0, 0)[/code], the value of [member ProjectSettings.debug/shapes/collision/shape_color] will be used instead. + + + If [code]true[/code], when the shape is displayed, it will show a solid fill color in addition to its wireframe. + A disabled collision shape has no effect in the world. diff --git a/godot/doc/classes/Control.xml b/godot/doc/classes/Control.xml index 342e2075..2cf2bdf0 100644 --- a/godot/doc/classes/Control.xml +++ b/godot/doc/classes/Control.xml @@ -12,7 +12,7 @@ Call [method accept_event] so no other node receives the event. Once you accept an input, it becomes handled so [method Node._unhandled_input] will not process it. Only one [Control] node can be in focus. Only the node in focus will receive events. To get the focus, call [method grab_focus]. [Control] nodes lose focus when another node grabs it, or if you hide the node in focus. Sets [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] to tell a [Control] node to ignore mouse or touch events. You'll need it if you place an icon on top of a button. - [Theme] resources change the Control's appearance. If you change the [Theme] on a [Control] node, it affects all of its children. To override some of the theme's parameters, call one of the [code]add_theme_*_override[/code] methods, like [method add_theme_font_override]. You can override the theme with the Inspector. + [Theme] resources change the control's appearance. The [member theme] of a [Control] node affects all of its direct and indirect children (as long as a chain of controls is uninterrupted). To override some of the theme items, call one of the [code]add_theme_*_override[/code] methods, like [method add_theme_font_override]. You can also override theme items in the Inspector. [b]Note:[/b] Theme items are [i]not[/i] [Object] properties. This means you can't access their values using [method Object.get] and [method Object.set]. Instead, use the [code]get_theme_*[/code] and [code]add_theme_*_override[/code] methods provided by this class. @@ -113,7 +113,7 @@ Virtual method to be implemented by the user. Returns the tooltip text for the position [param at_position] in control's local coordinates, which will typically appear when the cursor is resting over this control. See [method get_tooltip]. - [b]Note:[/b] If this method returns an empty [String], no tooltip is displayed. + [b]Note:[/b] If this method returns an empty [String] and [method _make_custom_tooltip] is not overridden, no tooltip is displayed. @@ -164,11 +164,12 @@ - Virtual method to be implemented by the user. Returns a [Control] node that should be used as a tooltip instead of the default one. The [param for_text] includes the contents of the [member tooltip_text] property. + Virtual method to be implemented by the user. Returns a [Control] node that should be used as a tooltip instead of the default one. [param for_text] is the return value of [method get_tooltip]. The returned node must be of type [Control] or Control-derived. It can have child nodes of any type. It is freed when the tooltip disappears, so make sure you always provide a new instance (if you want to use a pre-existing node from your scene tree, you can duplicate it and pass the duplicated instance). When [code]null[/code] or a non-Control node is returned, the default tooltip will be used instead. The returned node will be added as child to a [PopupPanel], so you should only provide the contents of that panel. That [PopupPanel] can be themed using [method Theme.set_stylebox] for the type [code]"TooltipPanel"[/code] (see [member tooltip_text] for an example). [b]Note:[/b] The tooltip is shrunk to minimal size. If you want to ensure it's fully visible, you might want to set its [member custom_minimum_size] to some non-zero value. [b]Note:[/b] The node (and any relevant children) should have their [member CanvasItem.visible] set to [code]true[/code] when returned, otherwise, the viewport that instantiates it will not be able to calculate its minimum size reliably. + [b]Note:[/b] If overridden, this method is called even if [method get_tooltip] returns an empty string. When this happens with the default tooltip, it is not displayed. To copy this behavior, return [code]null[/code] in this method when [param for_text] is empty. [b]Example:[/b] Use a constructed node as a tooltip: [codeblocks] [gdscript] @@ -553,7 +554,7 @@ Returns the tooltip text for the position [param at_position] in control's local coordinates, which will typically appear when the cursor is resting over this control. By default, it returns [member tooltip_text]. This method can be overridden to customize its behavior. See [method _get_tooltip]. - [b]Note:[/b] If this method returns an empty [String], no tooltip is displayed. + [b]Note:[/b] If this method returns an empty [String] and [method _make_custom_tooltip] is not overridden, no tooltip is displayed. @@ -1065,7 +1066,8 @@ [b]Note:[/b] Tooltips customized using [method _make_custom_tooltip] do not use this auto translate mode automatically. - The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [member ProjectSettings.gui/timers/tooltip_delay_sec] option. See also [method get_tooltip]. + The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [member ProjectSettings.gui/timers/tooltip_delay_sec] setting. + This string is the default return value of [method get_tooltip]. Override [method _get_tooltip] to generate tooltip text dynamically. Override [method _make_custom_tooltip] to customize the tooltip interface and behavior. The tooltip popup will use either a default implementation, or a custom one that you can provide by overriding [method _make_custom_tooltip]. The default tooltip includes a [PopupPanel] and [Label] whose theme properties can be customized using [Theme] methods with the [code]"TooltipPanel"[/code] and [code]"TooltipLabel"[/code] respectively. For example: [codeblocks] [gdscript] diff --git a/godot/doc/classes/DisplayServer.xml b/godot/doc/classes/DisplayServer.xml index e2a352de..47611e59 100644 --- a/godot/doc/classes/DisplayServer.xml +++ b/godot/doc/classes/DisplayServer.xml @@ -1135,7 +1135,7 @@ Returns the scale factor of the specified screen by index. [b]Note:[/b] On macOS, the returned value is [code]2.0[/code] for hiDPI (Retina) screens, and [code]1.0[/code] for all other cases. [b]Note:[/b] On Linux (Wayland), the returned value is accurate only when [param screen] is [constant SCREEN_OF_MAIN_WINDOW]. Due to API limitations, passing a direct index will return a rounded-up integer, if the screen has a fractional scale (e.g. [code]1.25[/code] would get rounded up to [code]2.0[/code]). - [b]Note:[/b] This method is implemented only on macOS and Linux (Wayland). + [b]Note:[/b] This method is implemented on Android, iOS, Web, macOS, and Linux (Wayland). diff --git a/godot/doc/classes/LightmapGI.xml b/godot/doc/classes/LightmapGI.xml index 0a492364..70265443 100644 --- a/godot/doc/classes/LightmapGI.xml +++ b/godot/doc/classes/LightmapGI.xml @@ -10,6 +10,7 @@ [b]Note:[/b] Due to how lightmaps work, most properties only have a visible effect once lightmaps are baked again. [b]Note:[/b] Lightmap baking on [CSGShape3D]s and [PrimitiveMesh]es is not supported, as these cannot store UV2 data required for baking. [b]Note:[/b] If no custom lightmappers are installed, [LightmapGI] can only be baked from devices that support the Forward+ or Mobile rendering backends. + [b]Note:[/b] The [LightmapGI] node only bakes light data for child nodes of its parent. Nodes further up the hierarchy of the scene will not be baked. $DOCS_URL/tutorials/3d/global_illumination/using_lightmap_gi.html diff --git a/godot/doc/classes/LookAtModifier3D.xml b/godot/doc/classes/LookAtModifier3D.xml index b6d106c4..2475de18 100644 --- a/godot/doc/classes/LookAtModifier3D.xml +++ b/godot/doc/classes/LookAtModifier3D.xml @@ -32,8 +32,11 @@ - - The bone index of the [Skeleton3D] that the modification will operate on. + + Index of the [member bone_name] in the parent [Skeleton3D]. + + + The bone name of the [Skeleton3D] that the modification will operate on. The duration of the time-based interpolation. Interpolation is triggered at the following cases: @@ -48,6 +51,9 @@ The forward axis of the bone. This [SkeletonModifier3D] modifies the bone so that this axis points toward the [member target_node]. + Index of the [member origin_bone_name] in the parent [Skeleton3D]. + + If [member origin_from] is [constant ORIGIN_FROM_SPECIFIC_BONE], the bone global pose position specified for this is used as origin. diff --git a/godot/doc/classes/MeshLibrary.xml b/godot/doc/classes/MeshLibrary.xml index f65e29af..5f51d18e 100644 --- a/godot/doc/classes/MeshLibrary.xml +++ b/godot/doc/classes/MeshLibrary.xml @@ -45,6 +45,13 @@ Returns the item's mesh. + + + + + Returns the item's shadow casting mode. See [enum RenderingServer.ShadowCastingSetting] for possible values. + + @@ -116,6 +123,14 @@ Sets the item's mesh. + + + + + + Sets the item's shadow casting mode. See [enum RenderingServer.ShadowCastingSetting] for possible values. + + diff --git a/godot/doc/classes/OS.xml b/godot/doc/classes/OS.xml index 5ab7c27f..2389db13 100644 --- a/godot/doc/classes/OS.xml +++ b/godot/doc/classes/OS.xml @@ -721,6 +721,7 @@ - If standard input is console, this method will block until the program receives a line break in standard input (usually by the user pressing [kbd]Enter[/kbd]). - If standard input is pipe, this method will block until a specific amount of data is read or pipe is closed. - If standard input is a file, this method will read a specific amount of data (or less if end-of-file is reached) and return immediately. + [b]Note:[/b] This method automatically replaces [code]\r\n[/code] line breaks with [code]\n[/code] and removes them from the end of the string. Use [method read_buffer_from_stdin] to read the unprocessed data. [b]Note:[/b] This method is implemented on Linux, macOS, and Windows. [b]Note:[/b] On exported Windows builds, run the console wrapper executable to access the terminal. If standard input is console, calling this method without console wrapped will freeze permanently. If standard input is pipe or file, it can be used without console wrapper. If you need a single executable with full console support, use a custom build compiled with the [code]windows_subsystem=console[/code] flag. diff --git a/godot/doc/classes/Object.xml b/godot/doc/classes/Object.xml index d0c193ea..73fd7e19 100644 --- a/godot/doc/classes/Object.xml +++ b/godot/doc/classes/Object.xml @@ -22,6 +22,7 @@ Lastly, every object can also contain metadata (data about data). [method set_meta] can be useful to store information that the object itself does not depend on. To keep your code clean, making excessive use of metadata is discouraged. [b]Note:[/b] Unlike references to a [RefCounted], references to an object stored in a variable can become invalid without being set to [code]null[/code]. To check if an object has been deleted, do [i]not[/i] compare it against [code]null[/code]. Instead, use [method @GlobalScope.is_instance_valid]. It's also recommended to inherit from [RefCounted] for classes storing data instead of [Object]. [b]Note:[/b] The [code]script[/code] is not exposed like most properties. To set or get an object's [Script] in code, use [method set_script] and [method get_script], respectively. + [b]Note:[/b] In a boolean context, an [Object] will evaluate to [code]false[/code] if it is equal to [code]null[/code] or it has been freed. Otherwise, an [Object] will always evaluate to [code]true[/code]. See also [method @GlobalScope.is_instance_valid]. $DOCS_URL/contributing/development/core_and_modules/object_class.html diff --git a/godot/doc/classes/RDTextureFormat.xml b/godot/doc/classes/RDTextureFormat.xml index ac875ab7..617ed954 100644 --- a/godot/doc/classes/RDTextureFormat.xml +++ b/godot/doc/classes/RDTextureFormat.xml @@ -37,6 +37,13 @@ The texture's height (in pixels). + + If a texture is discardable, its contents do not need to be preserved between frames. This flag is only relevant when the texture is used as target in a draw list. + This information is used by [RenderingDevice] to figure out if a texture's contents can be discarded, eliminating unnecessary writes to memory and boosting performance. + + + The texture will be used as the destination of a resolve operation. + The number of mipmaps available in the texture. diff --git a/godot/doc/classes/RDVertexAttribute.xml b/godot/doc/classes/RDVertexAttribute.xml index 31605f54..364b8252 100644 --- a/godot/doc/classes/RDVertexAttribute.xml +++ b/godot/doc/classes/RDVertexAttribute.xml @@ -10,14 +10,19 @@ + The way that this attribute's data is interpreted when sent to a shader. + The rate at which this attribute is pulled from its vertex buffer. + The location in the shader that this attribute is bound to. + The number of bytes between the start of the vertex buffer and the first instance of this attribute. + The number of bytes between the starts of consecutive instances of this attribute. diff --git a/godot/doc/classes/RenderSceneBuffersRD.xml b/godot/doc/classes/RenderSceneBuffersRD.xml index 6a5aba1d..6a9445a2 100644 --- a/godot/doc/classes/RenderSceneBuffersRD.xml +++ b/godot/doc/classes/RenderSceneBuffersRD.xml @@ -30,6 +30,7 @@ + Create a new texture with the given definition and cache this under the given name. Will return the existing texture if it already exists. diff --git a/godot/doc/classes/RenderingDevice.xml b/godot/doc/classes/RenderingDevice.xml index fe23f791..59ca0608 100644 --- a/godot/doc/classes/RenderingDevice.xml +++ b/godot/doc/classes/RenderingDevice.xml @@ -210,15 +210,12 @@ - - - - - - - - - + + + + + + Starts a list of raster drawing commands created with the [code]draw_*[/code] methods. The returned value should be passed to other [code]draw_list_*[/code] functions. Multiple draw lists cannot be created at the same time; you must finish the previous draw list first using [method draw_list_end]. @@ -226,7 +223,7 @@ [codeblock] var rd = RenderingDevice.new() var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)]) - var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS) + var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.CLEAR_COLOR_ALL, clear_colors, true, 1.0f, true, 0, Rect2(), RenderingDevice.OPAQUE_PASS) # Draw opaque. rd.draw_list_bind_render_pipeline(draw_list, raster_pipeline) @@ -241,10 +238,11 @@ rd.draw_list_end() [/codeblock] + The [param draw_flags] indicates if the texture attachments of the framebuffer should be cleared or ignored. Only one of the two flags can be used for each individual attachment. Ignoring an attachment means that any contents that existed before the draw list will be completely discarded, reducing the memory bandwidth used by the render pass but producing garbage results if the pixels aren't replaced. The default behavior allows the engine to figure out the right operation to use if the texture is discardable, which can result in increased performance. See [RDTextureFormat] or [method texture_set_discardable]. The [param breadcrumb] parameter can be an arbitrary 32-bit integer that is useful to diagnose GPU crashes. If Godot is built in dev or debug mode; when the GPU crashes Godot will dump all shaders that were being executed at the time of the crash and the breadcrumb is useful to diagnose what passes did those shaders belong to. It does not affect rendering behavior and can be set to 0. It is recommended to use [enum BreadcrumbMarker] enumerations for consistency but it's not required. It is also possible to use bitwise operations to add extra data. e.g. [codeblock] - rd.draw_list_begin(fb[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS | 5) + rd.draw_list_begin(fb[i], RenderingDevice.CLEAR_COLOR_ALL, clear_colors, true, 1.0f, true, 0, Rect2(), RenderingDevice.OPAQUE_PASS | 5) [/codeblock] @@ -947,6 +945,13 @@ [b]Note:[/b] This function returns a [code]uint64_t[/code] which internally maps to a [code]GLuint[/code] (OpenGL) or [code]VkImage[/code] (Vulkan). + + + + + Returns [code]true[/code] if the [param texture] is discardable, [code]false[/code] otherwise. See [RDTextureFormat] or [method texture_set_discardable]. + + @@ -984,6 +989,16 @@ [b]Note:[/b] [param to_texture] texture must [b]not[/b] be multisampled and must also be 2D (or a slice of a 3D/cubemap texture). + + + + + + Updates the discardable property of [param texture]. + If a texture is discardable, its contents do not need to be preserved between frames. This flag is only relevant when the texture is used as target in a draw list. + This information is used by [RenderingDevice] to figure out if a texture's contents can be discarded, eliminating unnecessary writes to memory and boosting performance. + + @@ -2279,40 +2294,40 @@ - + Load the previous contents of the framebuffer. - + Clear the whole framebuffer or its specified region. - + Ignore the previous contents of the framebuffer. This is the fastest option if you'll overwrite all of the pixels and don't need to read any of them. - + Represents the size of the [enum InitialAction] enum. - + - + - + - + - + - + Store the result of the draw list in the framebuffer. This is generally what you want to do. - + Discard the contents of the framebuffer. This is the fastest option if you don't need to use the results of the draw list. - + Represents the size of the [enum FinalAction] enum. - + - + Vertex shader stage. This can be used to manipulate vertices from a shader (but not create new vertices). @@ -2514,5 +2529,86 @@ + + Do not clear or ignore any attachments. + + + Clear the first color attachment. + + + Clear the second color attachment. + + + Clear the third color attachment. + + + Clear the fourth color attachment. + + + Clear the fifth color attachment. + + + Clear the sixth color attachment. + + + Clear the seventh color attachment. + + + Clear the eighth color attachment. + + + Mask for clearing all color attachments. + + + Clear all color attachments. + + + Ignore the previous contents of the first color attachment. + + + Ignore the previous contents of the second color attachment. + + + Ignore the previous contents of the third color attachment. + + + Ignore the previous contents of the fourth color attachment. + + + Ignore the previous contents of the fifth color attachment. + + + Ignore the previous contents of the sixth color attachment. + + + Ignore the previous contents of the seventh color attachment. + + + Ignore the previous contents of the eighth color attachment. + + + Mask for ignoring all the previous contents of the color attachments. + + + Ignore the previous contents of all color attachments. + + + Clear the depth attachment. + + + Ignore the previous contents of the depth attachment. + + + Clear the stencil attachment. + + + Ignore the previous contents of the stencil attachment. + + + Clear all attachments. + + + Ignore the previous contents of all attachments. + diff --git a/godot/doc/classes/Viewport.xml b/godot/doc/classes/Viewport.xml index 678b7690..3e772c4e 100644 --- a/godot/doc/classes/Viewport.xml +++ b/godot/doc/classes/Viewport.xml @@ -104,6 +104,13 @@ Returns the transform from the Viewport's coordinates to the screen coordinates of the containing window manager window. + + + + Returns the automatically computed 2D stretch transform, taking the [Viewport]'s stretch settings into account. The final value is multiplied by [member Window.content_scale_factor], but only for the root viewport. If this method is called on a [SubViewport] (e.g., in a scene tree with [SubViewportContainer] and [SubViewport]), the scale factor of the root window will not be applied. Using [method Transform2D.get_scale] on the returned value, this can be used to compensate for scaling when zooming a [Camera2D] node, or to scale down a [TextureRect] to be pixel-perfect regardless of the automatically computed scale factor. + [b]Note:[/b] Due to how pixel scaling works, the transform's X scale value may differ slightly from the Y scale, even when [member Window.content_scale_aspect] is set to a mode that preserves pixel aspect ratio. If [member Window.content_scale_aspect] is [constant Window.CONTENT_SCALE_ASPECT_IGNORE], the X value may differ [i]significantly[/i] from Y due to differences between the original aspect ratio and the window aspect ratio. + + diff --git a/godot/doc/classes/Window.xml b/godot/doc/classes/Window.xml index 424941b8..6e833ef2 100644 --- a/godot/doc/classes/Window.xml +++ b/godot/doc/classes/Window.xml @@ -572,7 +572,7 @@ Specifies how the content's aspect behaves when the [Window] is resized. The base aspect is determined by [member content_scale_size]. - Specifies the base scale of [Window]'s content when its [member size] is equal to [member content_scale_size]. + Specifies the base scale of [Window]'s content when its [member size] is equal to [member content_scale_size]. See also [method Viewport.get_stretch_transform]. Specifies how the content is scaled when the [Window] is resized. diff --git a/godot/drivers/d3d12/rendering_context_driver_d3d12.cpp b/godot/drivers/d3d12/rendering_context_driver_d3d12.cpp index 8fa495f5..7cd36f90 100644 --- a/godot/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/godot/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -96,6 +96,9 @@ RenderingContextDriverD3D12::~RenderingContextDriverD3D12() { if (lib_dxgi) { FreeLibrary(lib_dxgi); } + if (lib_dcomp) { + FreeLibrary(lib_dcomp); + } } Error RenderingContextDriverD3D12::_init_device_factory() { @@ -108,6 +111,9 @@ Error RenderingContextDriverD3D12::_init_device_factory() { lib_dxgi = LoadLibraryW(L"DXGI.dll"); ERR_FAIL_NULL_V(lib_dxgi, ERR_CANT_CREATE); + lib_dcomp = LoadLibraryW(L"Dcomp.dll"); + ERR_FAIL_NULL_V(lib_dcomp, ERR_CANT_CREATE); + // Note: symbol is not available in MinGW import library. PFN_D3D12_GET_INTERFACE d3d_D3D12GetInterface = (PFN_D3D12_GET_INTERFACE)(void *)GetProcAddress(lib_d3d12, "D3D12GetInterface"); if (!d3d_D3D12GetInterface) { diff --git a/godot/drivers/d3d12/rendering_context_driver_d3d12.h b/godot/drivers/d3d12/rendering_context_driver_d3d12.h index a2d828de..3eed6444 100644 --- a/godot/drivers/d3d12/rendering_context_driver_d3d12.h +++ b/godot/drivers/d3d12/rendering_context_driver_d3d12.h @@ -59,6 +59,20 @@ #undef AS #endif +#if (WINVER < _WIN32_WINNT_WIN8) && defined(_MSC_VER) +#pragma push_macro("NTDDI_VERSION") +#pragma push_macro("WINVER") +#undef NTDDI_VERSION +#undef WINVER +#define NTDDI_VERSION NTDDI_WIN8 +#define WINVER _WIN32_WINNT_WIN8 +#include +#pragma pop_macro("WINVER") +#pragma pop_macro("NTDDI_VERSION") +#else +#include +#endif + #include "d3dx12.h" #include @@ -114,10 +128,14 @@ class RenderingContextDriverD3D12 : public RenderingContextDriver { uint32_t height = 0; DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED; bool needs_resize = false; + ComPtr composition_device; + ComPtr composition_target; + ComPtr composition_visual; }; HMODULE lib_d3d12 = nullptr; HMODULE lib_dxgi = nullptr; + HMODULE lib_dcomp = nullptr; IDXGIAdapter1 *create_adapter(uint32_t p_adapter_index) const; ID3D12DeviceFactory *device_factory_get() const; diff --git a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp index 0b1b0651..b72a1932 100644 --- a/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/godot/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2469,7 +2469,7 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; if (swap_chain->d3d_swap_chain != nullptr) { _swap_chain_release_buffers(swap_chain); - res = swap_chain->d3d_swap_chain->ResizeBuffers(p_desired_framebuffer_count, 0, 0, DXGI_FORMAT_UNKNOWN, creation_flags); + res = swap_chain->d3d_swap_chain->ResizeBuffers(p_desired_framebuffer_count, surface->width, surface->height, DXGI_FORMAT_UNKNOWN, creation_flags); ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_UNAVAILABLE); } else { swap_chain_desc.BufferCount = p_desired_framebuffer_count; @@ -2478,7 +2478,7 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.Flags = creation_flags; - swap_chain_desc.Scaling = DXGI_SCALING_NONE; + swap_chain_desc.Scaling = DXGI_SCALING_STRETCH; if (OS::get_singleton()->is_layered_allowed()) { swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; has_comp_alpha[(uint64_t)p_cmd_queue.id] = true; @@ -2486,14 +2486,11 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; has_comp_alpha[(uint64_t)p_cmd_queue.id] = false; } + swap_chain_desc.Width = surface->width; + swap_chain_desc.Height = surface->height; ComPtr swap_chain_1; - res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf()); - if (!SUCCEEDED(res) && swap_chain_desc.AlphaMode != DXGI_ALPHA_MODE_IGNORE) { - swap_chain_desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - has_comp_alpha[(uint64_t)p_cmd_queue.id] = false; - res = context_driver->dxgi_factory_get()->CreateSwapChainForHwnd(command_queue->d3d_queue.Get(), surface->hwnd, &swap_chain_desc, nullptr, nullptr, swap_chain_1.GetAddressOf()); - } + res = context_driver->dxgi_factory_get()->CreateSwapChainForComposition(command_queue->d3d_queue.Get(), &swap_chain_desc, nullptr, swap_chain_1.GetAddressOf()); ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); swap_chain_1.As(&swap_chain->d3d_swap_chain); @@ -2503,6 +2500,36 @@ Error RenderingDeviceDriverD3D12::swap_chain_resize(CommandQueueID p_cmd_queue, ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); } + if (surface->composition_device.Get() == nullptr) { + using PFN_DCompositionCreateDevice = HRESULT(WINAPI *)(IDXGIDevice *, REFIID, void **); + PFN_DCompositionCreateDevice pfn_DCompositionCreateDevice = (PFN_DCompositionCreateDevice)(void *)GetProcAddress(context_driver->lib_dcomp, "DCompositionCreateDevice"); + ERR_FAIL_NULL_V(pfn_DCompositionCreateDevice, ERR_CANT_CREATE); + + res = pfn_DCompositionCreateDevice(nullptr, IID_PPV_ARGS(surface->composition_device.GetAddressOf())); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_device->CreateTargetForHwnd(surface->hwnd, TRUE, surface->composition_target.GetAddressOf()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_device->CreateVisual(surface->composition_visual.GetAddressOf()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_visual->SetContent(swap_chain->d3d_swap_chain.Get()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_target->SetRoot(surface->composition_visual.Get()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_device->Commit(); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + } else { + res = surface->composition_visual->SetContent(swap_chain->d3d_swap_chain.Get()); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + + res = surface->composition_device->Commit(); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + } + res = swap_chain->d3d_swap_chain->GetDesc1(&swap_chain_desc); ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); ERR_FAIL_COND_V(swap_chain_desc.BufferCount == 0, ERR_CANT_CREATE); diff --git a/godot/drivers/gles3/rasterizer_scene_gles3.cpp b/godot/drivers/gles3/rasterizer_scene_gles3.cpp index dfc7d02a..17bdcbca 100644 --- a/godot/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/godot/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3314,10 +3314,6 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, spec_constants); - - prev_shader = shader; - prev_variant = instance_variant; - prev_spec_constants = spec_constants; } // Pass in lighting uniforms. @@ -3355,7 +3351,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } // Pass light count and array of light indices for base pass. - if ((prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) && pass == 0) { + if ((prev_inst != inst || prev_shader != shader || prev_variant != instance_variant || prev_spec_constants != spec_constants) && pass == 0) { // Rebind the light indices. material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_gl_cache.size(), shader->version, instance_variant, spec_constants); material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_gl_cache.size(), shader->version, instance_variant, spec_constants); @@ -3412,11 +3408,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } else if (inst->lightmap_sh) { glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast(inst->lightmap_sh->sh)); } - prev_inst = inst; } } + prev_shader = shader; + prev_variant = instance_variant; + prev_spec_constants = spec_constants; + // Pass in reflection probe data if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { if (pass == 0 && inst->reflection_probe_rid_cache.size() > 0) { diff --git a/godot/drivers/unix/os_unix.cpp b/godot/drivers/unix/os_unix.cpp index ffc270cd..299ac653 100644 --- a/godot/drivers/unix/os_unix.cpp +++ b/godot/drivers/unix/os_unix.cpp @@ -191,7 +191,7 @@ String OS_Unix::get_stdin_string(int64_t p_buffer_size) { Vector data; data.resize(p_buffer_size); if (fgets((char *)data.ptrw(), data.size(), stdin)) { - return String::utf8((char *)data.ptr()); + return String::utf8((char *)data.ptr()).replace("\r\n", "\n").rstrip("\n"); } return String(); } diff --git a/godot/editor/animation_track_editor.cpp b/godot/editor/animation_track_editor.cpp index 19e0647f..1a612a3e 100644 --- a/godot/editor/animation_track_editor.cpp +++ b/godot/editor/animation_track_editor.cpp @@ -1400,12 +1400,26 @@ void AnimationTimelineEdit::_anim_loop_pressed() { undo_redo->add_undo_method(this, "update_values"); undo_redo->commit_action(); } else { - String base_path = animation->get_path(); - if (FileAccess::exists(base_path + ".import")) { - EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from imported scene.")); + String base = animation->get_path(); + int srpos = base.find("::"); + if (srpos != -1) { + base = animation->get_path().substr(0, srpos); + } + + if (FileAccess::exists(base + ".import")) { + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from an imported scene.\n\nTo change this animation's loop mode, navigate to the scene's Advanced Import settings and select the animation.\nYou can then change the loop mode from the inspector menu.")); + } else { + EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from an imported resource.")); + } } else { - EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another scene.")); + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another scene.\n\nYou must open this scene and change the animation's loop mode from there.")); + } else { + EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another resource.")); + } } + update_values(); } } @@ -6628,6 +6642,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { case EDIT_SCALE_SELECTION: case EDIT_SCALE_FROM_CURSOR: { scale_dialog->popup_centered(Size2(200, 100) * EDSCALE); + scale->get_line_edit()->grab_focus(); } break; case EDIT_SCALE_CONFIRM: { if (selection.is_empty()) { @@ -7875,10 +7890,13 @@ AnimationTrackEditor::AnimationTrackEditor() { scale->set_min(-99999); scale->set_max(99999); scale->set_step(0.001); + scale->set_select_all_on_focus(true); vbc->add_margin_child(TTR("Scale Ratio:"), scale); - scale_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM)); + scale_dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM), CONNECT_DEFERRED); add_child(scale_dialog); + scale_dialog->register_text_enter(scale->get_line_edit()); + // ease_dialog = memnew(ConfirmationDialog); ease_dialog->set_title(TTR("Select Transition and Easing")); diff --git a/godot/editor/create_dialog.cpp b/godot/editor/create_dialog.cpp index 2273014f..c66adb63 100644 --- a/godot/editor/create_dialog.cpp +++ b/godot/editor/create_dialog.cpp @@ -764,6 +764,7 @@ CreateDialog::CreateDialog() { favorites->connect("cell_selected", callable_mp(this, &CreateDialog::_favorite_selected)); favorites->connect("item_activated", callable_mp(this, &CreateDialog::_favorite_activated)); favorites->add_theme_constant_override("draw_guides", 1); + favorites->set_theme_type_variation("TreeSecondary"); SET_DRAG_FORWARDING_GCD(favorites, CreateDialog); fav_vb->add_margin_child(TTR("Favorites:"), favorites, true); @@ -779,6 +780,7 @@ CreateDialog::CreateDialog() { recent->connect(SceneStringName(item_selected), callable_mp(this, &CreateDialog::_history_selected)); recent->connect("item_activated", callable_mp(this, &CreateDialog::_history_activated)); recent->add_theme_constant_override("draw_guides", 1); + recent->set_theme_type_variation("ItemListSecondary"); VBoxContainer *vbc = memnew(VBoxContainer); vbc->set_custom_minimum_size(Size2(300, 0) * EDSCALE); diff --git a/godot/editor/debugger/editor_performance_profiler.cpp b/godot/editor/debugger/editor_performance_profiler.cpp index 1ea9a665..baa57b02 100644 --- a/godot/editor/debugger/editor_performance_profiler.cpp +++ b/godot/editor/debugger/editor_performance_profiler.cpp @@ -81,6 +81,9 @@ void EditorPerformanceProfiler::Monitor::reset() { String EditorPerformanceProfiler::_create_label(float p_value, Performance::MonitorType p_type) { switch (p_type) { + case Performance::MONITOR_TYPE_QUANTITY: { + return TS->format_number(itos(p_value)); + } case Performance::MONITOR_TYPE_MEMORY: { return String::humanize_size(p_value); } @@ -401,6 +404,7 @@ EditorPerformanceProfiler::EditorPerformanceProfiler() { monitor_tree->connect("item_edited", callable_mp(this, &EditorPerformanceProfiler::_monitor_select)); monitor_tree->create_item(); monitor_tree->set_hide_root(true); + monitor_tree->set_theme_type_variation("TreeSecondary"); add_child(monitor_tree); monitor_draw = memnew(Control); diff --git a/godot/editor/debugger/editor_profiler.cpp b/godot/editor/debugger/editor_profiler.cpp index 33fa208f..87e57967 100644 --- a/godot/editor/debugger/editor_profiler.cpp +++ b/godot/editor/debugger/editor_profiler.cpp @@ -712,6 +712,7 @@ EditorProfiler::EditorProfiler() { variables->set_column_expand(2, false); variables->set_column_clip_content(2, true); variables->set_column_custom_minimum_width(2, 50 * EDSCALE); + variables->set_theme_type_variation("TreeSecondary"); variables->connect("item_edited", callable_mp(this, &EditorProfiler::_item_edited)); graph = memnew(TextureRect); diff --git a/godot/editor/debugger/editor_visual_profiler.cpp b/godot/editor/debugger/editor_visual_profiler.cpp index 9a83277e..0b2304b0 100644 --- a/godot/editor/debugger/editor_visual_profiler.cpp +++ b/godot/editor/debugger/editor_visual_profiler.cpp @@ -807,6 +807,7 @@ EditorVisualProfiler::EditorVisualProfiler() { variables->set_column_expand(2, false); variables->set_column_clip_content(2, true); variables->set_column_custom_minimum_width(2, 75 * EDSCALE); + variables->set_theme_type_variation("TreeSecondary"); variables->connect("cell_selected", callable_mp(this, &EditorVisualProfiler::_item_selected)); graph = memnew(TextureRect); diff --git a/godot/editor/debugger/script_editor_debugger.cpp b/godot/editor/debugger/script_editor_debugger.cpp index da59450d..6eb9cd14 100644 --- a/godot/editor/debugger/script_editor_debugger.cpp +++ b/godot/editor/debugger/script_editor_debugger.cpp @@ -1062,6 +1062,7 @@ void ScriptEditorDebugger::_update_buttons_state() { for (KeyValue &I : threads_debugged) { threadss.push_back(&I.value); } + threads->set_disabled(threadss.is_empty()); threadss.sort_custom(); threads->clear(); @@ -1921,6 +1922,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { stack_dump->set_column_title(0, TTR("Stack Frames")); stack_dump->set_hide_root(true); stack_dump->set_v_size_flags(SIZE_EXPAND_FILL); + stack_dump->set_theme_type_variation("TreeSecondary"); stack_dump->connect("cell_selected", callable_mp(this, &ScriptEditorDebugger::_stack_dump_frame_selected)); stack_vb->add_child(stack_dump); @@ -1956,6 +1958,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { breakpoints_tree->set_allow_reselect(true); breakpoints_tree->set_allow_rmb_select(true); breakpoints_tree->set_hide_root(true); + breakpoints_tree->set_theme_type_variation("TreeSecondary"); breakpoints_tree->connect("item_mouse_selected", callable_mp(this, &ScriptEditorDebugger::_breakpoints_item_rmb_selected)); breakpoints_tree->create_item(); diff --git a/godot/editor/dependency_editor.cpp b/godot/editor/dependency_editor.cpp index 9ca12070..ba57806a 100644 --- a/godot/editor/dependency_editor.cpp +++ b/godot/editor/dependency_editor.cpp @@ -527,6 +527,20 @@ void DependencyRemoveDialog::_build_removed_dependency_tree(const Vectorclear(); + + for (const String &s : dirs_to_delete) { + String t = s.trim_prefix("res://"); + files_to_delete_list->add_item(t, Ref(), false); + } + + for (const String &s : files_to_delete) { + String t = s.trim_prefix("res://"); + files_to_delete_list->add_item(t, Ref(), false); + } +} + void DependencyRemoveDialog::show(const Vector &p_folders, const Vector &p_files) { all_remove_files.clear(); dirs_to_delete.clear(); @@ -543,21 +557,24 @@ void DependencyRemoveDialog::show(const Vector &p_folders, const Vector< files_to_delete.push_back(p_files[i]); } + _show_files_to_delete_list(); + Vector removed_deps; _find_all_removed_dependencies(EditorFileSystem::get_singleton()->get_filesystem(), removed_deps); _find_localization_remaps_of_removed_files(removed_deps); removed_deps.sort(); if (removed_deps.is_empty()) { - owners->hide(); + vb_owners->hide(); text->set_text(TTR("Remove the selected files from the project? (Cannot be undone.)\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently.")); reset_size(); popup_centered(); } else { _build_removed_dependency_tree(removed_deps); - owners->show(); + vb_owners->show(); text->set_text(TTR("The files being removed are required by other resources in order for them to work.\nRemove them anyway? (Cannot be undone.)\nDepending on your filesystem configuration, the files will either be moved to the system trash or deleted permanently.")); popup_centered(Size2(500, 350)); } + EditorFileSystem::get_singleton()->scan_changes(); } @@ -666,15 +683,38 @@ DependencyRemoveDialog::DependencyRemoveDialog() { set_ok_button_text(TTR("Remove")); VBoxContainer *vb = memnew(VBoxContainer); + vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); add_child(vb); text = memnew(Label); vb->add_child(text); + Label *files_to_delete_label = memnew(Label); + files_to_delete_label->set_theme_type_variation("HeaderSmall"); + files_to_delete_label->set_text(TTR("Files to be deleted:")); + vb->add_child(files_to_delete_label); + + files_to_delete_list = memnew(ItemList); + files_to_delete_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + files_to_delete_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + files_to_delete_list->set_custom_minimum_size(Size2(0, 94) * EDSCALE); + vb->add_child(files_to_delete_list); + + vb_owners = memnew(VBoxContainer); + vb_owners->set_h_size_flags(Control::SIZE_EXPAND_FILL); + vb_owners->set_v_size_flags(Control::SIZE_EXPAND_FILL); + vb->add_child(vb_owners); + + Label *owners_label = memnew(Label); + owners_label->set_theme_type_variation("HeaderSmall"); + owners_label->set_text(TTR("Dependencies of files to be deleted:")); + vb_owners->add_child(owners_label); + owners = memnew(Tree); owners->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); owners->set_hide_root(true); - vb->add_child(owners); + owners->set_custom_minimum_size(Size2(0, 94) * EDSCALE); + vb_owners->add_child(owners); owners->set_v_size_flags(Control::SIZE_EXPAND_FILL); } diff --git a/godot/editor/dependency_editor.h b/godot/editor/dependency_editor.h index 0256f399..93954cbd 100644 --- a/godot/editor/dependency_editor.h +++ b/godot/editor/dependency_editor.h @@ -31,6 +31,7 @@ #ifndef DEPENDENCY_EDITOR_H #define DEPENDENCY_EDITOR_H +#include "scene/gui/box_container.h" #include "scene/gui/dialogs.h" #include "scene/gui/item_list.h" #include "scene/gui/tab_container.h" @@ -98,6 +99,8 @@ class DependencyRemoveDialog : public ConfirmationDialog { Label *text = nullptr; Tree *owners = nullptr; + VBoxContainer *vb_owners = nullptr; + ItemList *files_to_delete_list = nullptr; HashMap all_remove_files; Vector dirs_to_delete; @@ -122,6 +125,7 @@ class DependencyRemoveDialog : public ConfirmationDialog { void _find_all_removed_dependencies(EditorFileSystemDirectory *efsd, Vector &p_removed); void _find_localization_remaps_of_removed_files(Vector &p_removed); void _build_removed_dependency_tree(const Vector &p_removed); + void _show_files_to_delete_list(); void ok_pressed() override; diff --git a/godot/editor/editor_asset_installer.cpp b/godot/editor/editor_asset_installer.cpp index 72755e89..5035900f 100644 --- a/godot/editor/editor_asset_installer.cpp +++ b/godot/editor/editor_asset_installer.cpp @@ -735,6 +735,7 @@ EditorAssetInstaller::EditorAssetInstaller() { source_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); source_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); source_tree->connect("item_edited", callable_mp(this, &EditorAssetInstaller::_item_checked_cbk)); + source_tree->set_theme_type_variation("TreeSecondary"); source_tree_vb->add_child(source_tree); VBoxContainer *destination_tree_vb = memnew(VBoxContainer); diff --git a/godot/editor/editor_audio_buses.cpp b/godot/editor/editor_audio_buses.cpp index 06492722..103a649d 100644 --- a/godot/editor/editor_audio_buses.cpp +++ b/godot/editor/editor_audio_buses.cpp @@ -959,6 +959,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { effects->set_allow_rmb_select(true); effects->set_focus_mode(FOCUS_CLICK); effects->set_allow_reselect(true); + effects->set_theme_type_variation("TreeSecondary"); effects->connect(SceneStringName(gui_input), callable_mp(this, &EditorAudioBus::_effects_gui_input)); send = memnew(OptionButton); diff --git a/godot/editor/editor_feature_profile.cpp b/godot/editor/editor_feature_profile.cpp index 9cf10c0e..9b6c387f 100644 --- a/godot/editor/editor_feature_profile.cpp +++ b/godot/editor/editor_feature_profile.cpp @@ -989,6 +989,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() { class_list->connect("cell_selected", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_selected)); class_list->connect("item_edited", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_edited), CONNECT_DEFERRED); class_list->connect("item_collapsed", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_collapsed)); + class_list->set_theme_type_variation("TreeSecondary"); // It will be displayed once the user creates or chooses a profile. class_list_vbc->hide(); diff --git a/godot/editor/editor_inspector.cpp b/godot/editor/editor_inspector.cpp index 6e837560..1a973d7b 100644 --- a/godot/editor/editor_inspector.cpp +++ b/godot/editor/editor_inspector.cpp @@ -3472,6 +3472,14 @@ void EditorInspector::update_tree() { editors.append_array(late_editors); + const Node *node = Object::cast_to(object); + + Vector sstack; + if (node != nullptr) { + const Node *es = EditorNode::get_singleton()->get_edited_scene(); + sstack = PropertyUtils::get_node_states_stack(node, es); + } + for (int i = 0; i < editors.size(); i++) { EditorProperty *ep = Object::cast_to(editors[i].property_editor); const Vector &properties = editors[i].properties; @@ -3525,7 +3533,15 @@ void EditorInspector::update_tree() { ep->set_checked(checked); ep->set_keying(keying); ep->set_read_only(property_read_only || all_read_only); - ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); + if (p.name.begins_with("metadata/")) { + Variant _default = Variant(); + if (node != nullptr) { + _default = PropertyUtils::get_property_default_value(node, p.name, nullptr, &sstack, false, nullptr, nullptr); + } + ep->set_deletable(_default == Variant()); + } else { + ep->set_deletable(deletable_properties); + } } if (ep && ep->is_favoritable() && current_favorites.has(p.name)) { diff --git a/godot/editor/editor_interface.cpp b/godot/editor/editor_interface.cpp index 1d732f0c..3cdcc91a 100644 --- a/godot/editor/editor_interface.cpp +++ b/godot/editor/editor_interface.cpp @@ -103,7 +103,7 @@ EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const { return EditorUndoRedoManager::get_singleton(); } -AABB EditorInterface::_calculate_aabb_for_scene(Node *p_node, AABB &scene_aabb) { +AABB EditorInterface::_calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb) { MeshInstance3D *mesh_node = Object::cast_to(p_node); if (mesh_node && mesh_node->get_mesh().is_valid()) { Transform3D accum_xform; @@ -114,14 +114,14 @@ AABB EditorInterface::_calculate_aabb_for_scene(Node *p_node, AABB &scene_aabb) } AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb()); - scene_aabb.merge_with(aabb); + p_scene_aabb.merge_with(aabb); } for (int i = 0; i < p_node->get_child_count(); i++) { - scene_aabb = _calculate_aabb_for_scene(p_node->get_child(i), scene_aabb); + p_scene_aabb = _calculate_aabb_for_scene(p_node->get_child(i), p_scene_aabb); } - return scene_aabb; + return p_scene_aabb; } TypedArray EditorInterface::_make_mesh_previews(const TypedArray &p_meshes, int p_preview_size) { @@ -230,7 +230,7 @@ Vector> EditorInterface::make_mesh_previews(const Vectoris_inside_tree(), "The scene must not be inside the tree."); ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be called from the editor."); @@ -314,10 +314,11 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in // Cleanup the viewport. if (sub_viewport_node) { - sub_viewport_node->queue_free(); if (sub_viewport_node->get_parent()) { sub_viewport_node->get_parent()->remove_child(sub_viewport_node); } + sub_viewport_node->queue_free(); + sub_viewport_node = nullptr; } // Now generate the cache image. diff --git a/godot/editor/editor_interface.h b/godot/editor/editor_interface.h index 86a99714..92d98120 100644 --- a/godot/editor/editor_interface.h +++ b/godot/editor/editor_interface.h @@ -79,7 +79,7 @@ class EditorInterface : public Object { // Editor tools. TypedArray _make_mesh_previews(const TypedArray &p_meshes, int p_preview_size); - AABB _calculate_aabb_for_scene(Node *p_node, AABB &scene_aabb); + AABB _calculate_aabb_for_scene(Node *p_node, AABB &p_scene_aabb); protected: static void _bind_methods(); diff --git a/godot/editor/editor_node.cpp b/godot/editor/editor_node.cpp index f056a477..222696a6 100644 --- a/godot/editor/editor_node.cpp +++ b/godot/editor/editor_node.cpp @@ -5190,7 +5190,8 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) { _close_save_scene_progress(); accept->set_ok_button_text(p_title); accept->set_text(p_text); - EditorInterface::get_singleton()->popup_dialog_centered(accept); + accept->reset_size(); + EditorInterface::get_singleton()->popup_dialog_centered_clamped(accept, Size2i(), 0.0); } } @@ -5200,7 +5201,8 @@ void EditorNode::show_save_accept(const String &p_text, const String &p_title) { _close_save_scene_progress(); save_accept->set_ok_button_text(p_title); save_accept->set_text(p_text); - EditorInterface::get_singleton()->popup_dialog_centered(save_accept); + save_accept->reset_size(); + EditorInterface::get_singleton()->popup_dialog_centered_clamped(save_accept, Size2i(), 0.0); } } @@ -5209,7 +5211,8 @@ void EditorNode::show_warning(const String &p_text, const String &p_title) { _close_save_scene_progress(); warning->set_text(p_text); warning->set_title(p_title); - EditorInterface::get_singleton()->popup_dialog_centered(warning); + warning->reset_size(); + EditorInterface::get_singleton()->popup_dialog_centered_clamped(warning, Size2i(), 0.0); } else { WARN_PRINT(p_title + " " + p_text); } diff --git a/godot/editor/editor_properties.cpp b/godot/editor/editor_properties.cpp index 2b2b32eb..b600e49b 100644 --- a/godot/editor/editor_properties.cpp +++ b/godot/editor/editor_properties.cpp @@ -52,6 +52,7 @@ #include "scene/3d/fog_volume.h" #include "scene/3d/gpu_particles_3d.h" #include "scene/gui/color_picker.h" +#include "scene/gui/grid_container.h" #include "scene/main/window.h" #include "scene/resources/font.h" #include "scene/resources/mesh.h" @@ -2645,7 +2646,7 @@ EditorPropertyColor::EditorPropertyColor() { add_child(picker); picker->set_flat(true); picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed)); - picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed)); + picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED); picker->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(picker->get_picker())); picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_picker_opening)); } diff --git a/godot/editor/editor_sectioned_inspector.cpp b/godot/editor/editor_sectioned_inspector.cpp index d88cc4d5..75eace83 100644 --- a/godot/editor/editor_sectioned_inspector.cpp +++ b/godot/editor/editor_sectioned_inspector.cpp @@ -353,6 +353,7 @@ SectionedInspector::SectionedInspector() : sections->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); sections->set_v_size_flags(SIZE_EXPAND_FILL); sections->set_hide_root(true); + sections->set_theme_type_variation("TreeSecondary"); left_vb->add_child(sections, true); diff --git a/godot/editor/export/project_export.cpp b/godot/editor/export/project_export.cpp index 0fc62416..f5194eb7 100644 --- a/godot/editor/export/project_export.cpp +++ b/godot/editor/export/project_export.cpp @@ -1416,6 +1416,7 @@ ProjectExportDialog::ProjectExportDialog() { preset_vb->add_child(mc); mc->set_v_size_flags(Control::SIZE_EXPAND_FILL); presets = memnew(ItemList); + presets->set_theme_type_variation("ItemListSecondary"); presets->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); SET_DRAG_FORWARDING_GCD(presets, ProjectExportDialog); mc->add_child(presets); diff --git a/godot/editor/filesystem_dock.cpp b/godot/editor/filesystem_dock.cpp index c7e12d1f..0de2f525 100644 --- a/godot/editor/filesystem_dock.cpp +++ b/godot/editor/filesystem_dock.cpp @@ -4146,6 +4146,7 @@ FileSystemDock::FileSystemDock() { files = memnew(FileSystemList); files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_select_mode(ItemList::SELECT_MULTI); + files->set_theme_type_variation("ItemListSecondary"); SET_DRAG_FORWARDING_GCD(files, FileSystemDock); files->connect("item_clicked", callable_mp(this, &FileSystemDock::_file_list_item_clicked)); files->connect(SceneStringName(gui_input), callable_mp(this, &FileSystemDock::_file_list_gui_input)); diff --git a/godot/editor/gui/editor_file_dialog.cpp b/godot/editor/gui/editor_file_dialog.cpp index 03816098..2e86bfb3 100644 --- a/godot/editor/gui/editor_file_dialog.cpp +++ b/godot/editor/gui/editor_file_dialog.cpp @@ -2317,6 +2317,7 @@ EditorFileDialog::EditorFileDialog() { favorites->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); fav_vb->add_child(favorites); favorites->set_v_size_flags(Control::SIZE_EXPAND_FILL); + favorites->set_theme_type_variation("ItemListSecondary"); favorites->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_favorite_selected)); VBoxContainer *rec_vb = memnew(VBoxContainer); @@ -2326,6 +2327,7 @@ EditorFileDialog::EditorFileDialog() { recent = memnew(ItemList); recent->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); recent->set_allow_reselect(true); + recent->set_theme_type_variation("ItemListSecondary"); rec_vb->add_margin_child(TTR("Recent:"), recent, true); recent->connect(SceneStringName(item_selected), callable_mp(this, &EditorFileDialog::_recent_selected)); diff --git a/godot/editor/import/3d/resource_importer_scene.cpp b/godot/editor/import/3d/resource_importer_scene.cpp index ef747e73..9ab97abd 100644 --- a/godot/editor/import/3d/resource_importer_scene.cpp +++ b/godot/editor/import/3d/resource_importer_scene.cpp @@ -2821,7 +2821,7 @@ void ResourceImporterScene::_generate_editor_preview_for_scene(const String &p_p return; } ERR_FAIL_COND_MSG(p_path.is_empty(), "Path is empty, cannot generate preview."); - ERR_FAIL_COND_MSG(!p_scene, "Scene is null, cannot generate preview."); + ERR_FAIL_NULL_MSG(p_scene, "Scene is null, cannot generate preview."); EditorInterface::get_singleton()->make_scene_preview(p_path, p_scene, 1024); } diff --git a/godot/editor/plugins/animation_player_editor_plugin.cpp b/godot/editor/plugins/animation_player_editor_plugin.cpp index 4edd021b..818ac341 100644 --- a/godot/editor/plugins/animation_player_editor_plugin.cpp +++ b/godot/editor/plugins/animation_player_editor_plugin.cpp @@ -901,7 +901,7 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { player->connect(SNAME("animation_list_changed"), callable_mp(this, &AnimationPlayerEditor::_animation_libraries_updated), CONNECT_DEFERRED); } if (!player->is_connected(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed))) { - player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed), CONNECT_DEFERRED); + player->connect(SNAME("current_animation_changed"), callable_mp(this, &AnimationPlayerEditor::_current_animation_changed)); } } diff --git a/godot/editor/plugins/camera_3d_editor_plugin.cpp b/godot/editor/plugins/camera_3d_editor_plugin.cpp index f4116ed3..10ed7d19 100644 --- a/godot/editor/plugins/camera_3d_editor_plugin.cpp +++ b/godot/editor/plugins/camera_3d_editor_plugin.cpp @@ -30,8 +30,11 @@ #include "camera_3d_editor_plugin.h" +#include "core/config/project_settings.h" #include "editor/editor_node.h" #include "node_3d_editor_plugin.h" +#include "scene/gui/texture_rect.h" +#include "scene/main/viewport.h" void Camera3DEditor::_node_removed(Node *p_node) { if (p_node == node) { @@ -76,9 +79,35 @@ Camera3DEditor::Camera3DEditor() { preview->connect(SceneStringName(pressed), callable_mp(this, &Camera3DEditor::_pressed)); } +void Camera3DPreview::_update_sub_viewport_size() { + sub_viewport->set_size(Node3DEditor::get_camera_viewport_size(camera)); +} + +Camera3DPreview::Camera3DPreview(Camera3D *p_camera) : + TexturePreview(nullptr, false), camera(p_camera), sub_viewport(memnew(SubViewport)) { + RenderingServer::get_singleton()->viewport_attach_camera(sub_viewport->get_viewport_rid(), camera->get_camera()); + add_child(sub_viewport); + + TextureRect *display = get_texture_display(); + display->set_texture(sub_viewport->get_texture()); + sub_viewport->connect("size_changed", callable_mp((CanvasItem *)display, &CanvasItem::queue_redraw)); + + ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &Camera3DPreview::_update_sub_viewport_size)); + _update_sub_viewport_size(); +} + +bool EditorInspectorPluginCamera3DPreview::can_handle(Object *p_object) { + return Object::cast_to(p_object) != nullptr; +} + +void EditorInspectorPluginCamera3DPreview::parse_begin(Object *p_object) { + Camera3D *camera = Object::cast_to(p_object); + Camera3DPreview *preview = memnew(Camera3DPreview(camera)); + add_custom_control(preview); +} + void Camera3DEditorPlugin::edit(Object *p_object) { Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); - //camera_editor->edit(Object::cast_to(p_object)); } bool Camera3DEditorPlugin::handles(Object *p_object) const { @@ -86,27 +115,15 @@ bool Camera3DEditorPlugin::handles(Object *p_object) const { } void Camera3DEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - //Node3DEditor::get_singleton()->set_can_preview(Object::cast_to(p_object)); - } else { + if (!p_visible) { Node3DEditor::get_singleton()->set_can_preview(nullptr); } } Camera3DEditorPlugin::Camera3DEditorPlugin() { - /* camera_editor = memnew( CameraEditor ); - EditorNode::get_singleton()->get_main_screen_control()->add_child(camera_editor); - - camera_editor->set_anchor(SIDE_LEFT,Control::ANCHOR_END); - camera_editor->set_anchor(SIDE_RIGHT,Control::ANCHOR_END); - camera_editor->set_offset(SIDE_LEFT,60); - camera_editor->set_offset(SIDE_RIGHT,0); - camera_editor->set_offset(SIDE_TOP,0); - camera_editor->set_offset(SIDE_BOTTOM,10); - - - camera_editor->hide(); -*/ + Ref plugin; + plugin.instantiate(); + add_inspector_plugin(plugin); } Camera3DEditorPlugin::~Camera3DEditorPlugin() { diff --git a/godot/editor/plugins/camera_3d_editor_plugin.h b/godot/editor/plugins/camera_3d_editor_plugin.h index 1c6838aa..98d0bcc4 100644 --- a/godot/editor/plugins/camera_3d_editor_plugin.h +++ b/godot/editor/plugins/camera_3d_editor_plugin.h @@ -32,7 +32,10 @@ #define CAMERA_3D_EDITOR_PLUGIN_H #include "editor/plugins/editor_plugin.h" -#include "scene/3d/camera_3d.h" +#include "editor/plugins/texture_editor_plugin.h" + +class Camera3D; +class SubViewport; class Camera3DEditor : public Control { GDCLASS(Camera3DEditor, Control); @@ -51,11 +54,29 @@ class Camera3DEditor : public Control { Camera3DEditor(); }; +class Camera3DPreview : public TexturePreview { + GDCLASS(Camera3DPreview, TexturePreview); + + Camera3D *camera = nullptr; + SubViewport *sub_viewport = nullptr; + + void _update_sub_viewport_size(); + +public: + Camera3DPreview(Camera3D *p_camera); +}; + +class EditorInspectorPluginCamera3DPreview : public EditorInspectorPluginTexture { + GDCLASS(EditorInspectorPluginCamera3DPreview, EditorInspectorPluginTexture); + +public: + virtual bool can_handle(Object *p_object) override; + virtual void parse_begin(Object *p_object) override; +}; + class Camera3DEditorPlugin : public EditorPlugin { GDCLASS(Camera3DEditorPlugin, EditorPlugin); - //CameraEditor *camera_editor; - public: virtual String get_name() const override { return "Camera3D"; } bool has_main_screen() const override { return false; } diff --git a/godot/editor/plugins/canvas_item_editor_plugin.cpp b/godot/editor/plugins/canvas_item_editor_plugin.cpp index b56a233d..f8a14b48 100644 --- a/godot/editor/plugins/canvas_item_editor_plugin.cpp +++ b/godot/editor/plugins/canvas_item_editor_plugin.cpp @@ -1998,15 +1998,14 @@ bool CanvasItemEditor::_gui_input_scale(const Ref &p_event) { } } + Transform2D edit_transform; + bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y); + if (using_temp_pivot) { + edit_transform = Transform2D(drag_selection.front()->get()->_edit_get_rotation(), temp_pivot); + } else { + edit_transform = drag_selection.front()->get()->_edit_get_transform(); + } for (CanvasItem *ci : drag_selection) { - Transform2D edit_transform; - bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y); - if (using_temp_pivot) { - edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); - } else { - edit_transform = ci->_edit_get_transform(); - } - Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); Transform2D unscaled_transform = (transform * parent_xform * edit_transform).orthonormalized(); Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; diff --git a/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index 63a48d41..359bb590 100644 --- a/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -46,24 +46,6 @@ Camera3DGizmoPlugin::Camera3DGizmoPlugin() { create_handle_material("handles"); } -Size2i Camera3DGizmoPlugin::_get_viewport_size(Camera3D *p_camera) { - Viewport *viewport = p_camera->get_viewport(); - - Window *window = Object::cast_to(viewport); - if (window) { - return window->get_size(); - } - - SubViewport *sub_viewport = Object::cast_to(viewport); - ERR_FAIL_NULL_V(sub_viewport, Size2i()); - - if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) { - return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); - } - - return sub_viewport->get_size(); -} - bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { return Object::cast_to(p_spatial) != nullptr; } @@ -166,7 +148,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Ref material = get_material("camera_material", p_gizmo); Ref icon = get_material("camera_icon", p_gizmo); - const Size2i viewport_size = _get_viewport_size(camera); + const Size2i viewport_size = Node3DEditor::get_camera_viewport_size(camera); const real_t viewport_aspect = viewport_size.x > 0 && viewport_size.y > 0 ? viewport_size.aspect() : 1.0; const Size2 size_factor = viewport_aspect > 1.0 ? Size2(1.0, 1.0 / viewport_aspect) : Size2(viewport_aspect, 1.0); diff --git a/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.h b/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.h index ba65ebb8..94238704 100644 --- a/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.h +++ b/godot/editor/plugins/gizmos/camera_3d_gizmo_plugin.h @@ -37,7 +37,6 @@ class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin); private: - static Size2i _get_viewport_size(Camera3D *p_camera); static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform); public: diff --git a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 573c686d..b92abbcf 100644 --- a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -49,17 +49,47 @@ CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() { helper.instantiate(); - const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color(); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); + + create_collision_material("shape_material", 2.0); + create_collision_material("shape_material_arraymesh", 0.0625); + + create_collision_material("shape_material_disabled", 0.0625); + create_collision_material("shape_material_arraymesh_disabled", 0.015625); + create_handle_material("handles"); } CollisionShape3DGizmoPlugin::~CollisionShape3DGizmoPlugin() { } +void CollisionShape3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) { + Vector> mats; + + const Color collision_color(1.0, 1.0, 1.0, p_alpha); + + for (int i = 0; i < 4; i++) { + bool instantiated = i < 2; + + Ref material = memnew(StandardMaterial3D); + + Color color = collision_color; + color.a *= instantiated ? 0.25 : 1.0; + + material->set_albedo(color); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + material->set_cull_mode(StandardMaterial3D::CULL_BACK); + material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + mats.push_back(material); + } + + materials[p_name] = mats; +} + bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { return Object::cast_to(p_spatial) != nullptr; } @@ -311,9 +341,20 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { return; } - const Ref material = + const Ref material = get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - Ref handles_material = get_material("handles"); + const Ref material_arraymesh = + get_material(!cs->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo); + const Ref handles_material = get_material("handles"); + + const Color collision_color = cs->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : cs->get_debug_color(); + + if (cs->get_debug_fill_enabled()) { + Ref array_mesh = s->get_debug_arraymesh_faces(collision_color); + if (array_mesh.is_valid()) { + p_gizmo->add_mesh(array_mesh, material_arraymesh); + } + } if (Object::cast_to(*s)) { Ref sp = s; @@ -351,7 +392,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { collision_segments.push_back(Vector3(b.x, b.y, 0)); } - p_gizmo->add_lines(points, material); + p_gizmo->add_lines(points, material, false, collision_color); p_gizmo->add_collision_segments(collision_segments); Vector handles; handles.push_back(Vector3(r, 0, 0)); @@ -374,7 +415,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { const Vector handles = helper->box_get_handles(bs->get_size()); - p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(lines, material, false, collision_color); p_gizmo->add_collision_segments(lines); p_gizmo->add_handles(handles, handles_material); } @@ -412,7 +453,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { points.push_back(Vector3(b.y, b.x, 0) + dud); } - p_gizmo->add_lines(points, material); + p_gizmo->add_lines(points, material, false, collision_color); Vector collision_segments; @@ -476,7 +517,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } } - p_gizmo->add_lines(points, material); + p_gizmo->add_lines(points, material, false, collision_color); Vector collision_segments; @@ -531,7 +572,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p.normal * p.d + p.normal * 3 }; - p_gizmo->add_lines(points, material); + p_gizmo->add_lines(points, material, false, collision_color); p_gizmo->add_collision_segments(points); } @@ -549,7 +590,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a]; lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b]; } - p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(lines, material, false, collision_color); p_gizmo->add_collision_segments(lines); } } @@ -558,7 +599,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (Object::cast_to(*s)) { Ref cs2 = s; Ref mesh = cs2->get_debug_mesh(); - p_gizmo->add_mesh(mesh, material); + p_gizmo->add_lines(cs2->get_debug_mesh_lines(), material, false, collision_color); p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); } @@ -569,7 +610,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3(), Vector3(0, 0, rs->get_length()) }; - p_gizmo->add_lines(points, material); + p_gizmo->add_lines(points, material, false, collision_color); p_gizmo->add_collision_segments(points); Vector handles; handles.push_back(Vector3(0, 0, rs->get_length())); @@ -579,7 +620,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (Object::cast_to(*s)) { Ref hms = s; - Ref mesh = hms->get_debug_mesh(); - p_gizmo->add_mesh(mesh, material); + Vector lines = hms->get_debug_mesh_lines(); + p_gizmo->add_lines(lines, material, false, collision_color); } } diff --git a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h index 464012ac..09590fba 100644 --- a/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h +++ b/godot/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h @@ -38,6 +38,8 @@ class Gizmo3DHelper; class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); + void create_collision_material(const String &p_name, float p_alpha); + Ref helper; public: diff --git a/godot/editor/plugins/mesh_library_editor_plugin.cpp b/godot/editor/plugins/mesh_library_editor_plugin.cpp index 6f79ab05..39401e27 100644 --- a/godot/editor/plugins/mesh_library_editor_plugin.cpp +++ b/godot/editor/plugins/mesh_library_editor_plugin.cpp @@ -156,6 +156,25 @@ void MeshLibraryEditor::_import_scene_parse_node(Ref p_library, Has } p_library->set_item_mesh(item_id, item_mesh); + GeometryInstance3D::ShadowCastingSetting gi3d_cast_shadows_setting = mesh_instance_node->get_cast_shadows_setting(); + switch (gi3d_cast_shadows_setting) { + case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF: { + p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF); + } break; + case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON: { + p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED: { + p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED); + } break; + case GeometryInstance3D::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY: { + p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY); + } break; + default: { + p_library->set_item_mesh_cast_shadow(item_id, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + } + Transform3D item_mesh_transform; if (p_apply_xforms) { item_mesh_transform = mesh_instance_node->get_transform(); diff --git a/godot/editor/plugins/node_3d_editor_gizmos.cpp b/godot/editor/plugins/node_3d_editor_gizmos.cpp index 8aff3c9a..b716e925 100644 --- a/godot/editor/plugins/node_3d_editor_gizmos.cpp +++ b/godot/editor/plugins/node_3d_editor_gizmos.cpp @@ -292,14 +292,11 @@ void EditorNode3DGizmo::add_vertices(const Vector &p_vertices, const Re Vector color; color.resize(p_vertices.size()); + const Color vertex_color = (is_selected() ? Color(1, 1, 1, 0.8) : Color(1, 1, 1, 0.2)) * p_modulate; { Color *w = color.ptrw(); for (int i = 0; i < p_vertices.size(); i++) { - if (is_selected()) { - w[i] = Color(1, 1, 1, 0.8) * p_modulate; - } else { - w[i] = Color(1, 1, 1, 0.2) * p_modulate; - } + w[i] = vertex_color; } } diff --git a/godot/editor/plugins/node_3d_editor_plugin.cpp b/godot/editor/plugins/node_3d_editor_plugin.cpp index 4d6bbbf2..7db19cab 100644 --- a/godot/editor/plugins/node_3d_editor_plugin.cpp +++ b/godot/editor/plugins/node_3d_editor_plugin.cpp @@ -92,6 +92,7 @@ #include "scene/gui/center_container.h" #include "scene/gui/color_picker.h" #include "scene/gui/flow_container.h" +#include "scene/gui/separator.h" #include "scene/gui/split_container.h" #include "scene/gui/subviewport_container.h" #include "scene/resources/3d/sky_material.h" @@ -8822,7 +8823,12 @@ Node3DEditor::Node3DEditor() { ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), Key::O); ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), Key::F); ED_SHORTCUT_ARRAY("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), - { int32_t(KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::KP_0), int32_t(KeyModifierMask::ALT | KeyModifierMask::CMD_OR_CTRL | Key::M) }); + { int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::KP_0), + int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::M), + int32_t(KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::G) }); + ED_SHORTCUT_OVERRIDE_ARRAY("spatial_editor/align_transform_with_view", "macos", + { int32_t(KeyModifierMask::ALT | KeyModifierMask::META | Key::KP_0), + int32_t(KeyModifierMask::ALT | KeyModifierMask::META | Key::G) }); ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KeyModifierMask::ALT + KeyModifierMask::CMD_OR_CTRL + Key::F); ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KeyModifierMask::SHIFT + Key::F); ED_SHORTCUT("spatial_editor/decrease_fov", TTR("Decrease Field of View"), KeyModifierMask::CMD_OR_CTRL + Key::EQUAL); // Usually direct access key for `KEY_PLUS`. @@ -9306,6 +9312,24 @@ void Node3DEditorPlugin::set_state(const Dictionary &p_state) { spatial_editor->set_state(p_state); } +Size2i Node3DEditor::get_camera_viewport_size(Camera3D *p_camera) { + Viewport *viewport = p_camera->get_viewport(); + + Window *window = Object::cast_to(viewport); + if (window) { + return window->get_size(); + } + + SubViewport *sub_viewport = Object::cast_to(viewport); + ERR_FAIL_NULL_V(sub_viewport, Size2i()); + + if (sub_viewport == EditorNode::get_singleton()->get_scene_root()) { + return Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); + } + + return sub_viewport->get_size(); +} + Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const { if (is_snap_enabled()) { real_t snap = get_translate_snap(); diff --git a/godot/editor/plugins/node_3d_editor_plugin.h b/godot/editor/plugins/node_3d_editor_plugin.h index d35fcb76..27db7f27 100644 --- a/godot/editor/plugins/node_3d_editor_plugin.h +++ b/godot/editor/plugins/node_3d_editor_plugin.h @@ -880,6 +880,8 @@ class Node3DEditor : public VBoxContainer { public: static Node3DEditor *get_singleton() { return singleton; } + static Size2i get_camera_viewport_size(Camera3D *p_camera); + Vector3 snap_point(Vector3 p_target, Vector3 p_start = Vector3(0, 0, 0)) const; float get_znear() const { return settings_znear->get_value(); } diff --git a/godot/editor/plugins/plugin_config_dialog.cpp b/godot/editor/plugins/plugin_config_dialog.cpp index af9efda9..c3e87c50 100644 --- a/godot/editor/plugins/plugin_config_dialog.cpp +++ b/godot/editor/plugins/plugin_config_dialog.cpp @@ -305,7 +305,7 @@ PluginConfigDialog::PluginConfigDialog() { grid->add_child(script_name_label); script_edit = memnew(LineEdit); - script_edit->set_tooltip_text(TTR("Optional. The path to the script (relative to the add-on folder). If left empty, will default to \"plugin.gd\".")); + script_edit->set_tooltip_text(TTR("Optional. The name of the script file. If left empty, will default to the subfolder name.")); script_edit->set_placeholder("\"plugin.gd\" -> res://addons/my_plugin/plugin.gd"); script_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); grid->add_child(script_edit); diff --git a/godot/editor/plugins/script_editor_plugin.cpp b/godot/editor/plugins/script_editor_plugin.cpp index 49ecbac7..0b583699 100644 --- a/godot/editor/plugins/script_editor_plugin.cpp +++ b/godot/editor/plugins/script_editor_plugin.cpp @@ -64,6 +64,8 @@ #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" #include "editor/window_wrapper.h" +#include "scene/gui/separator.h" +#include "scene/gui/texture_rect.h" #include "scene/main/node.h" #include "scene/main/window.h" #include "script_text_editor.h" @@ -4154,6 +4156,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { scripts_vbox->add_child(script_list); script_list->set_custom_minimum_size(Size2(100, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing script_list->set_v_size_flags(SIZE_EXPAND_FILL); + script_list->set_theme_type_variation("ItemListSecondary"); script_split->set_split_offset(200 * EDSCALE); _sort_list_on_update = true; script_list->connect("item_clicked", callable_mp(this, &ScriptEditor::_script_list_clicked), CONNECT_DEFERRED); @@ -4197,6 +4200,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { members_overview = memnew(ItemList); members_overview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + members_overview->set_theme_type_variation("ItemListSecondary"); overview_vbox->add_child(members_overview); members_overview->set_allow_reselect(true); @@ -4206,6 +4210,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { help_overview = memnew(ItemList); help_overview->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + help_overview->set_theme_type_variation("ItemListSecondary"); overview_vbox->add_child(help_overview); help_overview->set_allow_reselect(true); help_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing diff --git a/godot/editor/plugins/script_text_editor.cpp b/godot/editor/plugins/script_text_editor.cpp index cf586c79..a28d709b 100644 --- a/godot/editor/plugins/script_text_editor.cpp +++ b/godot/editor/plugins/script_text_editor.cpp @@ -40,6 +40,7 @@ #include "editor/editor_string_names.h" #include "editor/gui/editor_toaster.h" #include "editor/themes/editor_scale.h" +#include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/split_container.h" diff --git a/godot/editor/plugins/shader_editor_plugin.cpp b/godot/editor/plugins/shader_editor_plugin.cpp index 5166619f..3c6d2e88 100644 --- a/godot/editor/plugins/shader_editor_plugin.cpp +++ b/godot/editor/plugins/shader_editor_plugin.cpp @@ -821,6 +821,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { shader_list = memnew(ItemList); shader_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + shader_list->set_theme_type_variation("ItemListSecondary"); left_panel->add_child(shader_list); shader_list->connect(SceneStringName(item_selected), callable_mp(this, &ShaderEditorPlugin::_shader_selected)); shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked)); diff --git a/godot/editor/plugins/shader_file_editor_plugin.cpp b/godot/editor/plugins/shader_file_editor_plugin.cpp index d2fd9b1c..25a02d60 100644 --- a/godot/editor/plugins/shader_file_editor_plugin.cpp +++ b/godot/editor/plugins/shader_file_editor_plugin.cpp @@ -257,6 +257,7 @@ ShaderFileEditor::ShaderFileEditor() { versions->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); versions->connect(SceneStringName(item_selected), callable_mp(this, &ShaderFileEditor::_version_selected)); versions->set_custom_minimum_size(Size2i(200 * EDSCALE, 0)); + versions->set_theme_type_variation("TreeSecondary"); main_hs->add_child(versions); VBoxContainer *main_vb = memnew(VBoxContainer); diff --git a/godot/editor/plugins/skeleton_3d_editor_plugin.cpp b/godot/editor/plugins/skeleton_3d_editor_plugin.cpp index 369a1fc8..1a7c4253 100644 --- a/godot/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/godot/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1103,6 +1103,7 @@ void Skeleton3DEditor::create_editors() { joint_tree->set_v_size_flags(SIZE_EXPAND_FILL); joint_tree->set_h_size_flags(SIZE_EXPAND_FILL); joint_tree->set_allow_rmb_select(true); + joint_tree->set_theme_type_variation("TreeSecondary"); SET_DRAG_FORWARDING_GCD(joint_tree, Skeleton3DEditor); s_con->add_child(joint_tree); diff --git a/godot/editor/plugins/sprite_frames_editor_plugin.cpp b/godot/editor/plugins/sprite_frames_editor_plugin.cpp index f29ace1d..968256d4 100644 --- a/godot/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/godot/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1956,6 +1956,7 @@ SpriteFramesEditor::SpriteFramesEditor() { // HACK: The cell_selected signal is emitted before the FPS spinbox loses focus and applies the change. animations->connect("cell_selected", callable_mp(this, &SpriteFramesEditor::_animation_selected), CONNECT_DEFERRED); animations->connect("item_edited", callable_mp(this, &SpriteFramesEditor::_animation_name_edited)); + animations->set_theme_type_variation("TreeSecondary"); animations->set_allow_reselect(true); add_anim->set_shortcut_context(animations); diff --git a/godot/editor/plugins/theme_editor_plugin.cpp b/godot/editor/plugins/theme_editor_plugin.cpp index de75ed8c..5e4c8617 100644 --- a/godot/editor/plugins/theme_editor_plugin.cpp +++ b/godot/editor/plugins/theme_editor_plugin.cpp @@ -48,6 +48,8 @@ #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/spin_box.h" #include "scene/gui/split_container.h" #include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" @@ -938,6 +940,7 @@ ThemeItemImportTree::ThemeItemImportTree() { import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM_DATA, 80 * EDSCALE); import_items_tree->set_column_clip_content(1, true); import_items_tree->set_column_clip_content(2, true); + import_items_tree->set_theme_type_variation("TreeSecondary"); ScrollContainer *import_bulk_sc = memnew(ScrollContainer); import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE); @@ -1937,6 +1940,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_dialog_side_vb->add_child(edit_type_list); edit_type_list->connect(SceneStringName(item_selected), callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected)); edit_type_list->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed)); + edit_type_list->set_theme_type_variation("TreeSecondary"); Label *edit_add_type_label = memnew(Label); edit_add_type_label->set_text(TTR("Add Type:")); diff --git a/godot/editor/plugins/theme_editor_preview.cpp b/godot/editor/plugins/theme_editor_preview.cpp index ea5e8a8a..3790b23f 100644 --- a/godot/editor/plugins/theme_editor_preview.cpp +++ b/godot/editor/plugins/theme_editor_preview.cpp @@ -41,9 +41,15 @@ #include "scene/gui/check_button.h" #include "scene/gui/color_picker.h" #include "scene/gui/color_rect.h" +#include "scene/gui/label.h" #include "scene/gui/margin_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/panel.h" #include "scene/gui/progress_bar.h" #include "scene/gui/scroll_container.h" +#include "scene/gui/separator.h" +#include "scene/gui/spin_box.h" #include "scene/gui/tab_container.h" #include "scene/gui/text_edit.h" #include "scene/gui/tree.h" diff --git a/godot/editor/plugins/tiles/atlas_merging_dialog.cpp b/godot/editor/plugins/tiles/atlas_merging_dialog.cpp index e25005f9..03229574 100644 --- a/godot/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/godot/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -318,6 +318,7 @@ AtlasMergingDialog::AtlasMergingDialog() { atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200)); atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI); + atlas_merging_atlases_list->set_theme_type_variation("ItemListSecondary"); atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2)); atlas_merging_h_split_container->add_child(atlas_merging_atlases_list); diff --git a/godot/editor/plugins/tiles/tile_map_layer_editor.cpp b/godot/editor/plugins/tiles/tile_map_layer_editor.cpp index 16d4ee6f..52e3784e 100644 --- a/godot/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/godot/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2415,6 +2415,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() { sources_list->set_stretch_ratio(0.25); sources_list->set_custom_minimum_size(Size2(70, 0) * EDSCALE); sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + sources_list->set_theme_type_variation("ItemListSecondary"); sources_list->connect(SceneStringName(item_selected), callable_mp(this, &TileMapLayerEditorTilesPlugin::_update_source_display).unbind(1)); sources_list->connect(SceneStringName(item_selected), callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current)); sources_list->connect("item_activated", callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::display_tile_set_editor_panel).unbind(1)); @@ -3531,6 +3532,7 @@ TileMapLayerEditorTerrainsPlugin::TileMapLayerEditorTerrainsPlugin() { terrains_tree->set_custom_minimum_size(Size2(70, 0) * EDSCALE); terrains_tree->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); terrains_tree->set_hide_root(true); + terrains_tree->set_theme_type_variation("ItemListSecondary"); terrains_tree->connect(SceneStringName(item_selected), callable_mp(this, &TileMapLayerEditorTerrainsPlugin::_update_tiles_list)); tilemap_tab_terrains->add_child(terrains_tree); diff --git a/godot/editor/plugins/tiles/tile_set_editor.cpp b/godot/editor/plugins/tiles/tile_set_editor.cpp index 6f473d1b..dd64e383 100644 --- a/godot/editor/plugins/tiles/tile_set_editor.cpp +++ b/godot/editor/plugins/tiles/tile_set_editor.cpp @@ -861,6 +861,7 @@ TileSetEditor::TileSetEditor() { sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE); sources_list->set_h_size_flags(SIZE_EXPAND_FILL); sources_list->set_v_size_flags(SIZE_EXPAND_FILL); + sources_list->set_theme_type_variation("ItemListSecondary"); sources_list->connect(SceneStringName(item_selected), callable_mp(this, &TileSetEditor::_source_selected)); sources_list->connect(SceneStringName(item_selected), callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::set_sources_lists_current)); sources_list->connect(SceneStringName(visibility_changed), callable_mp(TilesEditorUtils::get_singleton(), &TilesEditorUtils::synchronize_sources_list).bind(sources_list, source_sort_button)); @@ -946,6 +947,7 @@ TileSetEditor::TileSetEditor() { patterns_item_list->set_max_text_lines(2); patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size)); patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + patterns_item_list->set_theme_type_variation("ItemListSecondary"); patterns_item_list->connect(SceneStringName(gui_input), callable_mp(this, &TileSetEditor::_patterns_item_list_gui_input)); main_vb->add_child(patterns_item_list); patterns_item_list->hide(); diff --git a/godot/editor/plugins/version_control_editor_plugin.cpp b/godot/editor/plugins/version_control_editor_plugin.cpp index 815664c6..3a3d25ba 100644 --- a/godot/editor/plugins/version_control_editor_plugin.cpp +++ b/godot/editor/plugins/version_control_editor_plugin.cpp @@ -1294,6 +1294,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { commit_list->set_columns(2); // Commit msg, author commit_list->set_column_custom_minimum_width(0, 40); commit_list->set_column_custom_minimum_width(1, 20); + commit_list->set_theme_type_variation("TreeSecondary"); commit_list->connect(SceneStringName(item_selected), callable_mp(this, &VersionControlEditorPlugin::_load_diff).bind(commit_list)); version_commit_dock->add_child(commit_list); diff --git a/godot/editor/plugins/visual_shader_editor_plugin.cpp b/godot/editor/plugins/visual_shader_editor_plugin.cpp index 31e158bb..ca0241b6 100644 --- a/godot/editor/plugins/visual_shader_editor_plugin.cpp +++ b/godot/editor/plugins/visual_shader_editor_plugin.cpp @@ -59,6 +59,7 @@ #include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" #include "scene/gui/split_container.h" +#include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" #include "scene/gui/view_panner.h" #include "scene/main/window.h" @@ -6676,6 +6677,7 @@ VisualShaderEditor::VisualShaderEditor() { parameters->set_h_size_flags(SIZE_EXPAND_FILL); parameters->set_v_size_flags(SIZE_EXPAND_FILL); parameters->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + parameters->set_theme_type_variation("TreeSecondary"); parameters->connect(SceneStringName(item_selected), callable_mp(this, &VisualShaderEditor::_param_selected)); parameters->connect("nothing_selected", callable_mp(this, &VisualShaderEditor::_param_unselected)); sc->add_child(parameters); diff --git a/godot/editor/plugins/voxel_gi_editor_plugin.cpp b/godot/editor/plugins/voxel_gi_editor_plugin.cpp index 68fe013c..527138e0 100644 --- a/godot/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/godot/editor/plugins/voxel_gi_editor_plugin.cpp @@ -146,15 +146,15 @@ void VoxelGIEditorPlugin::make_visible(bool p_visible) { EditorProgress *VoxelGIEditorPlugin::tmp_progress = nullptr; -void VoxelGIEditorPlugin::bake_func_begin(int p_steps) { +void VoxelGIEditorPlugin::bake_func_begin() { ERR_FAIL_COND(tmp_progress != nullptr); - tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake VoxelGI"), p_steps)); + tmp_progress = memnew(EditorProgress("bake_gi", TTR("Bake VoxelGI"), 1000, true)); } -void VoxelGIEditorPlugin::bake_func_step(int p_step, const String &p_description) { - ERR_FAIL_NULL(tmp_progress); - tmp_progress->step(p_description, p_step, false); +bool VoxelGIEditorPlugin::bake_func_step(int p_progress, const String &p_description) { + ERR_FAIL_NULL_V(tmp_progress, false); + return tmp_progress->step(p_description, p_progress, false); } void VoxelGIEditorPlugin::bake_func_end() { diff --git a/godot/editor/plugins/voxel_gi_editor_plugin.h b/godot/editor/plugins/voxel_gi_editor_plugin.h index d09822dd..01a2ab4b 100644 --- a/godot/editor/plugins/voxel_gi_editor_plugin.h +++ b/godot/editor/plugins/voxel_gi_editor_plugin.h @@ -50,8 +50,8 @@ class VoxelGIEditorPlugin : public EditorPlugin { EditorFileDialog *probe_file = nullptr; static EditorProgress *tmp_progress; - static void bake_func_begin(int p_steps); - static void bake_func_step(int p_step, const String &p_description); + static void bake_func_begin(); + static bool bake_func_step(int p_progress, const String &p_description); static void bake_func_end(); void _bake(); diff --git a/godot/editor/themes/editor_theme_manager.cpp b/godot/editor/themes/editor_theme_manager.cpp index a4251bfd..3bf3fefa 100644 --- a/godot/editor/themes/editor_theme_manager.cpp +++ b/godot/editor/themes/editor_theme_manager.cpp @@ -2132,6 +2132,10 @@ void EditorThemeManager::_populate_editor_styles(const Ref &p_theme // EditorValidationPanel. p_theme->set_stylebox(SceneStringName(panel), "EditorValidationPanel", p_config.tree_panel_style); + + // Secondary trees and item lists. + p_theme->set_type_variation("TreeSecondary", "Tree"); + p_theme->set_type_variation("ItemListSecondary", "ItemList"); } // Editor inspector. diff --git a/godot/main/main.cpp b/godot/main/main.cpp index f002e227..123114ce 100644 --- a/godot/main/main.cpp +++ b/godot/main/main.cpp @@ -733,6 +733,7 @@ Error Main::test_setup() { physics_server_2d_manager = memnew(PhysicsServer2DManager); // From `Main::setup2()`. + register_early_core_singletons(); initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE); register_core_extensions(); @@ -1869,12 +1870,55 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (editor) { Engine::get_singleton()->set_editor_hint(true); Engine::get_singleton()->set_extension_reloading_enabled(true); + + main_args.push_back("--editor"); + if (!init_windowed && !init_fullscreen) { + init_maximized = true; + window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED; + } + } + + if (!project_manager && !editor) { + // If we didn't find a project, we fall back to the project manager. + project_manager = !found_project && !cmdline_tool; + } + + if (project_manager) { + Engine::get_singleton()->set_project_manager_hint(true); } #endif + OS::get_singleton()->set_cmdline(execpath, main_args, user_args); + + Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second", PROPERTY_HINT_RANGE, "1,1000,1"), 60)); + Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); + Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); + Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); + if (max_fps >= 0) { + Engine::get_singleton()->set_max_fps(max_fps); + } + // Initialize user data dir. OS::get_singleton()->ensure_user_data_dir(); + OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); + OS::get_singleton()->set_low_processor_usage_mode_sleep_usec( + GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900)); // Roughly 144 FPS + + GLOBAL_DEF("application/run/delta_smoothing", true); + if (!delta_smoothing_override) { + OS::get_singleton()->set_delta_smoothing(GLOBAL_GET("application/run/delta_smoothing")); + } + + GLOBAL_DEF("debug/settings/stdout/print_fps", false); + GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); + GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); + GLOBAL_DEF("debug/settings/physics_interpolation/enable_warnings", true); + if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden. + OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout"); + } + + register_early_core_singletons(); initialize_modules(MODULE_INITIALIZATION_LEVEL_CORE); register_core_extensions(); // core extensions must be registered after globals setup and before display @@ -1899,20 +1943,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED if (editor) { packed_data->set_disabled(true); - main_args.push_back("--editor"); - if (!init_windowed && !init_fullscreen) { - init_maximized = true; - window_mode = DisplayServer::WINDOW_MODE_MAXIMIZED; - } - } - - if (!project_manager && !editor) { - // If we didn't find a project, we fall back to the project manager. - project_manager = !found_project && !cmdline_tool; - } - - if (project_manager) { - Engine::get_singleton()->set_project_manager_hint(true); } #endif @@ -1984,8 +2014,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Logger::set_flush_stdout_on_print(GLOBAL_GET("application/run/flush_stdout_on_print")); - OS::get_singleton()->set_cmdline(execpath, main_args, user_args); - { String driver_hints = ""; String driver_hints_with_d3d12 = ""; @@ -2544,9 +2572,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph window_vsync_mode = DisplayServer::VSyncMode::VSYNC_DISABLED; } } - Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/physics_ticks_per_second", PROPERTY_HINT_RANGE, "1,1000,1"), 60)); - Engine::get_singleton()->set_max_physics_steps_per_frame(GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "physics/common/max_physics_steps_per_frame", PROPERTY_HINT_RANGE, "1,100,1"), 8)); - Engine::get_singleton()->set_physics_jitter_fix(GLOBAL_DEF("physics/common/physics_jitter_fix", 0.5)); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "audio/driver/output_latency", PROPERTY_HINT_RANGE, "1,100,1"), 15); // Use a safer default output_latency for web to avoid audio cracking on low-end devices, especially mobile. @@ -2554,15 +2579,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_audio_output_latency(GLOBAL_GET("audio/driver/output_latency")); - GLOBAL_DEF("debug/settings/stdout/print_fps", false); - GLOBAL_DEF("debug/settings/stdout/print_gpu_profile", false); - GLOBAL_DEF("debug/settings/stdout/verbose_stdout", false); - GLOBAL_DEF("debug/settings/physics_interpolation/enable_warnings", true); - - if (!OS::get_singleton()->_verbose_stdout) { // Not manually overridden. - OS::get_singleton()->_verbose_stdout = GLOBAL_GET("debug/settings/stdout/verbose_stdout"); - } - #if defined(MACOS_ENABLED) || defined(IOS_ENABLED) OS::get_singleton()->set_environment("MVK_CONFIG_LOG_LEVEL", OS::get_singleton()->_verbose_stdout ? "3" : "1"); // 1 = Errors only, 3 = Info #endif @@ -2578,15 +2594,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph Engine::get_singleton()->set_audio_output_latency(audio_output_latency); } - OS::get_singleton()->set_low_processor_usage_mode(GLOBAL_DEF("application/run/low_processor_mode", false)); - OS::get_singleton()->set_low_processor_usage_mode_sleep_usec( - GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "0,33200,1,or_greater"), 6900)); // Roughly 144 FPS - - GLOBAL_DEF("application/run/delta_smoothing", true); - if (!delta_smoothing_override) { - OS::get_singleton()->set_delta_smoothing(GLOBAL_GET("application/run/delta_smoothing")); - } - GLOBAL_DEF("display/window/ios/allow_high_refresh_rate", true); GLOBAL_DEF("display/window/ios/hide_home_indicator", true); GLOBAL_DEF("display/window/ios/hide_status_bar", true); @@ -3013,10 +3020,9 @@ Error Main::setup2(bool p_show_boot_logo) { } // Max FPS needs to be set after the DisplayServer is created. - Engine::get_singleton()->set_max_fps(GLOBAL_DEF(PropertyInfo(Variant::INT, "application/run/max_fps", PROPERTY_HINT_RANGE, "0,1000,1"), 0)); - - if (max_fps >= 0) { - Engine::get_singleton()->set_max_fps(max_fps); + RenderingDevice *rd = RenderingDevice::get_singleton(); + if (rd) { + rd->_set_max_fps(engine->get_max_fps()); } #ifdef TOOLS_ENABLED diff --git a/godot/methods.py b/godot/methods.py index 203f0dd8..be290f81 100644 --- a/godot/methods.py +++ b/godot/methods.py @@ -102,6 +102,7 @@ def add_source_files_scu(self, sources, files, allow_gen=False): subdir = os.path.dirname(files) subdir = subdir if subdir == "" else subdir + "/" section_name = self.Dir(subdir).tpath + section_name = section_name.replace("\\", "/") # win32 # if the section name is in the hash table? # i.e. is it part of the SCU build? global _scu_folders diff --git a/godot/misc/dist/html/editor.html b/godot/misc/dist/html/editor.html index 3a220555..4f2a3bc0 100644 --- a/godot/misc/dist/html/editor.html +++ b/godot/misc/dist/html/editor.html @@ -363,24 +363,28 @@

Important - Please read btn.style.display = ''; } if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('service.worker.js').then(function (reg) { - if (reg.waiting) { - notifyUpdate(reg.waiting); - } - reg.addEventListener('updatefound', function () { - const update = reg.installing; - update.addEventListener('statechange', function () { - if (update.state === 'installed') { - // It's a new install, claim and perform aggressive caching. - if (!reg.active) { - update.postMessage('claim'); - } else { - notifyUpdate(update); + try { + navigator.serviceWorker.register('service.worker.js').then(function (reg) { + if (reg.waiting) { + notifyUpdate(reg.waiting); + } + reg.addEventListener('updatefound', function () { + const update = reg.installing; + update.addEventListener('statechange', function () { + if (update.state === 'installed') { + // It's a new install, claim and perform aggressive caching. + if (!reg.active) { + update.postMessage('claim'); + } else { + notifyUpdate(update); + } } - } + }); }); }); - }); + } catch (e) { + console.error('Error while registering service worker:', e); + } } const missing = Engine.getMissingFeatures({ diff --git a/godot/misc/dist/html/full-size.html b/godot/misc/dist/html/full-size.html index b59c417d..3d68b66f 100644 --- a/godot/misc/dist/html/full-size.html +++ b/godot/misc/dist/html/full-size.html @@ -152,9 +152,15 @@ if (missing.length !== 0) { if (GODOT_CONFIG['serviceWorker'] && GODOT_CONFIG['ensureCrossOriginIsolationHeaders'] && 'serviceWorker' in navigator) { + let serviceWorkerRegistrationPromise; + try { + serviceWorkerRegistrationPromise = navigator.serviceWorker.getRegistration(); + } catch (err) { + serviceWorkerRegistrationPromise = Promise.reject(new Error('Service worker registration failed.')); + } // There's a chance that installing the service worker would fix the issue Promise.race([ - navigator.serviceWorker.getRegistration().then((registration) => { + serviceWorkerRegistrationPromise.then((registration) => { if (registration != null) { return Promise.reject(new Error('Service worker already exists.')); } diff --git a/godot/misc/extension_api_validation/4.3-stable.expected b/godot/misc/extension_api_validation/4.3-stable.expected index 506844e6..0c988b89 100644 --- a/godot/misc/extension_api_validation/4.3-stable.expected +++ b/godot/misc/extension_api_validation/4.3-stable.expected @@ -159,3 +159,22 @@ Validate extension JSON: API was removed: builtin_classes/Vector4i/constants/AXI Validate extension JSON: API was removed: builtin_classes/Vector4i/constants/AXIS_Z These constants have been replaced with corresponding enum constants. + + +GH-98670 +-------- +Validate extension JSON: Error: Field 'classes/RenderSceneBuffersRD/methods/create_texture/arguments': size changed value in new API, from 9 to 10. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments': size changed value in new API, from 10 to 7. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments': size changed value in new API, from 9 to 7. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/1': type changed value in new API, from "enum::RenderingDevice.InitialAction" to "bitfield::RenderingDevice.DrawFlags". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/2': type changed value in new API, from "enum::RenderingDevice.FinalAction" to "PackedColorArray". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/3': type changed value in new API, from "enum::RenderingDevice.InitialAction" to "float". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/4': type changed value in new API, from "enum::RenderingDevice.FinalAction" to "int". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/5': default_value changed value in new API, from "PackedColorArray()" to "Rect2(0, 0, 0, 0)". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/5': type changed value in new API, from "PackedColorArray" to "Rect2". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': default_value changed value in new API, from "1.0" to "0". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': meta changed value in new API, from "float" to "uint32". +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments/6': type changed value in new API, from "float" to "int". + +Draw lists no longer require the initial and final action for color and depth attachments to be specified. +Draw lists can now specify if a particular color, depth, or stencil attachment should be cleared. diff --git a/godot/misc/utility/.clang-format-glsl b/godot/misc/utility/.clang-format-glsl index c588df02..59efa8fa 100644 --- a/godot/misc/utility/.clang-format-glsl +++ b/godot/misc/utility/.clang-format-glsl @@ -30,10 +30,7 @@ JavaImportGroups: - com.google - java - javax -KeepEmptyLines: - AtEndOfFile: false - AtStartOfBlock: false - AtStartOfFile: false +KeepEmptyLinesAtTheStartOfBlocks: false ObjCBlockIndentWidth: 4 PackConstructorInitializers: NextLine RemoveSemicolon: false # Differs from base .clang-format diff --git a/godot/modules/csg/SCsub b/godot/modules/csg/SCsub index b4612c75..a14e999f 100644 --- a/godot/modules/csg/SCsub +++ b/godot/modules/csg/SCsub @@ -14,21 +14,20 @@ thirdparty_obj = [] thirdparty_dir = "#thirdparty/manifold/" thirdparty_sources = [ - "src/cross_section/cross_section.cpp", + "src/boolean_result.cpp", + "src/boolean3.cpp", "src/constructors.cpp", - "src/polygon.cpp", + "src/csg_tree.cpp", "src/edge_op.cpp", "src/face_op.cpp", "src/impl.cpp", - "src/boolean_result.cpp", - "src/boolean3.cpp", "src/manifold.cpp", + "src/polygon.cpp", "src/properties.cpp", + "src/quickhull.cpp", "src/smoothing.cpp", "src/sort.cpp", - "src/csg_tree.cpp", "src/subdivision.cpp", - "src/quickhull.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] diff --git a/godot/modules/csg/csg_shape.cpp b/godot/modules/csg/csg_shape.cpp index 0c92ddf3..dd2a1c1f 100644 --- a/godot/modules/csg/csg_shape.cpp +++ b/godot/modules/csg/csg_shape.cpp @@ -31,8 +31,7 @@ #include "csg_shape.h" #include "core/math/geometry_2d.h" -#include "manifold/common.h" -#include "manifold/manifold.h" +#include void CSGShape3D::set_use_collision(bool p_enable) { if (use_collision == p_enable) { @@ -199,12 +198,9 @@ static void _unpack_manifold( material = p_mesh_materials[original_id]; } // Find or reserve a material ID in the brush. - int run_material = 0; int32_t material_id = r_mesh_merge->materials.find(material); - if (material_id != -1) { - run_material = material_id; - } else { - run_material = r_mesh_merge->materials.size(); + if (material_id == -1) { + material_id = r_mesh_merge->materials.size(); r_mesh_merge->materials.push_back(material); } @@ -212,21 +208,18 @@ static void _unpack_manifold( size_t end = mesh.runIndex[run_i + 1]; for (size_t vert_i = begin; vert_i < end; vert_i += 3) { CSGBrush::Face face; - face.material = run_material; + face.material = material_id; int32_t first_property_index = mesh.triVerts[vert_i + order[0]]; face.smooth = mesh.vertProperties[first_property_index * mesh.numProp + MANIFOLD_PROPERTY_SMOOTH_GROUP] > 0.5f; face.invert = mesh.vertProperties[first_property_index * mesh.numProp + MANIFOLD_PROPERTY_INVERT] > 0.5f; for (int32_t tri_order_i = 0; tri_order_i < 3; tri_order_i++) { int32_t property_i = mesh.triVerts[vert_i + order[tri_order_i]]; - ERR_FAIL_COND_MSG(property_i * mesh.numProp >= mesh.vertProperties.size(), "Invalid index into vertex properties"); - face.vertices[tri_order_i] = Vector3( mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_X], mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_Y], mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_POSITION_Z]); - face.uvs[tri_order_i] = Vector2( mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_UV_X_0], mesh.vertProperties[property_i * mesh.numProp + MANIFOLD_PROPERTY_UV_Y_0]); @@ -242,7 +235,6 @@ static void _pack_manifold( const CSGBrush *const p_mesh_merge, manifold::Manifold &r_manifold, HashMap> &p_mesh_materials, - Ref p_default_material, float p_snap) { ERR_FAIL_NULL_MSG(p_mesh_merge, "p_mesh_merge is null"); @@ -273,10 +265,6 @@ static void _pack_manifold( material = p_mesh_merge->materials[material_id]; } - if (material.is_null()) { - material = p_default_material; - } - p_mesh_materials.insert(reserved_id, material); for (const CSGBrush::Face &face : faces) { for (int32_t tri_order_i = 0; tri_order_i < 3; tri_order_i++) { @@ -303,24 +291,7 @@ static void _pack_manifold( // runIndex needs an explicit end value. mesh.runIndex.push_back(mesh.triVerts.size()); ERR_FAIL_COND_MSG(mesh.vertProperties.size() % mesh.numProp != 0, "Invalid vertex properties size."); - /** - * MeshGL64::merge(): updates the mergeFromVert and mergeToVert vectors in order to create a - * manifold solid. If the MeshGL64 is already manifold, no change will occur, and - * the function will return false. - */ - if (mesh.Merge()) { - std::vector index_map(mesh.vertProperties.size() / MANIFOLD_PROPERTY_MAX, -1); - const size_t vertices_count = mesh.mergeFromVert.size(); - for (size_t i = 0; i < vertices_count; ++i) { - index_map[mesh.mergeFromVert[i]] = mesh.mergeToVert[i]; - } - const size_t indices_count = mesh.triVerts.size(); - for (size_t i = 0; i < indices_count; ++i) { - if (index_map[i] > -1) { - mesh.triVerts[i] = index_map[i]; - } - } - } + mesh.Merge(); r_manifold = manifold::Manifold(mesh); manifold::Manifold::Error err = r_manifold.Status(); if (err != manifold::Manifold::Error::NoError) { @@ -358,7 +329,7 @@ CSGBrush *CSGShape3D::_get_brush() { CSGBrush *n = _build_brush(); HashMap> mesh_materials; manifold::Manifold root_manifold; - _pack_manifold(n, root_manifold, mesh_materials, default_material, get_snap()); + _pack_manifold(n, root_manifold, mesh_materials, get_snap()); manifold::OpType current_op = ManifoldOperation::convert_csg_op(get_operation()); std::vector manifolds; manifolds.push_back(root_manifold); @@ -374,7 +345,7 @@ CSGBrush *CSGShape3D::_get_brush() { CSGBrush transformed_brush; transformed_brush.copy_from(*child_brush, child->get_transform()); manifold::Manifold child_manifold; - _pack_manifold(&transformed_brush, child_manifold, mesh_materials, default_material, get_snap()); + _pack_manifold(&transformed_brush, child_manifold, mesh_materials, get_snap()); manifold::OpType child_operation = ManifoldOperation::convert_csg_op(child->get_operation()); if (child_operation != current_op) { manifold::Manifold result = manifold::Manifold::BatchBoolean(manifolds, current_op); @@ -908,7 +879,6 @@ void CSGShape3D::_bind_methods() { CSGShape3D::CSGShape3D() { set_notify_local_transform(true); - default_material.instantiate(); } CSGShape3D::~CSGShape3D() { diff --git a/godot/modules/csg/csg_shape.h b/godot/modules/csg/csg_shape.h index 28a331ac..8f23ae2f 100644 --- a/godot/modules/csg/csg_shape.h +++ b/godot/modules/csg/csg_shape.h @@ -51,7 +51,6 @@ class CSGShape3D : public GeometryInstance3D { }; private: - Ref default_material; Operation operation = OPERATION_UNION; CSGShape3D *parent_shape = nullptr; diff --git a/godot/modules/dds/texture_loader_dds.cpp b/godot/modules/dds/texture_loader_dds.cpp index 601d0e0c..667de70d 100644 --- a/godot/modules/dds/texture_loader_dds.cpp +++ b/godot/modules/dds/texture_loader_dds.cpp @@ -289,6 +289,15 @@ static Ref _dds_load_layer(Ref p_file, DDSFormat p_dds_format if (info.compressed) { // BC compressed. + w += w % info.divisor; + h += h % info.divisor; + if (w != p_width) { + WARN_PRINT(vformat("%s: DDS width '%d' is not divisible by %d. This is not allowed as per the DDS specification, attempting to load anyway.", p_file->get_path(), p_width, info.divisor)); + } + if (h != p_height) { + WARN_PRINT(vformat("%s: DDS height '%d' is not divisible by %d. This is not allowed as per the DDS specification, attempting to load anyway.", p_file->get_path(), p_height, info.divisor)); + } + uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; if (p_flags & DDSD_LINEARSIZE) { diff --git a/godot/modules/gdscript/language_server/gdscript_extend_parser.cpp b/godot/modules/gdscript/language_server/gdscript_extend_parser.cpp index 2a3db4f5..46ae4a88 100644 --- a/godot/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/godot/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -57,6 +57,12 @@ lsp::Position GodotPosition::to_lsp(const Vector &p_lines) const { return res; } res.line = line - 1; + + // Special case: `column = 0` -> Starts at beginning of line. + if (column <= 0) { + return res; + } + // Note: character outside of `pos_line.length()-1` is valid. res.character = column - 1; @@ -238,9 +244,12 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p r_symbol.kind = lsp::SymbolKind::Class; r_symbol.deprecated = false; r_symbol.range = range_of_node(p_class); - r_symbol.range.start.line = MAX(r_symbol.range.start.line, 0); if (p_class->identifier) { r_symbol.selectionRange = range_of_node(p_class->identifier); + } else { + // No meaningful `selectionRange`, but we must ensure that it is inside of `range`. + r_symbol.selectionRange.start = r_symbol.range.start; + r_symbol.selectionRange.end = r_symbol.range.start; } r_symbol.detail = "class " + r_symbol.name; { diff --git a/godot/modules/gdscript/tests/scripts/lsp/first_line_comment.gd b/godot/modules/gdscript/tests/scripts/lsp/first_line_comment.gd new file mode 100644 index 00000000..34ead5fa --- /dev/null +++ b/godot/modules/gdscript/tests/scripts/lsp/first_line_comment.gd @@ -0,0 +1,2 @@ +# Some comment +extends Node diff --git a/godot/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/godot/modules/gdscript/tests/scripts/runtime/features/stringify.gd index 8579baf8..69aecec6 100644 --- a/godot/modules/gdscript/tests/scripts/runtime/features/stringify.gd +++ b/godot/modules/gdscript/tests/scripts/runtime/features/stringify.gd @@ -4,21 +4,25 @@ func test(): print(-1.25, 0.25, 1.25) print("hello world") - print(Vector2(0.25, 0.25)) + print(Vector2(0.25, 1)) print(Vector2i(0, 0)) - print(Rect2(0.25, 0.25, 0.5, 0.5)) + print(Rect2(0.25, 0.25, 0.5, 1)) print(Rect2i(0, 0, 0, 0)) - print(Vector3(0.25, 0.25, 0.25)) + print(Vector3(0.25, 0.25, 1)) print(Vector3i(0, 0, 0)) + print(Vector4(0.25, 0.25, 0.25, 1)) + print(Vector4i(0, 0, 0, 0)) + print(Transform2D.IDENTITY) print(Plane(1, 2, 3, 4)) print(Quaternion(1, 2, 3, 4)) print(AABB(Vector3.ZERO, Vector3.ONE)) print(Basis.from_euler(Vector3(0, 0, 0))) print(Transform3D.IDENTITY) + print(Projection.IDENTITY) print(Color(1, 2, 3, 4)) print(StringName("hello")) diff --git a/godot/modules/gdscript/tests/scripts/runtime/features/stringify.out b/godot/modules/gdscript/tests/scripts/runtime/features/stringify.out index 2463d70e..b044f810 100644 --- a/godot/modules/gdscript/tests/scripts/runtime/features/stringify.out +++ b/godot/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -3,18 +3,21 @@ truefalse -101 -1.250.251.25 hello world -(0.25, 0.25) +(0.25, 1.0) (0, 0) -[P: (0.25, 0.25), S: (0.5, 0.5)] +[P: (0.25, 0.25), S: (0.5, 1.0)] [P: (0, 0), S: (0, 0)] -(0.25, 0.25, 0.25) +(0.25, 0.25, 1.0) (0, 0, 0) +(0.25, 0.25, 0.25, 1.0) +(0, 0, 0, 0) [X: (1.0, 0.0), Y: (0.0, 1.0), O: (0.0, 0.0)] [N: (1.0, 2.0, 3.0), D: 4] (1, 2, 3, 4) [P: (0.0, 0.0, 0.0), S: (1.0, 1.0, 1.0)] [X: (1.0, 0.0, 0.0), Y: (0.0, 1.0, 0.0), Z: (0.0, 0.0, 1.0)] [X: (1.0, 0.0, 0.0), Y: (0.0, 1.0, 0.0), Z: (0.0, 0.0, 1.0), O: (0.0, 0.0, 0.0)] +[X: (1.0, 0.0, 0.0, 0.0), Y: (0.0, 1.0, 0.0, 0.0), Z: (0.0, 0.0, 1.0, 0.0), W: (0.0, 0.0, 0.0, 1.0)] (1.0, 2.0, 3.0, 4.0) hello hello/world @@ -32,4 +35,4 @@ Node::[signal]property_list_changed [(1.0, 1.0), (0.0, 0.0)] [(1.0, 1.0, 1.0), (0.0, 0.0, 0.0)] [(1.0, 0.0, 0.0, 1.0), (0.0, 0.0, 1.0, 1.0), (0.0, 1.0, 0.0, 1.0)] -[(1, 1, 1, 1), (0, 0, 0, 0)] +[(1.0, 1.0, 1.0, 1.0), (0.0, 0.0, 0.0, 0.0)] diff --git a/godot/modules/gdscript/tests/test_lsp.h b/godot/modules/gdscript/tests/test_lsp.h index b85c727b..b49f6cce 100644 --- a/godot/modules/gdscript/tests/test_lsp.h +++ b/godot/modules/gdscript/tests/test_lsp.h @@ -375,6 +375,18 @@ func f(): gd.to_lsp(lines); } + SUBCASE("special case: zero column for root class") { + GodotPosition gd(1, 0); + lsp::Position expected = lsp_pos(0, 0); + lsp::Position actual = gd.to_lsp(lines); + CHECK_EQ(actual, expected); + } + SUBCASE("special case: zero line and column for root class") { + GodotPosition gd(0, 0); + lsp::Position expected = lsp_pos(0, 0); + lsp::Position actual = gd.to_lsp(lines); + CHECK_EQ(actual, expected); + } SUBCASE("special case: negative line for root class") { GodotPosition gd(-1, 0); lsp::Position expected = lsp_pos(0, 0); @@ -468,6 +480,25 @@ func f(): test_resolve_symbols(uri, all_test_data, all_test_data); } + memdelete(proto); + finish_language(); + } + TEST_CASE("[workspace][document_symbol]") { + GDScriptLanguageProtocol *proto = initialize(root); + REQUIRE(proto); + + SUBCASE("selectionRange of root class must be inside range") { + String path = "res://lsp/first_line_comment.gd"; + assert_no_errors_in(path); + GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_local_script(path); + ExtendGDScriptParser *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_results[path]; + REQUIRE(parser); + lsp::DocumentSymbol cls = parser->get_symbols(); + + REQUIRE(((cls.range.start.line == cls.selectionRange.start.line && cls.range.start.character <= cls.selectionRange.start.character) || (cls.range.start.line < cls.selectionRange.start.line))); + REQUIRE(((cls.range.end.line == cls.selectionRange.end.line && cls.range.end.character >= cls.selectionRange.end.character) || (cls.range.end.line > cls.selectionRange.end.line))); + } + memdelete(proto); finish_language(); } diff --git a/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp b/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp index 0d522a05..e76e9e49 100644 --- a/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/godot/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1233,6 +1233,8 @@ void GridMapEditor::_update_cursor_instance() { Ref mesh = node->get_mesh_library()->get_item_mesh(selected_palette); if (!mesh.is_null() && mesh->get_rid().is_valid()) { cursor_instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world_3d()->get_scenario()); + RS::ShadowCastingSetting cast_shadows = (RS::ShadowCastingSetting)node->get_mesh_library()->get_item_mesh_cast_shadow(selected_palette); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(cursor_instance, cast_shadows); } } } else if (mode_buttons_group->get_pressed_button() == select_mode_button) { diff --git a/godot/modules/gridmap/grid_map.cpp b/godot/modules/gridmap/grid_map.cpp index 0588ba03..29634a0a 100644 --- a/godot/modules/gridmap/grid_map.cpp +++ b/godot/modules/gridmap/grid_map.cpp @@ -714,6 +714,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) { RS::get_singleton()->instance_set_transform(instance, get_global_transform()); } + RS::ShadowCastingSetting cast_shadows = (RS::ShadowCastingSetting)mesh_library->get_item_mesh_cast_shadow(E.key); + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, cast_shadows); + mmi.multimesh = mm; mmi.instance = instance; diff --git a/godot/modules/lightmapper_rd/lightmapper_rd.cpp b/godot/modules/lightmapper_rd/lightmapper_rd.cpp index bd71e29d..9cfeff4f 100644 --- a/godot/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/godot/modules/lightmapper_rd/lightmapper_rd.cpp @@ -722,7 +722,7 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int raster_push_constant.uv_offset[0] = -0.5f / float(atlas_size.x); raster_push_constant.uv_offset[1] = -0.5f / float(atlas_size.y); - RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 1.0, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS); + RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::DRAW_CLEAR_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS); //draw opaque rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline); rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0); @@ -1419,6 +1419,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d tf.texture_type = RD::TEXTURE_TYPE_2D; tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; tf.format = RD::DATA_FORMAT_D32_SFLOAT; + tf.is_discardable = true; raster_depth_buffer = rd->texture_create(tf, RD::TextureView()); } @@ -2049,8 +2050,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d uint32_t seam_offset = 0; uint32_t triangle_offset = 0; - Vector clear_colors; - clear_colors.push_back(Color(0, 0, 0, 1)); for (int i = 0; i < atlas_slices; i++) { int subslices = (p_bake_sh ? 4 : 1); @@ -2064,7 +2063,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d seams_push_constant.debug = debug; // Store the current subslice in the breadcrumb. - RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 1.0, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice); + RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i * subslices + k], RD::DRAW_CLEAR_DEPTH, Vector(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::LIGHTMAPPER_PASS | seams_push_constant.slice); rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0); rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1); diff --git a/godot/modules/many_bone_ik/.gitrepo b/godot/modules/many_bone_ik/.gitrepo index 03bafee4..6b763294 100644 --- a/godot/modules/many_bone_ik/.gitrepo +++ b/godot/modules/many_bone_ik/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/V-Sekai/many_bone_ik.git branch = main - commit = ad2e65ba6c63924d61171ef2b25c46cb5a9f9edb - parent = ccfdadee38e32204dd2f5ac00fba4995ff461c7e + commit = b6646697056c93d3296aca79fbf0093a307f0cb2 + parent = 251a9ddb2b40ab42487b86928ed97940af6b602c method = merge - cmdver = 0.4.6 + cmdver = 0.4.9 diff --git a/godot/modules/many_bone_ik/src/ik_bone_segment_3d.cpp b/godot/modules/many_bone_ik/src/ik_bone_segment_3d.cpp index 52f43c5f..b0aba235 100644 --- a/godot/modules/many_bone_ik/src/ik_bone_segment_3d.cpp +++ b/godot/modules/many_bone_ik/src/ik_bone_segment_3d.cpp @@ -140,11 +140,11 @@ void IKBoneSegment3D::_set_optimal_rotation(Ref p_for_bone, PackedVect do { _update_tip_headings(p_for_bone, &tip_headings); if (!p_constraint_mode) { - QCP qcp = QCP(evec_prec); - Basis rotation = qcp.weighted_superpose(*r_htip, *r_htarget, *r_weights, p_translate); - Vector3 translation = qcp.get_translation(); + Array superpose_result = QuaternionCharacteristicPolynomial::weighted_superpose(*r_htip, *r_htarget, *r_weights, p_translate, evec_prec); + Quaternion rotation = superpose_result[0]; + Vector3 translation = superpose_result[1]; double dampening = (p_dampening != -1.0) ? p_dampening : bone_damp; - rotation = clamp_to_cos_half_angle(rotation.get_rotation_quaternion(), cos(dampening / 2.0)); + rotation = clamp_to_cos_half_angle(rotation, cos(dampening / 2.0)); if (current_iteration == 0) { current_iteration = 0.0001; } diff --git a/godot/modules/many_bone_ik/src/many_bone_ik_3d.h b/godot/modules/many_bone_ik/src/many_bone_ik_3d.h index 6be7e12d..0129e33a 100644 --- a/godot/modules/many_bone_ik/src/many_bone_ik_3d.h +++ b/godot/modules/many_bone_ik/src/many_bone_ik_3d.h @@ -40,7 +40,6 @@ #include "math/ik_node_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_modifier_3d.h" -#include "scene/main/scene_tree.h" class ManyBoneIK3DState; class ManyBoneIK3D : public SkeletonModifier3D { diff --git a/godot/modules/many_bone_ik/src/math/qcp.cpp b/godot/modules/many_bone_ik/src/math/qcp.cpp index 221a6f66..1e16b715 100644 --- a/godot/modules/many_bone_ik/src/math/qcp.cpp +++ b/godot/modules/many_bone_ik/src/math/qcp.cpp @@ -30,18 +30,18 @@ #include "qcp.h" -QCP::QCP(double p_evec_prec) { +QuaternionCharacteristicPolynomial::QuaternionCharacteristicPolynomial(double p_evec_prec) { eigenvector_precision = p_evec_prec; } -void QCP::set(PackedVector3Array &r_target, PackedVector3Array &r_moved) { +void QuaternionCharacteristicPolynomial::set(PackedVector3Array &r_target, PackedVector3Array &r_moved) { target = r_target; moved = r_moved; transformation_calculated = false; inner_product_calculated = false; } -Quaternion QCP::get_rotation() { +Quaternion QuaternionCharacteristicPolynomial::_get_rotation() { Quaternion result; if (!transformation_calculated) { if (!inner_product_calculated) { @@ -53,7 +53,7 @@ Quaternion QCP::get_rotation() { return result; } -Quaternion QCP::calculate_rotation() { +Quaternion QuaternionCharacteristicPolynomial::calculate_rotation() { Quaternion result; if (moved.size() == 1) { @@ -126,17 +126,17 @@ Quaternion QCP::calculate_rotation() { return result; } -void QCP::translate(Vector3 r_translate, PackedVector3Array &r_x) { +void QuaternionCharacteristicPolynomial::translate(Vector3 r_translate, PackedVector3Array &r_x) { for (Vector3 &p : r_x) { p += r_translate; } } -Vector3 QCP::get_translation() { +Vector3 QuaternionCharacteristicPolynomial::_get_translation() { return target_center - moved_center; } -Vector3 QCP::move_to_weighted_center(PackedVector3Array &r_to_center, Vector &r_weight) { +Vector3 QuaternionCharacteristicPolynomial::move_to_weighted_center(PackedVector3Array &r_to_center, Vector &r_weight) { Vector3 center; double total_weight = 0; bool weight_is_empty = r_weight.is_empty(); @@ -159,7 +159,7 @@ Vector3 QCP::move_to_weighted_center(PackedVector3Array &r_to_center, Vector &p_weight, bool translate) { +Quaternion QuaternionCharacteristicPolynomial::_weighted_superpose(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool translate) { set(p_moved, p_target, p_weight, translate); - return get_rotation(); + return _get_rotation(); } -void QCP::set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool p_translate) { +void QuaternionCharacteristicPolynomial::set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool p_translate) { transformation_calculated = false; inner_product_calculated = false; @@ -246,3 +246,24 @@ void QCP::set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector< } } } + +void QuaternionCharacteristicPolynomial::_bind_methods() { + ClassDB::bind_static_method("QuaternionCharacteristicPolynomial", + D_METHOD("weighted_superpose", "moved", "target", + "weight", "translate", "precision"), + &QuaternionCharacteristicPolynomial::weighted_superpose); +} + +Array QuaternionCharacteristicPolynomial::weighted_superpose(PackedVector3Array p_moved, + PackedVector3Array p_target, + Vector p_weight, bool p_translate, + double p_precision) { + QuaternionCharacteristicPolynomial qcp(p_precision); + Quaternion rotation = + qcp._weighted_superpose(p_moved, p_target, p_weight, p_translate); + Vector3 translation = qcp._get_translation(); + Array result; + result.push_back(rotation); + result.push_back(translation); + return result; +} diff --git a/godot/modules/many_bone_ik/src/math/qcp.h b/godot/modules/many_bone_ik/src/math/qcp.h index 4b42c554..274b74fb 100644 --- a/godot/modules/many_bone_ik/src/math/qcp.h +++ b/godot/modules/many_bone_ik/src/math/qcp.h @@ -31,8 +31,9 @@ #ifndef QCP_H #define QCP_H -#include "core/math/basis.h" #include "core/math/vector3.h" +#include "core/object/class_db.h" +#include "core/object/object.h" #include "core/variant/variant.h" /** @@ -67,7 +68,8 @@ * @author K. S. Ernest (iFire) Lee (adapted to ManyBoneIK) */ -class QCP { +class QuaternionCharacteristicPolynomial : Object { + GDCLASS(QuaternionCharacteristicPolynomial, Object); double eigenvector_precision = 1E-6; PackedVector3Array target, moved; @@ -88,12 +90,19 @@ class QCP { void set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool p_translate); static void translate(Vector3 r_translate, PackedVector3Array &r_x); Vector3 move_to_weighted_center(PackedVector3Array &r_to_center, Vector &r_weight); + QuaternionCharacteristicPolynomial(double p_evec_prec); + Quaternion _weighted_superpose(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool translate); + Quaternion _get_rotation(); + Vector3 _get_translation(); + +protected: + static void _bind_methods(); public: - QCP(double p_evec_prec); - Quaternion weighted_superpose(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool translate); - Quaternion get_rotation(); - Vector3 get_translation(); + static Array weighted_superpose(PackedVector3Array p_moved, + PackedVector3Array p_target, + Vector p_weight, bool p_translate, + double p_precision = 1E-6); }; #endif // QCP_H diff --git a/godot/modules/many_bone_ik/tests/test_qcp.h b/godot/modules/many_bone_ik/tests/test_qcp.h index 299b2f3d..857840fb 100644 --- a/godot/modules/many_bone_ik/tests/test_qcp.h +++ b/godot/modules/many_bone_ik/tests/test_qcp.h @@ -37,10 +37,7 @@ namespace TestQCP { -TEST_CASE("[Modules][QCP] Weighted Superpose") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); - +TEST_CASE("[Modules][QCP] No Translation") { Quaternion expected = Quaternion(0, 0, sqrt(2) / 2, sqrt(2) / 2); PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; PackedVector3Array target = moved; @@ -48,69 +45,99 @@ TEST_CASE("[Modules][QCP] Weighted Superpose") { element = expected.xform(element); } Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights - - Quaternion result = qcp.weighted_superpose(moved, target, weight, false); - CHECK(abs(result.x - expected.x) < epsilon); - CHECK(abs(result.y - expected.y) < epsilon); - CHECK(abs(result.z - expected.z) < epsilon); - CHECK(abs(result.w - expected.w) < epsilon); + bool translate = false; + double epsilon = 1e-6; + Array result = QuaternionCharacteristicPolynomial::weighted_superpose(moved, target, weight, translate, epsilon); + Quaternion rotation = result[0]; + CHECK(abs(rotation.x - expected.x) < epsilon); + CHECK(abs(rotation.y - expected.y) < epsilon); + CHECK(abs(rotation.z - expected.z) < epsilon); + CHECK(abs(rotation.w - expected.w) < epsilon); + Vector3 result_translation = result[1]; + CHECK(result_translation.is_zero_approx()); } -TEST_CASE("[Modules][QCP] Weighted Translation") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); - - Quaternion expected; +TEST_CASE("[Modules][QCP] Different Weights") { + Quaternion expected = Quaternion(0, 0, sqrt(2) / 2, sqrt(2) / 2); PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; PackedVector3Array target = moved; - Vector3 translation_vector = Vector3(1, 2, 3); for (Vector3 &element : target) { - element = expected.xform(element + translation_vector); + element = expected.xform(element); } - Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights - bool translate = true; + Vector weight = { 0.5, 1.0, 1.5 }; // Different weights + bool translate = false; + double epsilon = 1e-6; - Quaternion result = qcp.weighted_superpose(moved, target, weight, translate); - CHECK(abs(result.x - expected.x) < epsilon); - CHECK(abs(result.y - expected.y) < epsilon); - CHECK(abs(result.z - expected.z) < epsilon); - CHECK(abs(result.w - expected.w) < epsilon); + Array result = QuaternionCharacteristicPolynomial::weighted_superpose(moved, target, weight, translate, epsilon); + Quaternion rotation = result[0]; + CHECK(abs(rotation.x - expected.x) < epsilon); + CHECK(abs(rotation.y - expected.y) < epsilon); + CHECK(abs(rotation.z - expected.z) < epsilon); + CHECK(abs(rotation.w - expected.w) < epsilon); +} - // Check if translation occurred - CHECK(translate); - Vector3 translation_result = expected.xform_inv(qcp.get_translation()); - CHECK(abs(translation_result.x - translation_vector.x) < epsilon); - CHECK(abs(translation_result.y - translation_vector.y) < epsilon); - CHECK(abs(translation_result.z - translation_vector.z) < epsilon); +TEST_CASE("[Modules][QCP] Zero Weights") { + Quaternion expected = Quaternion(0, 0, sqrt(2) / 2, sqrt(2) / 2); + PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; + PackedVector3Array target = moved; + for (Vector3 &element : target) { + element = expected.xform(element); + } + Vector weight = { 0.0, 0.0, 0.0 }; // Zero weights + bool translate = false; + double epsilon = 1e-6; + + Array result = QuaternionCharacteristicPolynomial::weighted_superpose(moved, target, weight, translate, epsilon); + Quaternion rotation = result[0]; + CHECK(abs(rotation.x - expected.x) < epsilon); + CHECK(abs(rotation.y - expected.y) < epsilon); + CHECK(abs(rotation.z - expected.z) < epsilon); + CHECK(abs(rotation.w - expected.w) < epsilon); } -TEST_CASE("[Modules][QCP] Weighted Translation Shortest Path") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); +TEST_CASE("[Modules][QCP] Identity Rotation") { + Quaternion expected = Quaternion(); + PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; + PackedVector3Array target = moved; + Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights + bool translate = false; + double epsilon = 1e-6; - Quaternion expected = Quaternion(1, 2, 3, 4).normalized(); + Array result = QuaternionCharacteristicPolynomial::weighted_superpose(moved, target, weight, translate, epsilon); + Quaternion rotation = result[0]; + CHECK(abs(rotation.x - expected.x) < epsilon); + CHECK(abs(rotation.y - expected.y) < epsilon); + CHECK(abs(rotation.z - expected.z) < epsilon); + CHECK(abs(rotation.w - expected.w) < epsilon); +} + +TEST_CASE("[Modules][QCP] Random Rotation and Translation") { + Quaternion expected_rotation = Quaternion(0.1, 0.2, 0.3, 0.4).normalized(); + Vector3 expected_translation = Vector3(1, 2, 3); PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; PackedVector3Array target = moved; - Vector3 translation_vector = Vector3(1, 2, 3); for (Vector3 &element : target) { - element = expected.xform(element + translation_vector); + element = expected_rotation.xform(element + expected_translation); } Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights bool translate = true; + double epsilon = 1e-6; - Quaternion result = qcp.weighted_superpose(moved, target, weight, translate); - CHECK(abs(result.x - expected.x) > epsilon); - CHECK(abs(result.y - expected.y) > epsilon); - CHECK(abs(result.z - expected.z) > epsilon); - CHECK(abs(result.w - expected.w) > epsilon); + Array result = QuaternionCharacteristicPolynomial::weighted_superpose(moved, target, weight, translate, epsilon); + Quaternion rotation = result[0]; + Vector3 translation = result[1]; + CHECK(abs(rotation.x - expected_rotation.x) < epsilon); + CHECK(abs(rotation.y - expected_rotation.y) < epsilon); + CHECK(abs(rotation.z - expected_rotation.z) < epsilon); + CHECK(abs(rotation.w - expected_rotation.w) < epsilon); - // Check if translation occurred CHECK(translate); - Vector3 translation_result = expected.xform_inv(qcp.get_translation()); - CHECK(abs(translation_result.x - translation_vector.x) > epsilon); - CHECK(abs(translation_result.y - translation_vector.y) > epsilon); - CHECK(abs(translation_result.z - translation_vector.z) > epsilon); + Vector3 translation_result = expected_rotation.xform_inv(translation); + CHECK(abs(translation_result.x - expected_translation.x) < epsilon); + CHECK(abs(translation_result.y - expected_translation.y) < epsilon); + CHECK(abs(translation_result.z - expected_translation.z) < epsilon); } + } // namespace TestQCP #endif // TEST_QCP_H diff --git a/godot/modules/navigation/nav_map.cpp b/godot/modules/navigation/nav_map.cpp index 8055dd4b..d7b5ab87 100644 --- a/godot/modules/navigation/nav_map.cpp +++ b/godot/modules/navigation/nav_map.cpp @@ -356,16 +356,10 @@ Vector3 NavMap::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) void NavMap::sync() { RWLockWrite write_lock(map_rwlock); - // Performance Monitor - int _new_pm_region_count = regions.size(); - int _new_pm_agent_count = agents.size(); - int _new_pm_link_count = links.size(); - int _new_pm_polygon_count = pm_polygon_count; - int _new_pm_edge_count = pm_edge_count; - int _new_pm_edge_merge_count = pm_edge_merge_count; - int _new_pm_edge_connection_count = pm_edge_connection_count; - int _new_pm_edge_free_count = pm_edge_free_count; - int _new_pm_obstacle_count = obstacles.size(); + performance_data.pm_region_count = regions.size(); + performance_data.pm_agent_count = agents.size(); + performance_data.pm_link_count = links.size(); + performance_data.pm_obstacle_count = obstacles.size(); // Check if we need to update the links. if (regenerate_polygons) { @@ -388,11 +382,11 @@ void NavMap::sync() { } if (regenerate_links) { - _new_pm_polygon_count = 0; - _new_pm_edge_count = 0; - _new_pm_edge_merge_count = 0; - _new_pm_edge_connection_count = 0; - _new_pm_edge_free_count = 0; + performance_data.pm_polygon_count = 0; + performance_data.pm_edge_count = 0; + performance_data.pm_edge_merge_count = 0; + performance_data.pm_edge_connection_count = 0; + performance_data.pm_edge_free_count = 0; // Remove regions connections. region_external_connections.clear(); @@ -424,7 +418,7 @@ void NavMap::sync() { } } - _new_pm_polygon_count = polygon_count; + performance_data.pm_polygon_count = polygon_count; // Group all edges per key. connection_pairs_map.clear(); @@ -439,7 +433,7 @@ void NavMap::sync() { HashMap::Iterator pair_it = connection_pairs_map.find(ek); if (!pair_it) { pair_it = connection_pairs_map.insert(ek, ConnectionPair()); - _new_pm_edge_count += 1; + performance_data.pm_edge_count += 1; ++free_edges_count; } ConnectionPair &pair = pair_it->value; @@ -476,7 +470,7 @@ void NavMap::sync() { c1.polygon->edges[c1.edge].connections.push_back(c2); c2.polygon->edges[c2.edge].connections.push_back(c1); // Note: The pathway_start/end are full for those connection and do not need to be modified. - _new_pm_edge_merge_count += 1; + performance_data.pm_edge_merge_count += 1; } else { CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size)); if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) { @@ -492,7 +486,7 @@ void NavMap::sync() { // to be connected, create new polygons to remove that small gap is // not really useful and would result in wasteful computation during // connection, integration and path finding. - _new_pm_edge_free_count = free_edges.size(); + performance_data.pm_edge_free_count = free_edges.size(); const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin; @@ -549,7 +543,7 @@ void NavMap::sync() { // Add the connection to the region_connection map. region_external_connections[(NavRegion *)free_edge.polygon->owner].push_back(new_connection); - _new_pm_edge_connection_count += 1; + performance_data.pm_edge_connection_count += 1; } } @@ -687,17 +681,6 @@ void NavMap::sync() { regenerate_links = false; obstacles_dirty = false; agents_dirty = false; - - // Performance Monitor. - pm_region_count = _new_pm_region_count; - pm_agent_count = _new_pm_agent_count; - pm_link_count = _new_pm_link_count; - pm_polygon_count = _new_pm_polygon_count; - pm_edge_count = _new_pm_edge_count; - pm_edge_merge_count = _new_pm_edge_merge_count; - pm_edge_connection_count = _new_pm_edge_connection_count; - pm_edge_free_count = _new_pm_edge_free_count; - pm_obstacle_count = _new_pm_obstacle_count; } void NavMap::_update_rvo_obstacles_tree_2d() { diff --git a/godot/modules/navigation/nav_map.h b/godot/modules/navigation/nav_map.h index 3442b784..7eaf2133 100644 --- a/godot/modules/navigation/nav_map.h +++ b/godot/modules/navigation/nav_map.h @@ -116,15 +116,7 @@ class NavMap : public NavRid { bool avoidance_use_high_priority_threads = true; // Performance Monitor - int pm_region_count = 0; - int pm_agent_count = 0; - int pm_link_count = 0; - int pm_polygon_count = 0; - int pm_edge_count = 0; - int pm_edge_merge_count = 0; - int pm_edge_connection_count = 0; - int pm_edge_free_count = 0; - int pm_obstacle_count = 0; + gd::PerformanceData performance_data; HashMap> region_external_connections; @@ -220,15 +212,15 @@ class NavMap : public NavRid { void dispatch_callbacks(); // Performance Monitor - int get_pm_region_count() const { return pm_region_count; } - int get_pm_agent_count() const { return pm_agent_count; } - int get_pm_link_count() const { return pm_link_count; } - int get_pm_polygon_count() const { return pm_polygon_count; } - int get_pm_edge_count() const { return pm_edge_count; } - int get_pm_edge_merge_count() const { return pm_edge_merge_count; } - int get_pm_edge_connection_count() const { return pm_edge_connection_count; } - int get_pm_edge_free_count() const { return pm_edge_free_count; } - int get_pm_obstacle_count() const { return pm_obstacle_count; } + int get_pm_region_count() const { return performance_data.pm_region_count; } + int get_pm_agent_count() const { return performance_data.pm_agent_count; } + int get_pm_link_count() const { return performance_data.pm_link_count; } + int get_pm_polygon_count() const { return performance_data.pm_polygon_count; } + int get_pm_edge_count() const { return performance_data.pm_edge_count; } + int get_pm_edge_merge_count() const { return performance_data.pm_edge_merge_count; } + int get_pm_edge_connection_count() const { return performance_data.pm_edge_connection_count; } + int get_pm_edge_free_count() const { return performance_data.pm_edge_free_count; } + int get_pm_obstacle_count() const { return performance_data.pm_obstacle_count; } int get_region_connections_count(NavRegion *p_region) const; Vector3 get_region_connection_pathway_start(NavRegion *p_region, int p_connection_id) const; diff --git a/godot/modules/navigation/nav_utils.h b/godot/modules/navigation/nav_utils.h index c466c82f..993a9763 100644 --- a/godot/modules/navigation/nav_utils.h +++ b/godot/modules/navigation/nav_utils.h @@ -298,6 +298,19 @@ class Heap { } } }; + +struct PerformanceData { + int pm_region_count = 0; + int pm_agent_count = 0; + int pm_link_count = 0; + int pm_polygon_count = 0; + int pm_edge_count = 0; + int pm_edge_merge_count = 0; + int pm_edge_connection_count = 0; + int pm_edge_free_count = 0; + int pm_obstacle_count = 0; +}; + } // namespace gd #endif // NAV_UTILS_H diff --git a/godot/modules/renik/.gitrepo b/godot/modules/renik/.gitrepo index cb40615e..c753690e 100644 --- a/godot/modules/renik/.gitrepo +++ b/godot/modules/renik/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/V-Sekai/renik.git branch = master - commit = cb086004ff086a02bd6ea43fe67ec04405f05c6a - parent = 9fe52a27ee6f4a2460fad6a86439406c6cd03a9a + commit = 6e711ac83c75e014c9bb0c0db30f64d66ffeff81 + parent = 6c29d42e130d848b8c6505471340a3504bf0fb25 method = merge - cmdver = 0.4.6 + cmdver = 0.4.9 diff --git a/godot/modules/renik/math/qcp.cpp b/godot/modules/renik/math/qcp.cpp deleted file mode 100644 index f86ace70..00000000 --- a/godot/modules/renik/math/qcp.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/**************************************************************************/ -/* qcp.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. */ -/**************************************************************************/ - -#include "qcp.h" - -QCP::QCP(double p_evec_prec) { - eigenvector_precision = p_evec_prec; -} - -void QCP::set(PackedVector3Array &r_target, PackedVector3Array &r_moved) { - target = r_target; - moved = r_moved; - transformation_calculated = false; - inner_product_calculated = false; -} - -Quaternion QCP::get_rotation() { - Quaternion result; - if (!transformation_calculated) { - if (!inner_product_calculated) { - inner_product(target, moved); - } - result = calculate_rotation(); - transformation_calculated = true; - } - return result; -} - -Quaternion QCP::calculate_rotation() { - Quaternion result; - - if (moved.size() == 1) { - Vector3 u = moved[0]; - Vector3 v = target[0]; - double norm_product = u.length() * v.length(); - - if (norm_product == 0.0) { - return Quaternion(); - } - - double dot = u.dot(v); - - if (dot < ((2.0e-15 - 1.0) * norm_product)) { - Vector3 w = u.normalized(); - result = Quaternion(-w.x, -w.y, -w.z, 0.0f).normalized(); - } else { - double q0 = Math::sqrt(0.5 * (1.0 + dot / norm_product)); - double coeff = 1.0 / (2.0 * q0 * norm_product); - Vector3 q = v.cross(u).normalized(); - result = Quaternion(-coeff * q.x, -coeff * q.y, -coeff * q.z, q0).normalized(); - } - } else { - double a13 = -sum_xz_minus_zx; - double a14 = sum_xy_minus_yx; - double a21 = sum_yz_minus_zy; - double a22 = sum_xx_minus_yy - sum_zz - max_eigenvalue; - double a23 = sum_xy_plus_yx; - double a24 = sum_xz_plus_zx; - double a31 = a13; - double a32 = a23; - double a33 = sum_yy - sum_xx - sum_zz - max_eigenvalue; - double a34 = sum_yz_plus_zy; - double a41 = a14; - double a42 = a24; - double a43 = a34; - double a44 = sum_zz - sum_xx_plus_yy - max_eigenvalue; - - double a3344_4334 = a33 * a44 - a43 * a34; - double a3244_4234 = a32 * a44 - a42 * a34; - double a3243_4233 = a32 * a43 - a42 * a33; - double a3143_4133 = a31 * a43 - a41 * a33; - double a3144_4134 = a31 * a44 - a41 * a34; - double a3142_4132 = a31 * a42 - a41 * a32; - - double quaternion_w = a22 * a3344_4334 - a23 * a3244_4234 + a24 * a3243_4233; - double quaternion_x = -a21 * a3344_4334 + a23 * a3144_4134 - a24 * a3143_4133; - double quaternion_y = a21 * a3244_4234 - a22 * a3144_4134 + a24 * a3142_4132; - double quaternion_z = -a21 * a3243_4233 + a22 * a3143_4133 - a23 * a3142_4132; - double qsqr = quaternion_w * quaternion_w + quaternion_x * quaternion_x + quaternion_y * quaternion_y + quaternion_z * quaternion_z; - - if (qsqr < eigenvector_precision) { - return Quaternion(); - } - - quaternion_x *= -1; - quaternion_y *= -1; - quaternion_z *= -1; - double min = quaternion_w; - min = quaternion_x < min ? quaternion_x : min; - min = quaternion_y < min ? quaternion_y : min; - min = quaternion_z < min ? quaternion_z : min; - quaternion_w /= min; - quaternion_x /= min; - quaternion_y /= min; - quaternion_z /= min; - - result = Quaternion(quaternion_x, quaternion_y, quaternion_z, quaternion_w).normalized(); - } - - return result; -} - -void QCP::translate(Vector3 r_translate, PackedVector3Array &r_x) { - for (Vector3 &p : r_x) { - p += r_translate; - } -} - -Vector3 QCP::get_translation() { - return target_center - moved_center; -} - -Vector3 QCP::move_to_weighted_center(PackedVector3Array &r_to_center, Vector &r_weight) { - Vector3 center; - double total_weight = 0; - bool weight_is_empty = r_weight.is_empty(); - int size = r_to_center.size(); - - for (int i = 0; i < size; i++) { - if (!weight_is_empty) { - total_weight += r_weight[i]; - center += r_to_center[i] * r_weight[i]; - } else { - center += r_to_center[i]; - total_weight++; - } - } - - if (total_weight > 0) { - center /= total_weight; - } - - return center; -} - -void QCP::inner_product(PackedVector3Array &coords1, PackedVector3Array &coords2) { - Vector3 weighted_coord1, weighted_coord2; - double sum_of_squares1 = 0, sum_of_squares2 = 0; - - sum_xx = 0; - sum_xy = 0; - sum_xz = 0; - sum_yx = 0; - sum_yy = 0; - sum_yz = 0; - sum_zx = 0; - sum_zy = 0; - sum_zz = 0; - - bool weight_is_empty = weight.is_empty(); - int size = coords1.size(); - - for (int i = 0; i < size; i++) { - if (!weight_is_empty) { - weighted_coord1 = weight[i] * coords1[i]; - sum_of_squares1 += weighted_coord1.dot(coords1[i]); - } else { - weighted_coord1 = coords1[i]; - sum_of_squares1 += weighted_coord1.dot(weighted_coord1); - } - - weighted_coord2 = coords2[i]; - - sum_of_squares2 += weight_is_empty ? weighted_coord2.dot(weighted_coord2) : (weight[i] * weighted_coord2.dot(weighted_coord2)); - - sum_xx += (weighted_coord1.x * weighted_coord2.x); - sum_xy += (weighted_coord1.x * weighted_coord2.y); - sum_xz += (weighted_coord1.x * weighted_coord2.z); - - sum_yx += (weighted_coord1.y * weighted_coord2.x); - sum_yy += (weighted_coord1.y * weighted_coord2.y); - sum_yz += (weighted_coord1.y * weighted_coord2.z); - - sum_zx += (weighted_coord1.z * weighted_coord2.x); - sum_zy += (weighted_coord1.z * weighted_coord2.y); - sum_zz += (weighted_coord1.z * weighted_coord2.z); - } - - double initial_eigenvalue = (sum_of_squares1 + sum_of_squares2) * 0.5; - - sum_xz_plus_zx = sum_xz + sum_zx; - sum_yz_plus_zy = sum_yz + sum_zy; - sum_xy_plus_yx = sum_xy + sum_yx; - sum_yz_minus_zy = sum_yz - sum_zy; - sum_xz_minus_zx = sum_xz - sum_zx; - sum_xy_minus_yx = sum_xy - sum_yx; - sum_xx_plus_yy = sum_xx + sum_yy; - sum_xx_minus_yy = sum_xx - sum_yy; - max_eigenvalue = initial_eigenvalue; - - inner_product_calculated = true; -} - -Quaternion QCP::weighted_superpose(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool translate) { - set(p_moved, p_target, p_weight, translate); - return get_rotation(); -} - -void QCP::set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool p_translate) { - transformation_calculated = false; - inner_product_calculated = false; - - moved = p_moved; - target = p_target; - weight = p_weight; - - if (p_translate) { - moved_center = move_to_weighted_center(moved, weight); - w_sum = 0; // set wsum to 0 so we don't double up. - target_center = move_to_weighted_center(target, weight); - translate(moved_center * -1, moved); - translate(target_center * -1, target); - } else { - if (!p_weight.is_empty()) { - for (int i = 0; i < p_weight.size(); i++) { - w_sum += p_weight[i]; - } - } else { - w_sum = p_moved.size(); - } - } -} diff --git a/godot/modules/renik/math/qcp.h b/godot/modules/renik/math/qcp.h deleted file mode 100644 index 4b42c554..00000000 --- a/godot/modules/renik/math/qcp.h +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************/ -/* qcp.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 QCP_H -#define QCP_H - -#include "core/math/basis.h" -#include "core/math/vector3.h" -#include "core/variant/variant.h" - -/** - * Implementation of the Quaternion-Based Characteristic Polynomial algorithm - * for RMSD and Superposition calculations. - * - * Usage: - * 1. Create a QCP object with two Vector3 arrays of equal length as input. - * The input coordinates are not changed. - * 2. Optionally, provide weighting factors [0 - 1] for each point. - * 3. For maximum efficiency, create a QCP object once and reuse it. - * - * A. Calculate rmsd only: double rmsd = qcp.getRmsd(); - * B. Calculate a 4x4 transformation (Quaternion and translation) matrix: Matrix4f trans = qcp.getTransformationMatrix(); - * C. Get transformed points (y superposed onto the reference x): Vector3[] ySuperposed = qcp.getTransformedCoordinates(); - * - * Citations: - * - Liu P, Agrafiotis DK, & Theobald DL (2011) Reply to comment on: "Fast determination of the optimal Quaternionation matrix for macromolecular superpositions." Journal of Computational Chemistry 32(1):185-186. [http://dx.doi.org/10.1002/jcc.21606] - * - Liu P, Agrafiotis DK, & Theobald DL (2010) "Fast determination of the optimal Quaternionation matrix for macromolecular superpositions." Journal of Computational Chemistry 31(7):1561-1563. [http://dx.doi.org/10.1002/jcc.21439] - * - Douglas L Theobald (2005) "Rapid calculation of RMSDs using a quaternion-based characteristic polynomial." Acta Crystallogr A 61(4):478-480. [http://dx.doi.org/10.1107/S0108767305015266] - * - * This is an adaptation of the original C code QCPQuaternion 1.4 (2012, October 10) to C++. - * The original C source code is available from http://theobald.brandeis.edu/qcp/ and was developed by: - * - Douglas L. Theobald, Department of Biochemistry, Brandeis University - * - Pu Liu, Johnson & Johnson Pharmaceutical Research and Development, L.L.C. - * - * @author Douglas L. Theobald (original C code) - * @author Pu Liu (original C code) - * @author Peter Rose (adapted to Java) - * @author Aleix Lafita (adapted to Java) - * @author Eron Gjoni (adapted to EWB IK) - * @author K. S. Ernest (iFire) Lee (adapted to ManyBoneIK) - */ - -class QCP { - double eigenvector_precision = 1E-6; - - PackedVector3Array target, moved; - Vector weight; - double w_sum = 0; - - Vector3 target_center, moved_center; - - double sum_xy = 0, sum_xz = 0, sum_yx = 0, sum_yz = 0, sum_zx = 0, sum_zy = 0; - double sum_xx_plus_yy = 0, sum_zz = 0, max_eigenvalue = 0, sum_yz_minus_zy = 0, sum_xz_minus_zx = 0, sum_xy_minus_yx = 0; - double sum_xx_minus_yy = 0, sum_xy_plus_yx = 0, sum_xz_plus_zx = 0; - double sum_yy = 0, sum_xx = 0, sum_yz_plus_zy = 0; - bool transformation_calculated = false, inner_product_calculated = false; - - void inner_product(PackedVector3Array &coords1, PackedVector3Array &coords2); - void set(PackedVector3Array &r_target, PackedVector3Array &r_moved); - Quaternion calculate_rotation(); - void set(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool p_translate); - static void translate(Vector3 r_translate, PackedVector3Array &r_x); - Vector3 move_to_weighted_center(PackedVector3Array &r_to_center, Vector &r_weight); - -public: - QCP(double p_evec_prec); - Quaternion weighted_superpose(PackedVector3Array &p_moved, PackedVector3Array &p_target, Vector &p_weight, bool translate); - Quaternion get_rotation(); - Vector3 get_translation(); -}; - -#endif // QCP_H diff --git a/godot/modules/renik/renik.cpp b/godot/modules/renik/renik.cpp index 79ec87ca..9f04e0ae 100644 --- a/godot/modules/renik/renik.cpp +++ b/godot/modules/renik/renik.cpp @@ -31,7 +31,6 @@ #include "renik.h" #include "core/math/quaternion.h" -#include "math/qcp.h" #include "scene/3d/marker_3d.h" #ifndef _3D_DISABLED @@ -66,7 +65,7 @@ #define RENIK_PROPERTY_STRING_FOOT_LEFT_TARGET_PATH "armature_left_foot_target" #define RENIK_PROPERTY_STRING_FOOT_RIGHT_TARGET_PATH "armature_right_foot_target" -RenIK::RenIK(){}; +RenIK::RenIK() {} void RenIK::_bind_methods() { ClassDB::bind_method(D_METHOD("set_live_preview", "p_enable"), @@ -1541,10 +1540,10 @@ RenIK::SpineTransforms RenIK::perform_torso_ik() { Transform3D correctedHipTransform = hipGlobalTransform; correctedHipTransform.set_origin(-hipLocalPose.origin); - HashMap ik_map = solve_ik_qcp( + HashMap ik_map = RenIK::solve_ifabrik( spine_chain, correctedHipTransform * skeleton->get_bone_rest(hip).basis.inverse(), - headGlobalTransform); + headGlobalTransform, 0.01, 10); skeleton->set_bone_pose_rotation(hip, hipGlobalTransform.get_basis().get_rotation_quaternion()); skeleton->set_bone_pose_position(hip, hipGlobalTransform.get_origin()); @@ -3349,115 +3348,6 @@ float RenIK::get_sideways_scaling_ease() const { return placement.sideways_gait.scaling_ease * 100.0; } -HashMap RenIK::solve_ik_qcp(Ref chain, - Transform3D root, - Transform3D target) { - HashMap map; - - if (!chain->is_valid()) { - return map; - } - - Vector joints = chain->get_joints(); - const Transform3D true_root = root.translated_local(joints[0].relative_prev); - const Vector3 priority = Vector3(1.0 / 7.0, 1.0 / 7.0, 1.0 / 7.0); - - Vector global_transforms; - global_transforms.resize(joints.size()); - Transform3D current_global_transform = true_root; - - Vector local_transforms; - local_transforms.resize(joints.size()); - - for (int i = 0; i < joints.size(); i++) { - Transform3D local_transform; - local_transform.basis = Basis(joints[i].rotation); - if (i == 0) { - local_transform.origin = root.origin; - } else { - local_transform.origin = joints[i - 1].relative_next; - } - local_transforms.write[i] = local_transform; - } - - for (int i = 0; i < joints.size(); i++) { - current_global_transform *= local_transforms[i]; - global_transforms.write[i] = current_global_transform; - } - - static constexpr double evec_prec = static_cast(1E-6); - QCP qcp = QCP(evec_prec); - - Vector rest_positions; - Vector target_positions; - Vector weights; - rest_positions.resize(7); - target_positions.resize(7); - weights.resize(7); - weights.fill(1.0); - - for (int joint_i = 0; joint_i < global_transforms.size(); joint_i++) { - Transform3D bone_direction_global_transform = global_transforms[joint_i]; - real_t pin_weight = weights[joint_i]; - - Basis tip_basis = bone_direction_global_transform.basis.orthogonalized(); - Quaternion quaternion = tip_basis.get_rotation_quaternion(); - tip_basis.set_quaternion_scale(quaternion, tip_basis.get_scale()); - - rest_positions.write[0] = target.origin - bone_direction_global_transform.origin; - - double scale_by = pin_weight; - Vector3 target_global_space = target.origin; - if (!quaternion.is_equal_approx(Quaternion())) { - target_global_space = bone_direction_global_transform.xform(target.origin); - } - double distance = target_global_space.distance_to(bone_direction_global_transform.origin); - scale_by = MAX(1.0, distance); - - int rest_index = 1; - for (int axis_i = Vector3::AXIS_X; axis_i <= Vector3::AXIS_Z; ++axis_i) { - if (priority[axis_i] > 0.0) { - Vector3 column = target.basis.get_column(axis_i); - rest_positions.write[rest_index] = bone_direction_global_transform.affine_inverse().xform((column + target.origin) - bone_direction_global_transform.origin); - rest_positions.write[rest_index] *= scale_by; - rest_index++; - rest_positions.write[rest_index] = bone_direction_global_transform.affine_inverse().xform((target.origin - column) - bone_direction_global_transform.origin); - rest_positions.write[rest_index] *= scale_by; - rest_index++; - } - } - - target_positions.write[0] = target.origin - bone_direction_global_transform.origin; - - scale_by = pin_weight; - int target_index = 1; - for (int axis_j = Vector3::AXIS_X; axis_j <= Vector3::AXIS_Z; ++axis_j) { - if (priority[axis_j] > 0.0) { - real_t w = weights[target_index]; - Vector3 column = tip_basis.get_column(axis_j) * priority[axis_j]; - target_positions.write[target_index] = bone_direction_global_transform.xform((column + target.origin) - bone_direction_global_transform.origin); - target_positions.write[target_index] *= Vector3(w, w, w); - target_index++; - target_positions.write[target_index] = bone_direction_global_transform.xform((target.origin - column) - bone_direction_global_transform.origin); - target_positions.write[target_index] *= Vector3(w, w, w); - target_index++; - } - } - - Quaternion solved_global_pose = qcp.weighted_superpose(rest_positions, target_positions, weights, false); - - int parent_index = joint_i > 0 ? joint_i - 1 : 0; - const Basis new_rot = global_transforms[parent_index].basis; - - const Quaternion local_pose = new_rot.inverse() * solved_global_pose * new_rot; - map.insert(joints[joint_i].id, local_pose); - - global_transforms.write[joint_i] = global_transforms[parent_index] * Transform3D(Basis(local_pose)); - } - - return map; -} - void RenIK::setup_humanoid_bones(bool set_targets) { ERR_FAIL_NULL(skeleton); static const String HEAD = "Head"; @@ -3541,4 +3431,132 @@ bool RenIK::get_setup_humanoid_bones() const { return is_setup_humanoid_bones; } +HashMap +RenIK::solve_ifabrik(Ref chain, Transform3D root, + Transform3D target, float threshold, int loopLimit) { + HashMap map; + if (chain->is_valid()) { // if the chain is valid there's at least one joint + // in the chain and there's one bone between it and + // the root + Vector joints = + chain->get_joints(); // just so I don't have to call it all the time + Transform3D trueRoot = root.translated_local(joints[0].relative_prev); + Transform3D targetDelta = + target * + chain->get_relative_rest_leaf() + .affine_inverse(); // how the change in the target would affect the + // chain if the chain was parented to the target + // instead of the root + Transform3D trueRelativeTarget = trueRoot.affine_inverse() * target; + Quaternion alignToTarget = RenIKHelper::align_vectors( + chain->get_relative_rest_leaf().origin - joints[0].relative_prev, + trueRelativeTarget.origin); + float heightDiff = + (chain->get_relative_rest_leaf().origin - joints[0].relative_prev) + .length() - + trueRelativeTarget.origin.length(); + heightDiff = heightDiff < 0 ? 0 : heightDiff; + Transform3D prebentRoot = + Transform3D(trueRoot.basis * alignToTarget, trueRoot.origin) + .translated_local( + (chain->chain_curve_direction * chain->get_total_length() * + heightDiff) - + joints[0].relative_prev); // The angle root is rotated + // to point at the target; + + Vector globalJointPoints; + + // We generate the starting points + // Here is where we take into account root and target influences and the + // prebend vector + Vector3 relativeJoint = joints[0].relative_prev; + for (int i = 1; i < joints.size(); i++) { + relativeJoint = relativeJoint + joints[i].relative_prev; + Vector3 prebentJoint = prebentRoot.xform( + relativeJoint); // if you rotated the root around the true root so + // that the whole chain was pointing to the leaf and + // then you moved everything along the prebend vector + Vector3 rootJoint = + root.xform(relativeJoint); // if you moved the joint with the root + Vector3 leafJoint = targetDelta.xform( + relativeJoint); // if you moved the joint with the leaf + prebentJoint = prebentJoint.lerp(rootJoint, joints[i].root_influence); + prebentJoint = prebentJoint.lerp( + leafJoint, joints[i].leaf_influence); // leaf influence dominates + globalJointPoints.push_back(prebentJoint); + } + + // We then do regular FABRIK + for (int i = 0; i < loopLimit; i++) { + Vector3 lastJoint = target.origin; + // Backward + for (int j = joints.size() - 1; j >= 1; + j--) { // we skip the first joint because we're not allowed to move + // that joint + Vector3 delta = globalJointPoints[j - 1] - lastJoint; + delta = delta.normalized() * joints[j].next_distance; + globalJointPoints.set(j - 1, lastJoint + delta); + lastJoint = globalJointPoints[j - 1]; + } + lastJoint = trueRoot.origin; // the root joint + + // Forwards + for (int j = 1; j < joints.size(); + j++) { // we skip the first joint because we're not allowed to move + // that joint + Vector3 delta = globalJointPoints[j - 1] - lastJoint; + delta = delta.normalized() * joints[j].prev_distance; + globalJointPoints.set(j - 1, lastJoint + delta); + lastJoint = globalJointPoints[j - 1]; + } + + float error = (lastJoint - trueRoot.origin).length(); + if (error < threshold) { + break; + } + } + + // Add a little twist + // We align the leaf's y axis with the rest_leaf's y-axis and see how far + // off the x-axes are to calculate the twist. + trueRelativeTarget.orthonormalize(); + Vector3 leafX = + RenIKHelper::align_vectors( + trueRelativeTarget.basis.xform(Vector3(0, 1, 0)), + chain->get_relative_rest_leaf().basis.xform(Vector3(0, 1, 0))) + .normalized() + .xform(trueRelativeTarget.basis.xform(Vector3(1, 0, 0))); + Vector3 restX = + chain->get_relative_rest_leaf().basis.xform(Vector3(1, 0, 0)); + float maxTwist = leafX.angle_to(restX); + if (leafX.cross(restX).dot(Vector3(0, 1, 0)) > 0) { + maxTwist *= -1; + } + + // Convert everything to quaternions and store it in the map + Quaternion parentRot = root.get_basis().get_rotation_quaternion(); + Vector3 parentPos = trueRoot.origin; + Quaternion prevTwist; + globalJointPoints.push_back(target.origin); + for (int i = 0; i < joints.size(); + i++) { // the last one's rotation is defined by the leaf position not a + // joint so we skip it + Quaternion pose = RenIKHelper::align_vectors( + Vector3(0, 1, 0), + Transform3D(parentRot * joints[i].rotation, parentPos) + .affine_inverse() + .xform(globalJointPoints[i])); // offset by one because joints has + // one extra element + Quaternion twist = + Quaternion(Vector3(0, 1, 0), maxTwist * joints[i].twist_influence); + pose = prevTwist.inverse() * joints[i].rotation * pose * twist; + prevTwist = twist; + map.insert(joints[i].id, pose); + parentRot = parentRot * pose; + parentPos = globalJointPoints[i]; + } + } + return map; +} + #endif // _3D_DISABLED diff --git a/godot/modules/renik/renik.h b/godot/modules/renik/renik.h index 23528bf7..b8b3bdf1 100644 --- a/godot/modules/renik/renik.h +++ b/godot/modules/renik/renik.h @@ -78,9 +78,6 @@ class RenIK : public Node { static void _bind_methods(); public: - HashMap solve_ik_qcp(Ref chain, - Transform3D root, - Transform3D target); void set_setup_humanoid_bones(bool set_targets); bool get_setup_humanoid_bones() const; void update_ik(); @@ -471,7 +468,7 @@ class RenIK : public Node { Transform3D target); static HashMap - solve_ifabrik(Ref chain, Transform3D chain_parent_transform, + solve_ifabrik(Ref chain, Transform3D root, Transform3D target, float threshold, int loopLimit); private: diff --git a/godot/modules/renik/tests/test_qcp.h b/godot/modules/renik/tests/test_qcp.h deleted file mode 100644 index b873e13b..00000000 --- a/godot/modules/renik/tests/test_qcp.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************/ -/* test_qcp.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 TEST_QCP_H -#define TEST_QCP_H - -#include "core/math/quaternion.h" -#include "modules/renik/math/qcp.h" -#include "tests/test_macros.h" - -namespace TestQCP { - -TEST_CASE("[Modules][QCP] Weighted Superpose") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); - - Quaternion expected = Quaternion(0, 0, sqrt(2) / 2, sqrt(2) / 2); - PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; - PackedVector3Array target = moved; - for (Vector3 &element : target) { - element = expected.xform(element); - } - Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights - - Quaternion result = qcp.weighted_superpose(moved, target, weight, false); - CHECK(abs(result.x - expected.x) < epsilon); - CHECK(abs(result.y - expected.y) < epsilon); - CHECK(abs(result.z - expected.z) < epsilon); - CHECK(abs(result.w - expected.w) < epsilon); -} - -TEST_CASE("[Modules][QCP] Weighted Translation") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); - - Quaternion expected; - PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; - PackedVector3Array target = moved; - Vector3 translation_vector = Vector3(1, 2, 3); - for (Vector3 &element : target) { - element = expected.xform(element + translation_vector); - } - Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights - bool translate = true; - - Quaternion result = qcp.weighted_superpose(moved, target, weight, translate); - CHECK(abs(result.x - expected.x) < epsilon); - CHECK(abs(result.y - expected.y) < epsilon); - CHECK(abs(result.z - expected.z) < epsilon); - CHECK(abs(result.w - expected.w) < epsilon); - - // Check if translation occurred - CHECK(translate); - Vector3 translation_result = expected.xform_inv(qcp.get_translation()); - CHECK(abs(translation_result.x - translation_vector.x) < epsilon); - CHECK(abs(translation_result.y - translation_vector.y) < epsilon); - CHECK(abs(translation_result.z - translation_vector.z) < epsilon); -} - -TEST_CASE("[Modules][QCP] Weighted Translation Shortest Path") { - double epsilon = CMP_EPSILON; - QCP qcp(epsilon); - - Quaternion expected = Quaternion(1, 2, 3, 4).normalized(); - PackedVector3Array moved = { Vector3(4, 5, 6), Vector3(7, 8, 9), Vector3(1, 2, 3) }; - PackedVector3Array target = moved; - Vector3 translation_vector = Vector3(1, 2, 3); - for (Vector3 &element : target) { - element = expected.xform(element + translation_vector); - } - Vector weight = { 1.0, 1.0, 1.0 }; // Equal weights - bool translate = true; - - Quaternion result = qcp.weighted_superpose(moved, target, weight, translate); - CHECK(abs(result.x - expected.x) > epsilon); - CHECK(abs(result.y - expected.y) > epsilon); - CHECK(abs(result.z - expected.z) > epsilon); - CHECK(abs(result.w - expected.w) > epsilon); - - // Check if translation occurred - CHECK(translate); - Vector3 translation_result = expected.xform_inv(qcp.get_translation()); - CHECK(abs(translation_result.x - translation_vector.x) > epsilon); - CHECK(abs(translation_result.y - translation_vector.y) > epsilon); - CHECK(abs(translation_result.z - translation_vector.z) > epsilon); -} -} // namespace TestQCP - -#endif // TEST_QCP_H diff --git a/godot/modules/renik/tests/test_solve_ik_qcp.h b/godot/modules/renik/tests/test_solve_ik_qcp.h deleted file mode 100644 index f2f82d42..00000000 --- a/godot/modules/renik/tests/test_solve_ik_qcp.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************/ -/* test_solve_ik_qcp.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 TEST_SOLVE_IK_QCP_H -#define TEST_SOLVE_IK_QCP_H - -// #include "core/math/transform_3d.h" -// #include "modules/renik/renik/renik_chain.h" -// #include "modules/renik/renik.h" -// #include "scene/3d/skeleton_3d.h" -// #include "tests/test_macros.h" - -namespace TestSolveIKQCP { -// TEST_CASE("[Modules][RenIK][SceneTree] Solve IK QCP") { -// double epsilon = CMP_EPSILON; - -// Ref chain; -// chain.instantiate(); -// chain->init(Vector3(0, 1, 0), 0.5f, 0.5f, 0.2f, 0.1f); - -// Skeleton3D *skeleton = memnew(Skeleton3D); - -// skeleton->add_bone("root_bone"); -// skeleton->add_bone("leaf_bone"); -// CHECK_EQ(skeleton->get_bone_count(), 2); -// BoneId root_bone_id = skeleton->find_bone("root_bone"); -// CHECK_NE(root_bone_id, -1); -// BoneId leaf_bone_id = skeleton->find_bone("leaf_bone"); -// CHECK_NE(leaf_bone_id, -1); -// skeleton->set_bone_parent(leaf_bone_id, root_bone_id); -// Transform3D root_rest_pose(Basis(), Vector3(0, 0, 0)); -// Transform3D leaf_rest_pose(Basis(), Vector3(1, 0, 0)); -// skeleton->set_bone_rest(root_bone_id, root_rest_pose); -// skeleton->set_bone_rest(leaf_bone_id, leaf_rest_pose); -// chain->set_root_bone(skeleton, root_bone_id); -// chain->set_leaf_bone(skeleton, leaf_bone_id); - -// Transform3D root(Basis(), Vector3(0, 0, 0)); -// Transform3D target(Basis(), Vector3(2, 0, 0)); - -// RenIK *renik = memnew(RenIK); -// skeleton->add_child(renik); -// renik->set_owner(skeleton); -// renik->set_skeleton_path(NodePath("..")); -// renik->set_setup_humanoid_bones(true); -// renik->set_live_preview(true); -// HashMap result = renik->solve_ik_qcp(chain, root, target); - -// CHECK(result.has(root_bone_id)); -// CHECK(result.has(leaf_bone_id)); - -// Quaternion expected_rotation = Quaternion(); -// CHECK(abs(result[root_bone_id].x - expected_rotation.x) < epsilon); -// CHECK(abs(result[root_bone_id].y - expected_rotation.y) < epsilon); -// CHECK(abs(result[root_bone_id].z - expected_rotation.z) < epsilon); -// CHECK(abs(result[root_bone_id].w - expected_rotation.w) < epsilon); - -// CHECK(abs(result[leaf_bone_id].x - expected_rotation.x) < epsilon); -// CHECK(abs(result[leaf_bone_id].y - expected_rotation.y) < epsilon); -// CHECK(abs(result[leaf_bone_id].z - expected_rotation.z) < epsilon); -// CHECK(abs(result[leaf_bone_id].w - expected_rotation.w) < epsilon); -// } -} // namespace TestSolveIKQCP - -#endif // TEST_SOLVE_IK_QCP_H diff --git a/godot/modules/text_server_adv/thorvg_svg_in_ot.cpp b/godot/modules/text_server_adv/thorvg_svg_in_ot.cpp index 136ccf3a..f546c5bc 100644 --- a/godot/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/godot/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -57,8 +57,6 @@ using namespace godot; #include "thorvg_svg_in_ot.h" -#include "thorvg_bounds_iterator.h" - #include #include @@ -92,8 +90,9 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin parser.instantiate(); parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length); - float aspect = 1.0f; String xml_body; + double embox_x = document->units_per_EM; + double embox_y = document->units_per_EM; while (parser->read() == OK) { if (parser->has_attribute("id")) { const String &gl_name = parser->get_named_attribute_value("id"); @@ -111,15 +110,26 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" "); if (vb.size() == 4) { - aspect = vb[2].to_float() / vb[3].to_float(); + embox_x = vb[2].to_float(); + embox_y = vb[3].to_float(); } } - continue; + if (parser->has_attribute("width")) { + embox_x = parser->get_named_attribute_value("width").to_float(); + } + if (parser->has_attribute("height")) { + embox_y = parser->get_named_attribute_value("height").to_float(); + } } if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { xml_body += vformat("<%s", parser->get_node_name()); + bool is_svg_tag = parser->get_node_name() == "svg"; for (int i = 0; i < parser->get_attribute_count(); i++) { - xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i)); + String aname = parser->get_attribute_name(i); + if (is_svg_tag && (aname == "viewBox" || aname == "width" || aname == "height")) { + continue; + } + xml_body += vformat(" %s=\"%s\"", aname, parser->get_attribute_value(i)); } if (parser->is_empty()) { @@ -133,91 +143,78 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin xml_body += vformat("", parser->get_node_name()); } } - String temp_xml_str = "" + xml_body; - CharString temp_xml = temp_xml_str.utf8(); std::unique_ptr picture = tvg::Picture::gen(); - tvg::Result result = picture->load(temp_xml.get_data(), temp_xml.length(), "svg+xml", false); - if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection)."); - } - - float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY; - tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y); + gl_state.xml_code = xml_body.utf8(); - float new_h = (max_y - min_y); - float new_w = (max_x - min_x); - - if (new_h * aspect >= new_w) { - new_w = (new_h * aspect); - } else { - new_h = (new_w / aspect); + tvg::Result result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); } - String xml_code_str = "" + xml_body; - gl_state.xml_code = xml_code_str.utf8(); + float svg_width, svg_height; + picture->size(&svg_width, &svg_height); + double aspect = svg_width / svg_height; - picture = tvg::Picture::gen(); - result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); + result = picture->size(embox_x * aspect, embox_y); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); } - float x_svg_to_out, y_svg_to_out; - x_svg_to_out = (float)metrics.x_ppem / new_w; - y_svg_to_out = (float)metrics.y_ppem / new_h; + double x_svg_to_out = (double)metrics.x_ppem / embox_x; + double y_svg_to_out = (double)metrics.y_ppem / embox_y; - gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out; - gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out; - gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out; - gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out; - gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem; - gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem; + gl_state.m.e11 = (double)document->transform.xx / (1 << 16); + gl_state.m.e12 = -(double)document->transform.xy / (1 << 16); + gl_state.m.e21 = -(double)document->transform.yx / (1 << 16); + gl_state.m.e22 = (double)document->transform.yy / (1 << 16); + gl_state.m.e13 = (double)document->delta.x / 64 * embox_x / metrics.x_ppem; + gl_state.m.e23 = -(double)document->delta.y / 64 * embox_y / metrics.y_ppem; gl_state.m.e31 = 0; gl_state.m.e32 = 0; gl_state.m.e33 = 1; - result = picture->transform(gl_state.m); + result = picture->size(embox_x * aspect * x_svg_to_out, embox_y * y_svg_to_out); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); } - result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true); + result = picture->transform(gl_state.m); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); } - gl_state.bmp_y = gl_state.h + metrics.descender / 64.f; - gl_state.bmp_x = 0; + picture->size(&gl_state.w, &gl_state.h); + gl_state.x = (gl_state.h - gl_state.w) / 2.0; + gl_state.y = -gl_state.h; gl_state.ready = true; } - p_slot->bitmap_left = (FT_Int)gl_state.bmp_x; - p_slot->bitmap_top = (FT_Int)gl_state.bmp_y; + p_slot->bitmap_left = (FT_Int)gl_state.x; + p_slot->bitmap_top = (FT_Int)-gl_state.y; - float tmp = ceil(gl_state.h); - p_slot->bitmap.rows = (unsigned int)tmp; - tmp = ceil(gl_state.w); - p_slot->bitmap.width = (unsigned int)tmp; + double tmpd = Math::ceil(gl_state.h); + p_slot->bitmap.rows = (unsigned int)tmpd; + tmpd = Math::ceil(gl_state.w); + p_slot->bitmap.width = (unsigned int)tmpd; p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4; + p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; - float metrics_width, metrics_height; - float horiBearingX, horiBearingY; - float vertBearingX, vertBearingY; + float metrics_width = (float)gl_state.w; + float metrics_height = (float)gl_state.h; + + float horiBearingX = (float)gl_state.x; + float horiBearingY = (float)-gl_state.y; - metrics_width = (float)gl_state.w; - metrics_height = (float)gl_state.h; - horiBearingX = (float)gl_state.x; - horiBearingY = (float)-gl_state.y; - vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2; - vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2; + float vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2; + float vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2; - tmp = roundf(metrics_width * 64); - p_slot->metrics.width = (FT_Pos)tmp; - tmp = roundf(metrics_height * 64); - p_slot->metrics.height = (FT_Pos)tmp; + float tmpf = Math::round(metrics_width * 64); + p_slot->metrics.width = (FT_Pos)tmpf; + tmpf = Math::round(metrics_height * 64); + p_slot->metrics.height = (FT_Pos)tmpf; p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64); p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64); @@ -250,6 +247,10 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); } + res = picture->size(gl_state.w, gl_state.h); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); + } res = picture->transform(gl_state.m); if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); diff --git a/godot/modules/text_server_adv/thorvg_svg_in_ot.h b/godot/modules/text_server_adv/thorvg_svg_in_ot.h index ce048674..a0e7e3a1 100644 --- a/godot/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/godot/modules/text_server_adv/thorvg_svg_in_ot.h @@ -60,8 +60,6 @@ using namespace godot; struct GL_State { bool ready = false; - float bmp_x = 0; - float bmp_y = 0; float x = 0; float y = 0; float w = 0; diff --git a/godot/modules/text_server_fb/thorvg_bounds_iterator.cpp b/godot/modules/text_server_fb/thorvg_bounds_iterator.cpp deleted file mode 100644 index d273eef9..00000000 --- a/godot/modules/text_server_fb/thorvg_bounds_iterator.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.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. */ -/**************************************************************************/ - -#ifdef GDEXTENSION -// Headers for building as GDExtension plug-in. - -#include - -using namespace godot; - -#elif defined(GODOT_MODULE) -// Headers for building as built-in module. - -#include "core/typedefs.h" - -#include "modules/modules_enabled.gen.h" // For svg. -#endif - -#ifdef MODULE_SVG_ENABLED - -#include "thorvg_bounds_iterator.h" - -#include -#include - -// This function uses private ThorVG API to get bounding box of top level children elements. - -void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) { - tvg::IteratorAccessor itrAccessor; - if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) { - while (const tvg::Paint *child = it->next()) { - float x = 0, y = 0, w = 0, h = 0; - child->bounds(&x, &y, &w, &h, true); - r_min_x = MIN(x, r_min_x); - r_min_y = MIN(y, r_min_y); - r_max_x = MAX(x + w, r_max_x); - r_max_y = MAX(y + h, r_max_y); - } - delete (it); - } -} - -#endif // MODULE_SVG_ENABLED diff --git a/godot/modules/text_server_fb/thorvg_bounds_iterator.h b/godot/modules/text_server_fb/thorvg_bounds_iterator.h deleted file mode 100644 index afa2c137..00000000 --- a/godot/modules/text_server_fb/thorvg_bounds_iterator.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.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 THORVG_BOUNDS_ITERATOR_H -#define THORVG_BOUNDS_ITERATOR_H - -#ifdef GDEXTENSION -// Headers for building as GDExtension plug-in. - -#include -#include - -using namespace godot; - -#elif defined(GODOT_MODULE) -// Headers for building as built-in module. - -#include "core/typedefs.h" - -#include "modules/modules_enabled.gen.h" // For svg. -#endif - -#ifdef MODULE_SVG_ENABLED - -#include - -void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y); - -#endif // MODULE_SVG_ENABLED - -#endif // THORVG_BOUNDS_ITERATOR_H diff --git a/godot/modules/text_server_fb/thorvg_svg_in_ot.cpp b/godot/modules/text_server_fb/thorvg_svg_in_ot.cpp index 1ad33a88..f546c5bc 100644 --- a/godot/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/godot/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -49,7 +49,7 @@ using namespace godot; #include "core/typedefs.h" #include "core/variant/variant.h" -#include "modules/modules_enabled.gen.h" // For svg. +#include "modules/modules_enabled.gen.h" // For svg, freetype. #endif #ifdef MODULE_SVG_ENABLED @@ -57,8 +57,6 @@ using namespace godot; #include "thorvg_svg_in_ot.h" -#include "thorvg_bounds_iterator.h" - #include #include @@ -92,8 +90,9 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin parser.instantiate(); parser->_open_buffer((const uint8_t *)document->svg_document, document->svg_document_length); - float aspect = 1.0f; String xml_body; + double embox_x = document->units_per_EM; + double embox_y = document->units_per_EM; while (parser->read() == OK) { if (parser->has_attribute("id")) { const String &gl_name = parser->get_named_attribute_value("id"); @@ -111,15 +110,26 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin PackedStringArray vb = parser->get_named_attribute_value("viewBox").split(" "); if (vb.size() == 4) { - aspect = vb[2].to_float() / vb[3].to_float(); + embox_x = vb[2].to_float(); + embox_y = vb[3].to_float(); } } - continue; + if (parser->has_attribute("width")) { + embox_x = parser->get_named_attribute_value("width").to_float(); + } + if (parser->has_attribute("height")) { + embox_y = parser->get_named_attribute_value("height").to_float(); + } } if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { xml_body += vformat("<%s", parser->get_node_name()); + bool is_svg_tag = parser->get_node_name() == "svg"; for (int i = 0; i < parser->get_attribute_count(); i++) { - xml_body += vformat(" %s=\"%s\"", parser->get_attribute_name(i), parser->get_attribute_value(i)); + String aname = parser->get_attribute_name(i); + if (is_svg_tag && (aname == "viewBox" || aname == "width" || aname == "height")) { + continue; + } + xml_body += vformat(" %s=\"%s\"", aname, parser->get_attribute_value(i)); } if (parser->is_empty()) { @@ -133,91 +143,78 @@ FT_Error tvg_svg_in_ot_preset_slot(FT_GlyphSlot p_slot, FT_Bool p_cache, FT_Poin xml_body += vformat("", parser->get_node_name()); } } - String temp_xml_str = "" + xml_body; - CharString temp_xml = temp_xml_str.utf8(); std::unique_ptr picture = tvg::Picture::gen(); - tvg::Result result = picture->load(temp_xml.get_data(), temp_xml.length(), "svg+xml", false); - if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (bounds detection)."); - } - - float min_x = INFINITY, min_y = INFINITY, max_x = -INFINITY, max_y = -INFINITY; - tvg_get_bounds(picture.get(), min_x, min_y, max_x, max_y); + gl_state.xml_code = xml_body.utf8(); - float new_h = (max_y - min_y); - float new_w = (max_x - min_x); - - if (new_h * aspect >= new_w) { - new_w = (new_h * aspect); - } else { - new_h = (new_w / aspect); + tvg::Result result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); + if (result != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); } - String xml_code_str = "" + xml_body; - gl_state.xml_code = xml_code_str.utf8(); + float svg_width, svg_height; + picture->size(&svg_width, &svg_height); + double aspect = svg_width / svg_height; - picture = tvg::Picture::gen(); - result = picture->load(gl_state.xml_code.get_data(), gl_state.xml_code.length(), "svg+xml", false); + result = picture->size(embox_x * aspect, embox_y); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph metrics)."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); } - float x_svg_to_out, y_svg_to_out; - x_svg_to_out = (float)metrics.x_ppem / new_w; - y_svg_to_out = (float)metrics.y_ppem / new_h; + double x_svg_to_out = (double)metrics.x_ppem / embox_x; + double y_svg_to_out = (double)metrics.y_ppem / embox_y; - gl_state.m.e11 = (double)document->transform.xx / (1 << 16) * x_svg_to_out; - gl_state.m.e12 = -(double)document->transform.xy / (1 << 16) * x_svg_to_out; - gl_state.m.e21 = -(double)document->transform.yx / (1 << 16) * y_svg_to_out; - gl_state.m.e22 = (double)document->transform.yy / (1 << 16) * y_svg_to_out; - gl_state.m.e13 = (double)document->delta.x / 64 * new_w / metrics.x_ppem; - gl_state.m.e23 = -(double)document->delta.y / 64 * new_h / metrics.y_ppem; + gl_state.m.e11 = (double)document->transform.xx / (1 << 16); + gl_state.m.e12 = -(double)document->transform.xy / (1 << 16); + gl_state.m.e21 = -(double)document->transform.yx / (1 << 16); + gl_state.m.e22 = (double)document->transform.yy / (1 << 16); + gl_state.m.e13 = (double)document->delta.x / 64 * embox_x / metrics.x_ppem; + gl_state.m.e23 = -(double)document->delta.y / 64 * embox_y / metrics.y_ppem; gl_state.m.e31 = 0; gl_state.m.e32 = 0; gl_state.m.e33 = 1; - result = picture->transform(gl_state.m); + result = picture->size(embox_x * aspect * x_svg_to_out, embox_y * y_svg_to_out); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); } - result = picture->bounds(&gl_state.x, &gl_state.y, &gl_state.w, &gl_state.h, true); + result = picture->transform(gl_state.m); if (result != tvg::Result::Success) { - ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to get SVG bounds."); + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); } - gl_state.bmp_y = gl_state.h + metrics.descender / 64.f; - gl_state.bmp_x = 0; + picture->size(&gl_state.w, &gl_state.h); + gl_state.x = (gl_state.h - gl_state.w) / 2.0; + gl_state.y = -gl_state.h; gl_state.ready = true; } - p_slot->bitmap_left = (FT_Int)gl_state.bmp_x; - p_slot->bitmap_top = (FT_Int)gl_state.bmp_y; + p_slot->bitmap_left = (FT_Int)gl_state.x; + p_slot->bitmap_top = (FT_Int)-gl_state.y; - float tmp = ceil(gl_state.h); - p_slot->bitmap.rows = (unsigned int)tmp; - tmp = ceil(gl_state.w); - p_slot->bitmap.width = (unsigned int)tmp; + double tmpd = Math::ceil(gl_state.h); + p_slot->bitmap.rows = (unsigned int)tmpd; + tmpd = Math::ceil(gl_state.w); + p_slot->bitmap.width = (unsigned int)tmpd; p_slot->bitmap.pitch = (int)p_slot->bitmap.width * 4; + p_slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA; - float metrics_width, metrics_height; - float horiBearingX, horiBearingY; - float vertBearingX, vertBearingY; + float metrics_width = (float)gl_state.w; + float metrics_height = (float)gl_state.h; + + float horiBearingX = (float)gl_state.x; + float horiBearingY = (float)-gl_state.y; - metrics_width = (float)gl_state.w; - metrics_height = (float)gl_state.h; - horiBearingX = (float)gl_state.x; - horiBearingY = (float)-gl_state.y; - vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2; - vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2; + float vertBearingX = p_slot->metrics.horiBearingX / 64.0f - p_slot->metrics.horiAdvance / 64.0f / 2; + float vertBearingY = (p_slot->metrics.vertAdvance / 64.0f - p_slot->metrics.height / 64.0f) / 2; - tmp = roundf(metrics_width * 64); - p_slot->metrics.width = (FT_Pos)tmp; - tmp = roundf(metrics_height * 64); - p_slot->metrics.height = (FT_Pos)tmp; + float tmpf = Math::round(metrics_width * 64); + p_slot->metrics.width = (FT_Pos)tmpf; + tmpf = Math::round(metrics_height * 64); + p_slot->metrics.height = (FT_Pos)tmpf; p_slot->metrics.horiBearingX = (FT_Pos)(horiBearingX * 64); p_slot->metrics.horiBearingY = (FT_Pos)(horiBearingY * 64); @@ -250,6 +247,10 @@ FT_Error tvg_svg_in_ot_render(FT_GlyphSlot p_slot, FT_Pointer *p_state) { if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to load SVG document (glyph rendering)."); } + res = picture->size(gl_state.w, gl_state.h); + if (res != tvg::Result::Success) { + ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to resize SVG document."); + } res = picture->transform(gl_state.m); if (res != tvg::Result::Success) { ERR_FAIL_V_MSG(FT_Err_Invalid_SVG_Document, "Failed to apply transform to SVG document."); diff --git a/godot/modules/text_server_fb/thorvg_svg_in_ot.h b/godot/modules/text_server_fb/thorvg_svg_in_ot.h index ce048674..a0e7e3a1 100644 --- a/godot/modules/text_server_fb/thorvg_svg_in_ot.h +++ b/godot/modules/text_server_fb/thorvg_svg_in_ot.h @@ -60,8 +60,6 @@ using namespace godot; struct GL_State { bool ready = false; - float bmp_x = 0; - float bmp_y = 0; float x = 0; float y = 0; float w = 0; diff --git a/godot/modules/upnp/SCsub b/godot/modules/upnp/SCsub index 6657d75c..ba4a842c 100644 --- a/godot/modules/upnp/SCsub +++ b/godot/modules/upnp/SCsub @@ -10,7 +10,7 @@ env_upnp = env_modules.Clone() thirdparty_obj = [] -if env["builtin_miniupnpc"]: +if env["builtin_miniupnpc"] and env["platform"] != "web": thirdparty_dir = "#thirdparty/miniupnpc/" thirdparty_sources = [ "igd_desc_parse.c", diff --git a/godot/modules/upnp/register_types.cpp b/godot/modules/upnp/register_types.cpp index f6a34837..fdf39c0b 100644 --- a/godot/modules/upnp/register_types.cpp +++ b/godot/modules/upnp/register_types.cpp @@ -33,6 +33,11 @@ #include "upnp.h" #include "upnp_device.h" +#ifndef WEB_ENABLED +#include "upnp_device_miniupnp.h" +#include "upnp_miniupnp.h" +#endif + #include "core/error/error_macros.h" void initialize_upnp_module(ModuleInitializationLevel p_level) { @@ -40,8 +45,13 @@ void initialize_upnp_module(ModuleInitializationLevel p_level) { return; } - GDREGISTER_CLASS(UPNP); - GDREGISTER_CLASS(UPNPDevice); + ClassDB::register_custom_instance_class(); + ClassDB::register_custom_instance_class(); + +#ifndef WEB_ENABLED + UPNPMiniUPNP::make_default(); + UPNPDeviceMiniUPNP::make_default(); +#endif } void uninitialize_upnp_module(ModuleInitializationLevel p_level) { diff --git a/godot/modules/upnp/upnp.cpp b/godot/modules/upnp/upnp.cpp index 4305bf84..5ec0b984 100644 --- a/godot/modules/upnp/upnp.cpp +++ b/godot/modules/upnp/upnp.cpp @@ -30,298 +30,7 @@ #include "upnp.h" -#include -#include - -#include - -bool UPNP::is_common_device(const String &dev) const { - return dev.is_empty() || - dev.contains("InternetGatewayDevice") || - dev.contains("WANIPConnection") || - dev.contains("WANPPPConnection") || - dev.contains("rootdevice"); -} - -int UPNP::discover(int timeout, int ttl, const String &device_filter) { - ERR_FAIL_COND_V_MSG(timeout < 0, UPNP_RESULT_INVALID_PARAM, "The response's wait time can't be negative."); - ERR_FAIL_COND_V_MSG(ttl < 0 || ttl > 255, UPNP_RESULT_INVALID_PARAM, "The time-to-live must be set between 0 and 255 (inclusive)."); - - devices.clear(); - - int error = 0; - struct UPNPDev *devlist; - - CharString cs = discover_multicast_if.utf8(); - const char *m_if = cs.length() ? cs.get_data() : nullptr; - if (is_common_device(device_filter)) { - devlist = upnpDiscover(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error); - } else { - devlist = upnpDiscoverAll(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error); - } - - if (error != UPNPDISCOVER_SUCCESS) { - switch (error) { - case UPNPDISCOVER_SOCKET_ERROR: - return UPNP_RESULT_SOCKET_ERROR; - case UPNPDISCOVER_MEMORY_ERROR: - return UPNP_RESULT_MEM_ALLOC_ERROR; - default: - return UPNP_RESULT_UNKNOWN_ERROR; - } - } - - if (!devlist) { - return UPNP_RESULT_NO_DEVICES; - } - - struct UPNPDev *dev = devlist; - - while (dev) { - if (device_filter.is_empty() || strstr(dev->st, device_filter.utf8().get_data())) { - add_device_to_list(dev, devlist); - } - - dev = dev->pNext; - } - - freeUPNPDevlist(devlist); - - return UPNP_RESULT_SUCCESS; -} - -void UPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) { - Ref new_device; - new_device.instantiate(); - - new_device->set_description_url(dev->descURL); - new_device->set_service_type(dev->st); - - parse_igd(new_device, devlist); - - devices.push_back(new_device); -} - -char *UPNP::load_description(const String &url, int *size, int *status_code) const { - return (char *)miniwget(url.utf8().get_data(), size, 0, status_code); -} - -void UPNP::parse_igd(Ref dev, UPNPDev *devlist) { - int size = 0; - int status_code = -1; - char *xml = load_description(dev->get_description_url(), &size, &status_code); - - if (status_code != 200) { - dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR); - return; - } - - if (!xml || size < 1) { - dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY); - return; - } - - struct UPNPUrls urls = {}; - struct IGDdatas data; - - parserootdesc(xml, size, &data); - free(xml); - xml = nullptr; - - GetUPNPUrls(&urls, &data, dev->get_description_url().utf8().get_data(), 0); - - char addr[16]; -#if MINIUPNPC_API_VERSION >= 18 - int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16, nullptr, 0); -#else - int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16); -#endif - - if (i != 1) { - FreeUPNPUrls(&urls); - - switch (i) { - case 0: - dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD); - return; - case 2: - dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED); - return; - case 3: - dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE); - return; - default: - dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR); - return; - } - } - - if (urls.controlURL[0] == '\0') { - FreeUPNPUrls(&urls); - dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL); - return; - } - - dev->set_igd_control_url(urls.controlURL); - dev->set_igd_service_type(data.first.servicetype); - dev->set_igd_our_addr(addr); - dev->set_igd_status(UPNPDevice::IGD_STATUS_OK); - - FreeUPNPUrls(&urls); -} - -int UPNP::upnp_result(int in) { - switch (in) { - case UPNPCOMMAND_SUCCESS: - return UPNP_RESULT_SUCCESS; - case UPNPCOMMAND_UNKNOWN_ERROR: - return UPNP_RESULT_UNKNOWN_ERROR; - case UPNPCOMMAND_INVALID_ARGS: - return UPNP_RESULT_INVALID_ARGS; - case UPNPCOMMAND_HTTP_ERROR: - return UPNP_RESULT_HTTP_ERROR; - case UPNPCOMMAND_INVALID_RESPONSE: - return UPNP_RESULT_INVALID_RESPONSE; - case UPNPCOMMAND_MEM_ALLOC_ERROR: - return UPNP_RESULT_MEM_ALLOC_ERROR; - - case 402: - return UPNP_RESULT_INVALID_ARGS; - case 403: - return UPNP_RESULT_NOT_AUTHORIZED; - case 501: - return UPNP_RESULT_ACTION_FAILED; - case 606: - return UPNP_RESULT_NOT_AUTHORIZED; - case 714: - return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY; - case 715: - return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED; - case 716: - return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED; - case 718: - return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING; - case 724: - return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED; - case 725: - return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED; - case 726: - return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD; - case 727: - return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD; - case 728: - return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE; - case 729: - return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM; - case 732: - return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED; - case 733: - return UPNP_RESULT_INCONSISTENT_PARAMETERS; - } - - return UPNP_RESULT_UNKNOWN_ERROR; -} - -int UPNP::get_device_count() const { - return devices.size(); -} - -Ref UPNP::get_device(int index) const { - ERR_FAIL_INDEX_V(index, devices.size(), nullptr); - - return devices.get(index); -} - -void UPNP::add_device(Ref device) { - ERR_FAIL_COND(device.is_null()); - - devices.push_back(device); -} - -void UPNP::set_device(int index, Ref device) { - ERR_FAIL_INDEX(index, devices.size()); - ERR_FAIL_COND(device.is_null()); - - devices.set(index, device); -} - -void UPNP::remove_device(int index) { - ERR_FAIL_INDEX(index, devices.size()); - - devices.remove_at(index); -} - -void UPNP::clear_devices() { - devices.clear(); -} - -Ref UPNP::get_gateway() const { - ERR_FAIL_COND_V_MSG(devices.is_empty(), nullptr, "Couldn't find any UPNPDevices."); - - for (int i = 0; i < devices.size(); i++) { - Ref dev = get_device(i); - - if (dev.is_valid() && dev->is_valid_gateway()) { - return dev; - } - } - - return nullptr; -} - -void UPNP::set_discover_multicast_if(const String &m_if) { - discover_multicast_if = m_if; -} - -String UPNP::get_discover_multicast_if() const { - return discover_multicast_if; -} - -void UPNP::set_discover_local_port(int port) { - discover_local_port = port; -} - -int UPNP::get_discover_local_port() const { - return discover_local_port; -} - -void UPNP::set_discover_ipv6(bool ipv6) { - discover_ipv6 = ipv6; -} - -bool UPNP::is_discover_ipv6() const { - return discover_ipv6; -} - -String UPNP::query_external_address() const { - Ref dev = get_gateway(); - - if (dev.is_null()) { - return ""; - } - - return dev->query_external_address(); -} - -int UPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { - Ref dev = get_gateway(); - - if (dev.is_null()) { - return UPNP_RESULT_NO_GATEWAY; - } - - return dev->add_port_mapping(port, port_internal, desc, proto, duration); -} - -int UPNP::delete_port_mapping(int port, String proto) const { - Ref dev = get_gateway(); - - if (dev.is_null()) { - return UPNP_RESULT_NO_GATEWAY; - } - - return dev->delete_port_mapping(port, proto); -} +UPNP *(*UPNP::_create)(bool p_notify_postinitialize) = nullptr; void UPNP::_bind_methods() { ClassDB::bind_method(D_METHOD("get_device_count"), &UPNP::get_device_count); @@ -382,9 +91,3 @@ void UPNP::_bind_methods() { BIND_ENUM_CONSTANT(UPNP_RESULT_NO_DEVICES); BIND_ENUM_CONSTANT(UPNP_RESULT_UNKNOWN_ERROR); } - -UPNP::UPNP() { -} - -UPNP::~UPNP() { -} diff --git a/godot/modules/upnp/upnp.h b/godot/modules/upnp/upnp.h index dc9bbdbc..566b01ec 100644 --- a/godot/modules/upnp/upnp.h +++ b/godot/modules/upnp/upnp.h @@ -35,26 +35,14 @@ #include "core/object/ref_counted.h" -#include - class UPNP : public RefCounted { GDCLASS(UPNP, RefCounted); -private: - String discover_multicast_if = ""; - int discover_local_port = 0; - bool discover_ipv6 = false; - - Vector> devices; - - bool is_common_device(const String &dev) const; - void add_device_to_list(UPNPDev *dev, UPNPDev *devlist); - void parse_igd(Ref dev, UPNPDev *devlist); - char *load_description(const String &url, int *size, int *status_code) const; - protected: static void _bind_methods(); + static UPNP *(*_create)(bool p_notify_postinitialize); + public: enum UPNPResult { UPNP_RESULT_SUCCESS, @@ -88,35 +76,40 @@ class UPNP : public RefCounted { UPNP_RESULT_UNKNOWN_ERROR, }; - static int upnp_result(int in); + static UPNP *create(bool p_notify_postinitialize = true) { + if (!_create) { + return nullptr; + } + return _create(p_notify_postinitialize); + } - int get_device_count() const; - Ref get_device(int index) const; - void add_device(Ref device); - void set_device(int index, Ref device); - void remove_device(int index); - void clear_devices(); + virtual int get_device_count() const = 0; + virtual Ref get_device(int index) const = 0; + virtual void add_device(Ref device) = 0; + virtual void set_device(int index, Ref device) = 0; + virtual void remove_device(int index) = 0; + virtual void clear_devices() = 0; - Ref get_gateway() const; + virtual Ref get_gateway() const = 0; - int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice"); + virtual int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice") = 0; - String query_external_address() const; + virtual String query_external_address() const = 0; - int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const; - int delete_port_mapping(int port, String proto = "UDP") const; + virtual int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const = 0; + virtual int delete_port_mapping(int port, String proto = "UDP") const = 0; - void set_discover_multicast_if(const String &m_if); - String get_discover_multicast_if() const; + virtual void set_discover_multicast_if(const String &m_if) = 0; + virtual String get_discover_multicast_if() const = 0; - void set_discover_local_port(int port); - int get_discover_local_port() const; + virtual void set_discover_local_port(int port) = 0; + virtual int get_discover_local_port() const = 0; - void set_discover_ipv6(bool ipv6); - bool is_discover_ipv6() const; + virtual void set_discover_ipv6(bool ipv6) = 0; + virtual bool is_discover_ipv6() const = 0; - UPNP(); - ~UPNP(); + UPNP() {} + virtual ~UPNP() {} }; VARIANT_ENUM_CAST(UPNP::UPNPResult) diff --git a/godot/modules/upnp/upnp_device.cpp b/godot/modules/upnp/upnp_device.cpp index 11ee3681..45766281 100644 --- a/godot/modules/upnp/upnp_device.cpp +++ b/godot/modules/upnp/upnp_device.cpp @@ -30,119 +30,7 @@ #include "upnp_device.h" -#include "upnp.h" - -#include - -String UPNPDevice::query_external_address() const { - ERR_FAIL_COND_V_MSG(!is_valid_gateway(), "", "The Internet Gateway Device must be valid."); - - char addr[16]; - int i = UPNP_GetExternalIPAddress( - igd_control_url.utf8().get_data(), - igd_service_type.utf8().get_data(), - (char *)&addr); - - ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, "", "Couldn't get external IP address."); - - return String(addr); -} - -int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { - ERR_FAIL_COND_V_MSG(!is_valid_gateway(), UPNP::UPNP_RESULT_INVALID_GATEWAY, "The Internet Gateway Device must be valid."); - ERR_FAIL_COND_V_MSG(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 1 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(port_internal < 0 || port_internal > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 0 and 65535 (inclusive)."); // Needs to allow 0 because 0 signifies "use external port as internal port" - ERR_FAIL_COND_V_MSG(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL, "The protocol must be either TCP or UDP."); - ERR_FAIL_COND_V_MSG(duration < 0, UPNP::UPNP_RESULT_INVALID_DURATION, "The port mapping's lease duration can't be negative."); - - if (port_internal < 1) { - port_internal = port; - } - - int i = UPNP_AddPortMapping( - igd_control_url.utf8().get_data(), - igd_service_type.utf8().get_data(), - itos(port).utf8().get_data(), - itos(port_internal).utf8().get_data(), - igd_our_addr.utf8().get_data(), - desc.is_empty() ? nullptr : desc.utf8().get_data(), - proto.utf8().get_data(), - nullptr, // Remote host, always nullptr as IGDs don't support it - duration > 0 ? itos(duration).utf8().get_data() : nullptr); - - ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i), "Couldn't add port mapping."); - - return UPNP::UPNP_RESULT_SUCCESS; -} - -int UPNPDevice::delete_port_mapping(int port, String proto) const { - ERR_FAIL_COND_V_MSG(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 1 and 65535 (inclusive)."); - ERR_FAIL_COND_V_MSG(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL, "The protocol must be either TCP or UDP."); - - int i = UPNP_DeletePortMapping( - igd_control_url.utf8().get_data(), - igd_service_type.utf8().get_data(), - itos(port).utf8().get_data(), - proto.utf8().get_data(), - nullptr // Remote host, always nullptr as IGDs don't support it - ); - - ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, UPNP::upnp_result(i), "Couldn't delete port mapping."); - - return UPNP::UPNP_RESULT_SUCCESS; -} - -void UPNPDevice::set_description_url(const String &url) { - description_url = url; -} - -String UPNPDevice::get_description_url() const { - return description_url; -} - -void UPNPDevice::set_service_type(const String &type) { - service_type = type; -} - -String UPNPDevice::get_service_type() const { - return service_type; -} - -void UPNPDevice::set_igd_control_url(const String &url) { - igd_control_url = url; -} - -String UPNPDevice::get_igd_control_url() const { - return igd_control_url; -} - -void UPNPDevice::set_igd_service_type(const String &type) { - igd_service_type = type; -} - -String UPNPDevice::get_igd_service_type() const { - return igd_service_type; -} - -void UPNPDevice::set_igd_our_addr(const String &addr) { - igd_our_addr = addr; -} - -String UPNPDevice::get_igd_our_addr() const { - return igd_our_addr; -} - -void UPNPDevice::set_igd_status(IGDStatus status) { - igd_status = status; -} - -UPNPDevice::IGDStatus UPNPDevice::get_igd_status() const { - return igd_status; -} - -bool UPNPDevice::is_valid_gateway() const { - return igd_status == IGD_STATUS_OK; -} +UPNPDevice *(*UPNPDevice::_create)(bool p_notify_postinitialize) = nullptr; void UPNPDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("is_valid_gateway"), &UPNPDevice::is_valid_gateway); @@ -185,15 +73,3 @@ void UPNPDevice::_bind_methods() { BIND_ENUM_CONSTANT(IGD_STATUS_MALLOC_ERROR); BIND_ENUM_CONSTANT(IGD_STATUS_UNKNOWN_ERROR); } - -UPNPDevice::UPNPDevice() { - description_url = ""; - service_type = ""; - igd_control_url = ""; - igd_service_type = ""; - igd_our_addr = ""; - igd_status = IGD_STATUS_UNKNOWN_ERROR; -} - -UPNPDevice::~UPNPDevice() { -} diff --git a/godot/modules/upnp/upnp_device.h b/godot/modules/upnp/upnp_device.h index a49e5748..fdc5bab1 100644 --- a/godot/modules/upnp/upnp_device.h +++ b/godot/modules/upnp/upnp_device.h @@ -36,6 +36,11 @@ class UPNPDevice : public RefCounted { GDCLASS(UPNPDevice, RefCounted); +protected: + static void _bind_methods(); + + static UPNPDevice *(*_create)(bool p_notify_postinitialize); + public: enum IGDStatus { IGD_STATUS_OK, @@ -50,42 +55,38 @@ class UPNPDevice : public RefCounted { IGD_STATUS_UNKNOWN_ERROR, }; - void set_description_url(const String &url); - String get_description_url() const; + static UPNPDevice *create(bool p_notify_postinitialize = true) { + if (!_create) { + return nullptr; + } + return _create(p_notify_postinitialize); + } - void set_service_type(const String &type); - String get_service_type() const; + virtual void set_description_url(const String &url) = 0; + virtual String get_description_url() const = 0; - void set_igd_control_url(const String &url); - String get_igd_control_url() const; + virtual void set_service_type(const String &type) = 0; + virtual String get_service_type() const = 0; - void set_igd_service_type(const String &type); - String get_igd_service_type() const; + virtual void set_igd_control_url(const String &url) = 0; + virtual String get_igd_control_url() const = 0; - void set_igd_our_addr(const String &addr); - String get_igd_our_addr() const; + virtual void set_igd_service_type(const String &type) = 0; + virtual String get_igd_service_type() const = 0; - void set_igd_status(IGDStatus status); - IGDStatus get_igd_status() const; + virtual void set_igd_our_addr(const String &addr) = 0; + virtual String get_igd_our_addr() const = 0; - bool is_valid_gateway() const; - String query_external_address() const; - int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const; - int delete_port_mapping(int port, String proto = "UDP") const; + virtual void set_igd_status(IGDStatus status) = 0; + virtual IGDStatus get_igd_status() const = 0; - UPNPDevice(); - ~UPNPDevice(); - -protected: - static void _bind_methods(); + virtual bool is_valid_gateway() const = 0; + virtual String query_external_address() const = 0; + virtual int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const = 0; + virtual int delete_port_mapping(int port, String proto = "UDP") const = 0; -private: - String description_url; - String service_type; - String igd_control_url; - String igd_service_type; - String igd_our_addr; - IGDStatus igd_status; + UPNPDevice() {} + virtual ~UPNPDevice() {} }; VARIANT_ENUM_CAST(UPNPDevice::IGDStatus) diff --git a/godot/modules/upnp/upnp_device_miniupnp.cpp b/godot/modules/upnp/upnp_device_miniupnp.cpp new file mode 100644 index 00000000..46319f83 --- /dev/null +++ b/godot/modules/upnp/upnp_device_miniupnp.cpp @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* upnp_device_miniupnp.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 WEB_ENABLED + +#include "upnp_device_miniupnp.h" + +#include "upnp_miniupnp.h" + +#include + +void UPNPDeviceMiniUPNP::make_default() { + UPNPDevice::_create = UPNPDeviceMiniUPNP::_create; +} + +String UPNPDeviceMiniUPNP::query_external_address() const { + ERR_FAIL_COND_V_MSG(!is_valid_gateway(), "", "The Internet Gateway Device must be valid."); + + char addr[16]; + int i = UPNP_GetExternalIPAddress( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + (char *)&addr); + + ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, "", "Couldn't get external IP address."); + + return String(addr); +} + +int UPNPDeviceMiniUPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { + ERR_FAIL_COND_V_MSG(!is_valid_gateway(), UPNP::UPNP_RESULT_INVALID_GATEWAY, "The Internet Gateway Device must be valid."); + ERR_FAIL_COND_V_MSG(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 1 and 65535 (inclusive)."); + ERR_FAIL_COND_V_MSG(port_internal < 0 || port_internal > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 0 and 65535 (inclusive)."); // Needs to allow 0 because 0 signifies "use external port as internal port" + ERR_FAIL_COND_V_MSG(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL, "The protocol must be either TCP or UDP."); + ERR_FAIL_COND_V_MSG(duration < 0, UPNP::UPNP_RESULT_INVALID_DURATION, "The port mapping's lease duration can't be negative."); + + if (port_internal < 1) { + port_internal = port; + } + + int i = UPNP_AddPortMapping( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + itos(port).utf8().get_data(), + itos(port_internal).utf8().get_data(), + igd_our_addr.utf8().get_data(), + desc.is_empty() ? nullptr : desc.utf8().get_data(), + proto.utf8().get_data(), + nullptr, // Remote host, always nullptr as IGDs don't support it + duration > 0 ? itos(duration).utf8().get_data() : nullptr); + + ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, UPNPMiniUPNP::upnp_result(i), "Couldn't add port mapping."); + + return UPNP::UPNP_RESULT_SUCCESS; +} + +int UPNPDeviceMiniUPNP::delete_port_mapping(int port, String proto) const { + ERR_FAIL_COND_V_MSG(port < 1 || port > 65535, UPNP::UPNP_RESULT_INVALID_PORT, "The port number must be set between 1 and 65535 (inclusive)."); + ERR_FAIL_COND_V_MSG(proto != "UDP" && proto != "TCP", UPNP::UPNP_RESULT_INVALID_PROTOCOL, "The protocol must be either TCP or UDP."); + + int i = UPNP_DeletePortMapping( + igd_control_url.utf8().get_data(), + igd_service_type.utf8().get_data(), + itos(port).utf8().get_data(), + proto.utf8().get_data(), + nullptr // Remote host, always nullptr as IGDs don't support it + ); + + ERR_FAIL_COND_V_MSG(i != UPNPCOMMAND_SUCCESS, UPNPMiniUPNP::upnp_result(i), "Couldn't delete port mapping."); + + return UPNP::UPNP_RESULT_SUCCESS; +} + +void UPNPDeviceMiniUPNP::set_description_url(const String &url) { + description_url = url; +} + +String UPNPDeviceMiniUPNP::get_description_url() const { + return description_url; +} + +void UPNPDeviceMiniUPNP::set_service_type(const String &type) { + service_type = type; +} + +String UPNPDeviceMiniUPNP::get_service_type() const { + return service_type; +} + +void UPNPDeviceMiniUPNP::set_igd_control_url(const String &url) { + igd_control_url = url; +} + +String UPNPDeviceMiniUPNP::get_igd_control_url() const { + return igd_control_url; +} + +void UPNPDeviceMiniUPNP::set_igd_service_type(const String &type) { + igd_service_type = type; +} + +String UPNPDeviceMiniUPNP::get_igd_service_type() const { + return igd_service_type; +} + +void UPNPDeviceMiniUPNP::set_igd_our_addr(const String &addr) { + igd_our_addr = addr; +} + +String UPNPDeviceMiniUPNP::get_igd_our_addr() const { + return igd_our_addr; +} + +void UPNPDeviceMiniUPNP::set_igd_status(IGDStatus status) { + igd_status = status; +} + +UPNPDeviceMiniUPNP::IGDStatus UPNPDeviceMiniUPNP::get_igd_status() const { + return igd_status; +} + +bool UPNPDeviceMiniUPNP::is_valid_gateway() const { + return igd_status == IGD_STATUS_OK; +} + +#endif // WEB_ENABLED diff --git a/godot/modules/text_server_adv/thorvg_bounds_iterator.cpp b/godot/modules/upnp/upnp_device_miniupnp.h similarity index 55% rename from godot/modules/text_server_adv/thorvg_bounds_iterator.cpp rename to godot/modules/upnp/upnp_device_miniupnp.h index d273eef9..bea3b1d5 100644 --- a/godot/modules/text_server_adv/thorvg_bounds_iterator.cpp +++ b/godot/modules/upnp/upnp_device_miniupnp.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* thorvg_bounds_iterator.cpp */ +/* upnp_device_miniupnp.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,43 +28,56 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifdef GDEXTENSION -// Headers for building as GDExtension plug-in. +#ifndef UPNP_DEVICE_MINIUPNP_H +#define UPNP_DEVICE_MINIUPNP_H -#include +#ifndef WEB_ENABLED -using namespace godot; +#include "upnp_device.h" -#elif defined(GODOT_MODULE) -// Headers for building as built-in module. +class UPNPDeviceMiniUPNP : public UPNPDevice { + GDCLASS(UPNPDeviceMiniUPNP, UPNPDevice); -#include "core/typedefs.h" +private: + static UPNPDevice *_create(bool p_notify_postinitialize) { return static_cast(ClassDB::creator(p_notify_postinitialize)); } -#include "modules/modules_enabled.gen.h" // For svg. -#endif + String description_url; + String service_type; + String igd_control_url; + String igd_service_type; + String igd_our_addr; + IGDStatus igd_status = IGD_STATUS_UNKNOWN_ERROR; -#ifdef MODULE_SVG_ENABLED +public: + static void make_default(); -#include "thorvg_bounds_iterator.h" + virtual void set_description_url(const String &url) override; + virtual String get_description_url() const override; -#include -#include + virtual void set_service_type(const String &type) override; + virtual String get_service_type() const override; -// This function uses private ThorVG API to get bounding box of top level children elements. + virtual void set_igd_control_url(const String &url) override; + virtual String get_igd_control_url() const override; -void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y) { - tvg::IteratorAccessor itrAccessor; - if (tvg::Iterator *it = itrAccessor.iterator(p_picture)) { - while (const tvg::Paint *child = it->next()) { - float x = 0, y = 0, w = 0, h = 0; - child->bounds(&x, &y, &w, &h, true); - r_min_x = MIN(x, r_min_x); - r_min_y = MIN(y, r_min_y); - r_max_x = MAX(x + w, r_max_x); - r_max_y = MAX(y + h, r_max_y); - } - delete (it); - } -} + virtual void set_igd_service_type(const String &type) override; + virtual String get_igd_service_type() const override; -#endif // MODULE_SVG_ENABLED + virtual void set_igd_our_addr(const String &addr) override; + virtual String get_igd_our_addr() const override; + + virtual void set_igd_status(IGDStatus status) override; + virtual IGDStatus get_igd_status() const override; + + virtual bool is_valid_gateway() const override; + virtual String query_external_address() const override; + virtual int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const override; + virtual int delete_port_mapping(int port, String proto = "UDP") const override; + + UPNPDeviceMiniUPNP() {} + virtual ~UPNPDeviceMiniUPNP() {} +}; + +#endif // WEB_ENABLED + +#endif // UPNP_DEVICE_MINIUPNP_H diff --git a/godot/modules/upnp/upnp_miniupnp.cpp b/godot/modules/upnp/upnp_miniupnp.cpp new file mode 100644 index 00000000..0714d56a --- /dev/null +++ b/godot/modules/upnp/upnp_miniupnp.cpp @@ -0,0 +1,334 @@ +/**************************************************************************/ +/* upnp_miniupnp.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 WEB_ENABLED + +#include "upnp_miniupnp.h" + +#include "upnp_device_miniupnp.h" + +#include +#include + +#include + +void UPNPMiniUPNP::make_default() { + UPNP::_create = UPNPMiniUPNP::_create; +} + +bool UPNPMiniUPNP::is_common_device(const String &dev) const { + return dev.is_empty() || + dev.contains("InternetGatewayDevice") || + dev.contains("WANIPConnection") || + dev.contains("WANPPPConnection") || + dev.contains("rootdevice"); +} + +int UPNPMiniUPNP::discover(int timeout, int ttl, const String &device_filter) { + ERR_FAIL_COND_V_MSG(timeout < 0, UPNP_RESULT_INVALID_PARAM, "The response's wait time can't be negative."); + ERR_FAIL_COND_V_MSG(ttl < 0 || ttl > 255, UPNP_RESULT_INVALID_PARAM, "The time-to-live must be set between 0 and 255 (inclusive)."); + + devices.clear(); + + int error = 0; + struct UPNPDev *devlist; + + CharString cs = discover_multicast_if.utf8(); + const char *m_if = cs.length() ? cs.get_data() : nullptr; + if (is_common_device(device_filter)) { + devlist = upnpDiscover(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error); + } else { + devlist = upnpDiscoverAll(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error); + } + + if (error != UPNPDISCOVER_SUCCESS) { + switch (error) { + case UPNPDISCOVER_SOCKET_ERROR: + return UPNP_RESULT_SOCKET_ERROR; + case UPNPDISCOVER_MEMORY_ERROR: + return UPNP_RESULT_MEM_ALLOC_ERROR; + default: + return UPNP_RESULT_UNKNOWN_ERROR; + } + } + + if (!devlist) { + return UPNP_RESULT_NO_DEVICES; + } + + struct UPNPDev *dev = devlist; + + while (dev) { + if (device_filter.is_empty() || strstr(dev->st, device_filter.utf8().get_data())) { + add_device_to_list(dev, devlist); + } + + dev = dev->pNext; + } + + freeUPNPDevlist(devlist); + + return UPNP_RESULT_SUCCESS; +} + +void UPNPMiniUPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) { + Ref new_device; + new_device.instantiate(); + + new_device->set_description_url(dev->descURL); + new_device->set_service_type(dev->st); + + parse_igd(new_device, devlist); + + devices.push_back(new_device); +} + +char *UPNPMiniUPNP::load_description(const String &url, int *size, int *status_code) const { + return (char *)miniwget(url.utf8().get_data(), size, 0, status_code); +} + +void UPNPMiniUPNP::parse_igd(Ref dev, UPNPDev *devlist) { + int size = 0; + int status_code = -1; + char *xml = load_description(dev->get_description_url(), &size, &status_code); + + if (status_code != 200) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_ERROR); + return; + } + + if (!xml || size < 1) { + dev->set_igd_status(UPNPDevice::IGD_STATUS_HTTP_EMPTY); + return; + } + + struct UPNPUrls urls = {}; + struct IGDdatas data; + + parserootdesc(xml, size, &data); + free(xml); + xml = nullptr; + + GetUPNPUrls(&urls, &data, dev->get_description_url().utf8().get_data(), 0); + + char addr[16]; +#if MINIUPNPC_API_VERSION >= 18 + int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16, nullptr, 0); +#else + int i = UPNP_GetValidIGD(devlist, &urls, &data, (char *)&addr, 16); +#endif + + if (i != 1) { + FreeUPNPUrls(&urls); + + switch (i) { + case 0: + dev->set_igd_status(UPNPDevice::IGD_STATUS_NO_IGD); + return; + case 2: + dev->set_igd_status(UPNPDevice::IGD_STATUS_DISCONNECTED); + return; + case 3: + dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_DEVICE); + return; + default: + dev->set_igd_status(UPNPDevice::IGD_STATUS_UNKNOWN_ERROR); + return; + } + } + + if (urls.controlURL[0] == '\0') { + FreeUPNPUrls(&urls); + dev->set_igd_status(UPNPDevice::IGD_STATUS_INVALID_CONTROL); + return; + } + + dev->set_igd_control_url(urls.controlURL); + dev->set_igd_service_type(data.first.servicetype); + dev->set_igd_our_addr(addr); + dev->set_igd_status(UPNPDevice::IGD_STATUS_OK); + + FreeUPNPUrls(&urls); +} + +int UPNPMiniUPNP::upnp_result(int in) { + switch (in) { + case UPNPCOMMAND_SUCCESS: + return UPNP_RESULT_SUCCESS; + case UPNPCOMMAND_UNKNOWN_ERROR: + return UPNP_RESULT_UNKNOWN_ERROR; + case UPNPCOMMAND_INVALID_ARGS: + return UPNP_RESULT_INVALID_ARGS; + case UPNPCOMMAND_HTTP_ERROR: + return UPNP_RESULT_HTTP_ERROR; + case UPNPCOMMAND_INVALID_RESPONSE: + return UPNP_RESULT_INVALID_RESPONSE; + case UPNPCOMMAND_MEM_ALLOC_ERROR: + return UPNP_RESULT_MEM_ALLOC_ERROR; + + case 402: + return UPNP_RESULT_INVALID_ARGS; + case 403: + return UPNP_RESULT_NOT_AUTHORIZED; + case 501: + return UPNP_RESULT_ACTION_FAILED; + case 606: + return UPNP_RESULT_NOT_AUTHORIZED; + case 714: + return UPNP_RESULT_NO_SUCH_ENTRY_IN_ARRAY; + case 715: + return UPNP_RESULT_SRC_IP_WILDCARD_NOT_PERMITTED; + case 716: + return UPNP_RESULT_EXT_PORT_WILDCARD_NOT_PERMITTED; + case 718: + return UPNP_RESULT_CONFLICT_WITH_OTHER_MAPPING; + case 724: + return UPNP_RESULT_SAME_PORT_VALUES_REQUIRED; + case 725: + return UPNP_RESULT_ONLY_PERMANENT_LEASE_SUPPORTED; + case 726: + return UPNP_RESULT_REMOTE_HOST_MUST_BE_WILDCARD; + case 727: + return UPNP_RESULT_EXT_PORT_MUST_BE_WILDCARD; + case 728: + return UPNP_RESULT_NO_PORT_MAPS_AVAILABLE; + case 729: + return UPNP_RESULT_CONFLICT_WITH_OTHER_MECHANISM; + case 732: + return UPNP_RESULT_INT_PORT_WILDCARD_NOT_PERMITTED; + case 733: + return UPNP_RESULT_INCONSISTENT_PARAMETERS; + } + + return UPNP_RESULT_UNKNOWN_ERROR; +} + +int UPNPMiniUPNP::get_device_count() const { + return devices.size(); +} + +Ref UPNPMiniUPNP::get_device(int index) const { + ERR_FAIL_INDEX_V(index, devices.size(), nullptr); + + return devices.get(index); +} + +void UPNPMiniUPNP::add_device(Ref device) { + ERR_FAIL_COND(device.is_null()); + + devices.push_back(device); +} + +void UPNPMiniUPNP::set_device(int index, Ref device) { + ERR_FAIL_INDEX(index, devices.size()); + ERR_FAIL_COND(device.is_null()); + + devices.set(index, device); +} + +void UPNPMiniUPNP::remove_device(int index) { + ERR_FAIL_INDEX(index, devices.size()); + + devices.remove_at(index); +} + +void UPNPMiniUPNP::clear_devices() { + devices.clear(); +} + +Ref UPNPMiniUPNP::get_gateway() const { + ERR_FAIL_COND_V_MSG(devices.is_empty(), nullptr, "Couldn't find any UPNPDevices."); + + for (int i = 0; i < devices.size(); i++) { + Ref dev = get_device(i); + + if (dev.is_valid() && dev->is_valid_gateway()) { + return dev; + } + } + + return nullptr; +} + +void UPNPMiniUPNP::set_discover_multicast_if(const String &m_if) { + discover_multicast_if = m_if; +} + +String UPNPMiniUPNP::get_discover_multicast_if() const { + return discover_multicast_if; +} + +void UPNPMiniUPNP::set_discover_local_port(int port) { + discover_local_port = port; +} + +int UPNPMiniUPNP::get_discover_local_port() const { + return discover_local_port; +} + +void UPNPMiniUPNP::set_discover_ipv6(bool ipv6) { + discover_ipv6 = ipv6; +} + +bool UPNPMiniUPNP::is_discover_ipv6() const { + return discover_ipv6; +} + +String UPNPMiniUPNP::query_external_address() const { + Ref dev = get_gateway(); + + if (dev.is_null()) { + return ""; + } + + return dev->query_external_address(); +} + +int UPNPMiniUPNP::add_port_mapping(int port, int port_internal, String desc, String proto, int duration) const { + Ref dev = get_gateway(); + + if (dev.is_null()) { + return UPNP_RESULT_NO_GATEWAY; + } + + return dev->add_port_mapping(port, port_internal, desc, proto, duration); +} + +int UPNPMiniUPNP::delete_port_mapping(int port, String proto) const { + Ref dev = get_gateway(); + + if (dev.is_null()) { + return UPNP_RESULT_NO_GATEWAY; + } + + return dev->delete_port_mapping(port, proto); +} + +#endif // WEB_ENABLED diff --git a/godot/modules/text_server_adv/thorvg_bounds_iterator.h b/godot/modules/upnp/upnp_miniupnp.h similarity index 51% rename from godot/modules/text_server_adv/thorvg_bounds_iterator.h rename to godot/modules/upnp/upnp_miniupnp.h index afa2c137..0c7dba9d 100644 --- a/godot/modules/text_server_adv/thorvg_bounds_iterator.h +++ b/godot/modules/upnp/upnp_miniupnp.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* thorvg_bounds_iterator.h */ +/* upnp_miniupnp.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,31 +28,66 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef THORVG_BOUNDS_ITERATOR_H -#define THORVG_BOUNDS_ITERATOR_H +#ifndef UPNP_MINIUPNP_H +#define UPNP_MINIUPNP_H -#ifdef GDEXTENSION -// Headers for building as GDExtension plug-in. +#ifndef WEB_ENABLED -#include -#include +#include "upnp.h" -using namespace godot; +#include -#elif defined(GODOT_MODULE) -// Headers for building as built-in module. +class UPNPMiniUPNP : public UPNP { + GDCLASS(UPNPMiniUPNP, UPNP); -#include "core/typedefs.h" +private: + static UPNP *_create(bool p_notify_postinitialize) { return static_cast(ClassDB::creator(p_notify_postinitialize)); } -#include "modules/modules_enabled.gen.h" // For svg. -#endif + String discover_multicast_if = ""; + int discover_local_port = 0; + bool discover_ipv6 = false; -#ifdef MODULE_SVG_ENABLED + Vector> devices; -#include + bool is_common_device(const String &dev) const; + void add_device_to_list(UPNPDev *dev, UPNPDev *devlist); + void parse_igd(Ref dev, UPNPDev *devlist); + char *load_description(const String &url, int *size, int *status_code) const; -void tvg_get_bounds(tvg::Picture *p_picture, float &r_min_x, float &r_min_y, float &r_max_x, float &r_max_y); +public: + static void make_default(); -#endif // MODULE_SVG_ENABLED + static int upnp_result(int in); -#endif // THORVG_BOUNDS_ITERATOR_H + virtual int get_device_count() const override; + virtual Ref get_device(int index) const override; + virtual void add_device(Ref device) override; + virtual void set_device(int index, Ref device) override; + virtual void remove_device(int index) override; + virtual void clear_devices() override; + + virtual Ref get_gateway() const override; + + virtual int discover(int timeout = 2000, int ttl = 2, const String &device_filter = "InternetGatewayDevice") override; + + virtual String query_external_address() const override; + + virtual int add_port_mapping(int port, int port_internal = 0, String desc = "", String proto = "UDP", int duration = 0) const override; + virtual int delete_port_mapping(int port, String proto = "UDP") const override; + + virtual void set_discover_multicast_if(const String &m_if) override; + virtual String get_discover_multicast_if() const override; + + virtual void set_discover_local_port(int port) override; + virtual int get_discover_local_port() const override; + + virtual void set_discover_ipv6(bool ipv6) override; + virtual bool is_discover_ipv6() const override; + + UPNPMiniUPNP() {} + virtual ~UPNPMiniUPNP() {} +}; + +#endif // WEB_ENABLED + +#endif // UPNP_MINIUPNP_H diff --git a/godot/platform/android/export/export_plugin.cpp b/godot/platform/android/export/export_plugin.cpp index e20de99c..a7b08790 100644 --- a/godot/platform/android/export/export_plugin.cpp +++ b/godot/platform/android/export/export_plugin.cpp @@ -2529,7 +2529,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref da = DirAccess::open(java_sdk_path.path_join("bin"), &errn); if (errn != OK) { - err += TTR("Invalid Java SDK path in Editor Settings."); + err += TTR("Invalid Java SDK path in Editor Settings.") + " "; err += TTR("Missing 'bin' directory!"); err += "\n"; valid = false; @@ -2537,7 +2537,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref da = DirAccess::open(sdk_path.path_join("platform-tools"), &errn); if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Invalid Android SDK path in Editor Settings.") + " "; err += TTR("Missing 'platform-tools' directory!"); err += "\n"; valid = false; @@ -2563,7 +2563,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref build_tools_da = DirAccess::open(sdk_path.path_join("build-tools"), &errn); if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Invalid Android SDK path in Editor Settings.") + " "; err += TTR("Missing 'build-tools' directory!"); err += "\n"; valid = false; @@ -2585,7 +2585,7 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref): Int { - return primaryHost?.onNewGodotInstanceRequested(args) ?: 0 + return primaryHost?.onNewGodotInstanceRequested(args) ?: -1 } @Keep diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index d60595c0..7cfe3ef3 100644 --- a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -474,7 +474,7 @@ public int onNewGodotInstanceRequested(String[] args) { if (parentHost != null) { return parentHost.onNewGodotInstanceRequested(args); } - return 0; + return -1; } @Override diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 344b73f7..60d1f01b 100644 --- a/godot/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -92,7 +92,7 @@ default void onGodotRestartRequested(Godot instance) {} * @return the id of the new instance. See {@code onGodotForceQuit} */ default int onNewGodotInstanceRequested(String[] args) { - return 0; + return -1; } /** diff --git a/godot/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt b/godot/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt index 2befe058..19fb4528 100644 --- a/godot/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt +++ b/godot/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt @@ -37,6 +37,7 @@ import android.net.Uri import android.os.Build import android.provider.DocumentsContract import android.util.Log +import android.webkit.MimeTypeMap import androidx.annotation.RequiresApi import org.godotengine.godot.GodotLib import org.godotengine.godot.io.file.MediaStoreData @@ -145,10 +146,11 @@ internal class FilePicker { if (fileMode != FILE_MODE_OPEN_DIR) { intent.type = "*/*" if (filters.isNotEmpty()) { - if (filters.size == 1) { - intent.type = filters[0] + val resolvedFilters = filters.map { resolveMimeType(it) }.distinct() + if (resolvedFilters.size == 1) { + intent.type = resolvedFilters[0] } else { - intent.putExtra(Intent.EXTRA_MIME_TYPES, filters) + intent.putExtra(Intent.EXTRA_MIME_TYPES, resolvedFilters.toTypedArray()) } } intent.addCategory(Intent.CATEGORY_OPENABLE) @@ -156,5 +158,43 @@ internal class FilePicker { intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true) activity?.startActivityForResult(intent, FILE_PICKER_REQUEST) } + + /** + * Retrieves the MIME type for a given file extension. + * + * @param ext the extension whose MIME type is to be determined. + * @return the MIME type as a string, or "application/octet-stream" if the type is unknown. + */ + private fun resolveMimeType(ext: String): String { + val mimeTypeMap = MimeTypeMap.getSingleton() + var input = ext + + // Fix for extensions like "*.txt" or ".txt". + if (ext.contains(".")) { + input = ext.substring(ext.indexOf(".") + 1); + } + + // Check if the input is already a valid MIME type. + if (mimeTypeMap.hasMimeType(input)) { + return input + } + + val resolvedMimeType = mimeTypeMap.getMimeTypeFromExtension(input) + if (resolvedMimeType != null) { + return resolvedMimeType + } + // Check for wildcard MIME types like "image/*". + if (input.contains("/*")) { + val category = input.substringBefore("/*") + return when (category) { + "image" -> "image/*" + "video" -> "video/*" + "audio" -> "audio/*" + else -> "application/octet-stream" + } + } + // Fallback to a generic MIME type if the input is neither a valid extension nor MIME type. + return "application/octet-stream" + } } } diff --git a/godot/platform/android/os_android.cpp b/godot/platform/android/os_android.cpp index 7b0d3a29..06f5df48 100644 --- a/godot/platform/android/os_android.cpp +++ b/godot/platform/android/os_android.cpp @@ -863,6 +863,9 @@ Error OS_Android::create_process(const String &p_path, const List &p_arg Error OS_Android::create_instance(const List &p_arguments, ProcessID *r_child_id) { int instance_id = godot_java->create_new_godot_instance(p_arguments); + if (instance_id == -1) { + return FAILED; + } if (r_child_id) { *r_child_id = instance_id; } diff --git a/godot/platform/web/emscripten_helpers.py b/godot/platform/web/emscripten_helpers.py index aca5d4ec..6a3855da 100644 --- a/godot/platform/web/emscripten_helpers.py +++ b/godot/platform/web/emscripten_helpers.py @@ -71,6 +71,7 @@ def create_template_zip(env, js, wasm, side): "___GODOT_OPT_CACHE___": json.dumps(opt_cache), "___GODOT_OFFLINE_PAGE___": "offline.html", "___GODOT_THREADS_ENABLED___": "true" if env["threads"] else "false", + "___GODOT_ENSURE_CROSSORIGIN_ISOLATION_HEADERS___": "true", } html = env.Substfile(target="#bin/godot${PROGSUFFIX}.html", source=html, SUBST_DICT=subst_dict) in_files.append(html) diff --git a/godot/platform/web/js/engine/engine.js b/godot/platform/web/js/engine/engine.js index 04c4c44c..1aeeb62f 100644 --- a/godot/platform/web/js/engine/engine.js +++ b/godot/platform/web/js/engine/engine.js @@ -241,7 +241,11 @@ const Engine = (function () { */ installServiceWorker: function () { if (this.config.serviceWorker && 'serviceWorker' in navigator) { - return navigator.serviceWorker.register(this.config.serviceWorker); + try { + return navigator.serviceWorker.register(this.config.serviceWorker); + } catch (e) { + return Promise.reject(e); + } } return Promise.resolve(); }, diff --git a/godot/platform/web/js/libs/library_godot_os.js b/godot/platform/web/js/libs/library_godot_os.js index 56821227..2899d7e4 100644 --- a/godot/platform/web/js/libs/library_godot_os.js +++ b/godot/platform/web/js/libs/library_godot_os.js @@ -441,8 +441,12 @@ const GodotPWA = { godot_js_pwa_cb__sig: 'vi', godot_js_pwa_cb: function (p_update_cb) { if ('serviceWorker' in navigator) { - const cb = GodotRuntime.get_func(p_update_cb); - navigator.serviceWorker.getRegistration().then(GodotPWA.updateState.bind(null, cb)); + try { + const cb = GodotRuntime.get_func(p_update_cb); + navigator.serviceWorker.getRegistration().then(GodotPWA.updateState.bind(null, cb)); + } catch (e) { + GodotRuntime.error('Failed to assign PWA callback', e); + } } }, @@ -450,12 +454,17 @@ const GodotPWA = { godot_js_pwa_update__sig: 'i', godot_js_pwa_update: function () { if ('serviceWorker' in navigator && GodotPWA.hasUpdate) { - navigator.serviceWorker.getRegistration().then(function (reg) { - if (!reg || !reg.waiting) { - return; - } - reg.waiting.postMessage('update'); - }); + try { + navigator.serviceWorker.getRegistration().then(function (reg) { + if (!reg || !reg.waiting) { + return; + } + reg.waiting.postMessage('update'); + }); + } catch (e) { + GodotRuntime.error(e); + return 1; + } return 0; } return 1; diff --git a/godot/platform/windows/display_server_windows.cpp b/godot/platform/windows/display_server_windows.cpp index e300bd1c..ec3fcea6 100644 --- a/godot/platform/windows/display_server_windows.cpp +++ b/godot/platform/windows/display_server_windows.cpp @@ -2167,6 +2167,10 @@ void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_initiali r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; r_style_ex |= WS_EX_ACCEPTFILES; + + if (OS::get_singleton()->get_current_rendering_driver_name() == "d3d12") { + r_style_ex |= WS_EX_NOREDIRECTIONBITMAP; + } } void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint) { diff --git a/godot/platform/windows/display_server_windows.h b/godot/platform/windows/display_server_windows.h index 32ed8823..88602681 100644 --- a/godot/platform/windows/display_server_windows.h +++ b/godot/platform/windows/display_server_windows.h @@ -356,6 +356,10 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS { SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2, } SHC_PROCESS_DPI_AWARENESS; +#ifndef WS_EX_NOREDIRECTIONBITMAP +#define WS_EX_NOREDIRECTIONBITMAP 0x00200000L +#endif + class DropTargetWindows; class DisplayServerWindows : public DisplayServer { diff --git a/godot/platform/windows/os_windows.cpp b/godot/platform/windows/os_windows.cpp index a25b7ea4..2b5c77d4 100644 --- a/godot/platform/windows/os_windows.cpp +++ b/godot/platform/windows/os_windows.cpp @@ -817,22 +817,10 @@ double OS_Windows::get_unix_time() const { } void OS_Windows::delay_usec(uint32_t p_usec) const { - constexpr uint32_t tolerance = 1000 + 20; - - uint64_t t0 = get_ticks_usec(); - uint64_t target_time = t0 + p_usec; - - // Calculate sleep duration with a tolerance for fine-tuning. - if (p_usec > tolerance) { - uint32_t coarse_sleep_usec = p_usec - tolerance; - if (coarse_sleep_usec >= 1000) { - Sleep(coarse_sleep_usec / 1000); - } - } - - // Spin-wait until we reach the precise target time. - while (get_ticks_usec() < target_time) { - YieldProcessor(); + if (p_usec < 1000) { + Sleep(1); + } else { + Sleep(p_usec / 1000); } } @@ -1540,7 +1528,8 @@ DWRITE_FONT_STRETCH OS_Windows::_stretch_to_dw(int p_stretch) const { } Vector OS_Windows::get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale, const String &p_script, int p_weight, int p_stretch, bool p_italic) const { - if (!dwrite2_init) { + // This may be called before TextServerManager has been created, which would cause a crash downstream if we do not check here + if (!dwrite2_init || !TextServerManager::get_singleton()) { return Vector(); } @@ -1751,7 +1740,7 @@ String OS_Windows::get_stdin_string(int64_t p_buffer_size) { data.resize(p_buffer_size); DWORD count = 0; if (ReadFile(GetStdHandle(STD_INPUT_HANDLE), data.ptrw(), data.size(), &count, nullptr)) { - return String::utf8((const char *)data.ptr(), count); + return String::utf8((const char *)data.ptr(), count).replace("\r\n", "\n").rstrip("\n"); } return String(); diff --git a/godot/scene/2d/mesh_instance_2d.cpp b/godot/scene/2d/mesh_instance_2d.cpp index 9b7a89fe..f6a8e0de 100644 --- a/godot/scene/2d/mesh_instance_2d.cpp +++ b/godot/scene/2d/mesh_instance_2d.cpp @@ -65,6 +65,9 @@ void MeshInstance2D::set_mesh(const Ref &p_mesh) { mesh = p_mesh; if (mesh.is_valid()) { + // If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback + // so do this before connecting to the change signal. + mesh->get_rid(); mesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } diff --git a/godot/scene/3d/camera_3d.cpp b/godot/scene/3d/camera_3d.cpp index 2b841c4e..c520a42e 100644 --- a/godot/scene/3d/camera_3d.cpp +++ b/godot/scene/3d/camera_3d.cpp @@ -529,9 +529,12 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm = _get_camera_projection(p_z_depth); + Projection cm = _get_camera_projection(_near); - Vector2 vp_he = cm.get_viewport_half_extents(); + Plane z_slice(Vector3(0, 0, 1), -p_z_depth); + Vector3 res; + z_slice.intersect_3(cm.get_projection_plane(Projection::Planes::PLANE_RIGHT), cm.get_projection_plane(Projection::Planes::PLANE_TOP), &res); + Vector2 vp_he(res.x, res.y); Vector2 point; point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0; diff --git a/godot/scene/3d/look_at_modifier_3d.cpp b/godot/scene/3d/look_at_modifier_3d.cpp index ad33cd42..04dae61d 100644 --- a/godot/scene/3d/look_at_modifier_3d.cpp +++ b/godot/scene/3d/look_at_modifier_3d.cpp @@ -33,7 +33,7 @@ void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const { SkeletonModifier3D::_validate_property(p_property); - if (p_property.name == "bone" || p_property.name == "origin_bone") { + if (p_property.name == "bone_name" || p_property.name == "origin_bone_name") { Skeleton3D *skeleton = get_skeleton(); if (skeleton) { p_property.hint = PROPERTY_HINT_ENUM; @@ -49,11 +49,11 @@ void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NONE; } } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) { - if (p_property.name == "origin_bone") { + if (p_property.name == "origin_bone" || p_property.name == "origin_bone_name") { p_property.usage = PROPERTY_USAGE_NONE; } } else { - if (p_property.name == "origin_external_node" || p_property.name == "origin_bone") { + if (p_property.name == "origin_external_node" || p_property.name == "origin_bone" || p_property.name == "origin_bone_name") { p_property.usage = PROPERTY_USAGE_NONE; } } @@ -75,8 +75,29 @@ PackedStringArray LookAtModifier3D::get_configuration_warnings() const { return warnings; } +void LookAtModifier3D::set_bone_name(const String &p_bone_name) { + bone_name = p_bone_name; + Skeleton3D *sk = get_skeleton(); + if (sk) { + set_bone(sk->find_bone(bone_name)); + } +} + +String LookAtModifier3D::get_bone_name() const { + return bone_name; +} + void LookAtModifier3D::set_bone(int p_bone) { bone = p_bone; + Skeleton3D *sk = get_skeleton(); + if (sk) { + if (bone <= -1 || bone >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range!"); + bone = -1; + } else { + bone_name = sk->get_bone_name(bone); + } + } } int LookAtModifier3D::get_bone() const { @@ -132,8 +153,29 @@ LookAtModifier3D::OriginFrom LookAtModifier3D::get_origin_from() const { return origin_from; } +void LookAtModifier3D::set_origin_bone_name(const String &p_bone_name) { + origin_bone_name = p_bone_name; + Skeleton3D *sk = get_skeleton(); + if (sk) { + set_origin_bone(sk->find_bone(origin_bone_name)); + } +} + +String LookAtModifier3D::get_origin_bone_name() const { + return origin_bone_name; +} + void LookAtModifier3D::set_origin_bone(int p_bone) { origin_bone = p_bone; + Skeleton3D *sk = get_skeleton(); + if (sk) { + if (origin_bone <= -1 || origin_bone >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range!"); + origin_bone = -1; + } else { + origin_bone_name = sk->get_bone_name(origin_bone); + } + } } int LookAtModifier3D::get_origin_bone() const { @@ -330,6 +372,8 @@ void LookAtModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_target_node", "target_node"), &LookAtModifier3D::set_target_node); ClassDB::bind_method(D_METHOD("get_target_node"), &LookAtModifier3D::get_target_node); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &LookAtModifier3D::set_bone_name); + ClassDB::bind_method(D_METHOD("get_bone_name"), &LookAtModifier3D::get_bone_name); ClassDB::bind_method(D_METHOD("set_bone", "bone"), &LookAtModifier3D::set_bone); ClassDB::bind_method(D_METHOD("get_bone"), &LookAtModifier3D::get_bone); ClassDB::bind_method(D_METHOD("set_forward_axis", "forward_axis"), &LookAtModifier3D::set_forward_axis); @@ -343,6 +387,8 @@ void LookAtModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_origin_from", "origin_from"), &LookAtModifier3D::set_origin_from); ClassDB::bind_method(D_METHOD("get_origin_from"), &LookAtModifier3D::get_origin_from); + ClassDB::bind_method(D_METHOD("set_origin_bone_name", "bone_name"), &LookAtModifier3D::set_origin_bone_name); + ClassDB::bind_method(D_METHOD("get_origin_bone_name"), &LookAtModifier3D::get_origin_bone_name); ClassDB::bind_method(D_METHOD("set_origin_bone", "bone"), &LookAtModifier3D::set_origin_bone); ClassDB::bind_method(D_METHOD("get_origin_bone"), &LookAtModifier3D::get_origin_bone); ClassDB::bind_method(D_METHOD("set_origin_external_node", "external_node"), &LookAtModifier3D::set_origin_external_node); @@ -397,14 +443,16 @@ void LookAtModifier3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_target_node", "get_target_node"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_ENUM, ""), "set_bone", "get_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_bone_name", "get_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_bone", "get_bone"); ADD_PROPERTY(PropertyInfo(Variant::INT, "forward_axis", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z"), "set_forward_axis", "get_forward_axis"); ADD_PROPERTY(PropertyInfo(Variant::INT, "primary_rotation_axis", PROPERTY_HINT_ENUM, "X,Y,Z"), "set_primary_rotation_axis", "get_primary_rotation_axis"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_secondary_rotation"), "set_use_secondary_rotation", "is_using_secondary_rotation"); ADD_GROUP("Origin Settings", "origin_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_from", PROPERTY_HINT_ENUM, "Self,SpecificBone,ExternalNode"), "set_origin_from", "get_origin_from"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_ENUM, ""), "set_origin_bone", "get_origin_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "origin_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_origin_bone_name", "get_origin_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_origin_bone", "get_origin_bone"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "origin_external_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_origin_external_node", "get_origin_external_node"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "origin_safe_margin", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_origin_safe_margin", "get_origin_safe_margin"); @@ -474,7 +522,7 @@ void LookAtModifier3D::_process_modification() { destination = skeleton->get_bone_pose_rotation(bone); } else { Transform3D origin_tr; - if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone < skeleton->get_bone_count()) { + if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone >= 0 && origin_bone < skeleton->get_bone_count()) { origin_tr = skeleton->get_global_transform() * skeleton->get_bone_global_pose(origin_bone); } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) { Node3D *origin_src = Object::cast_to(get_node_or_null(origin_external_node)); @@ -486,7 +534,7 @@ void LookAtModifier3D::_process_modification() { } else { origin_tr = bone_rest_space; } - forward_vector = bone_rest_space.basis.xform_inv((target->get_global_position() - origin_tr.translated_local(origin_offset).origin)); + forward_vector = bone_rest_space.orthonormalized().basis.xform_inv((target->get_global_position() - origin_tr.translated_local(origin_offset).origin)); forward_vector_nrm = forward_vector.normalized(); if (forward_vector_nrm.abs().is_equal_approx(get_vector_from_axis(primary_rotation_axis))) { destination = skeleton->get_bone_pose_rotation(bone); @@ -497,6 +545,8 @@ void LookAtModifier3D::_process_modification() { } // Detect flipping. + bool is_not_max_influence = influence < 1.0; + bool is_flippable = use_angle_limitation || is_not_max_influence; Vector3::Axis current_forward_axis = get_axis_from_bone_axis(forward_axis); if (is_intersecting_axis(prev_forward_vector, forward_vector, current_forward_axis, secondary_rotation_axis) || is_intersecting_axis(prev_forward_vector, forward_vector, primary_rotation_axis, primary_rotation_axis, true) || @@ -504,16 +554,20 @@ void LookAtModifier3D::_process_modification() { (prev_forward_vector != Vector3(0, 0, 0) && forward_vector == Vector3(0, 0, 0)) || (prev_forward_vector == Vector3(0, 0, 0) && forward_vector != Vector3(0, 0, 0))) { init_transition(); - } else if (use_angle_limitation && signbit(prev_forward_vector[secondary_rotation_axis]) != signbit(forward_vector[secondary_rotation_axis])) { + } else if (is_flippable && signbit(prev_forward_vector[secondary_rotation_axis]) != signbit(forward_vector[secondary_rotation_axis])) { // Flipping by angle_limitation can be detected by sign of secondary rotation axes during forward_vector is rotated more than 90 degree from forward_axis (means dot production is negative). Vector3 prev_forward_vector_nrm = forward_vector.normalized(); Vector3 rest_forward_vector = get_vector_from_bone_axis(forward_axis); if (symmetry_limitation) { - if (!Math::is_equal_approx(primary_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) { + if ((is_not_max_influence || !Math::is_equal_approx(primary_limit_angle, (float)Math_TAU)) && + prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && + forward_vector_nrm.dot(rest_forward_vector) < 0) { init_transition(); } } else { - if (!Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) { + if ((is_not_max_influence || !Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math_TAU)) && + prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && + forward_vector_nrm.dot(rest_forward_vector) < 0) { init_transition(); } } @@ -528,7 +582,7 @@ void LookAtModifier3D::_process_modification() { delta = get_physics_process_delta_time(); } remaining = MAX(0, remaining - time_step * delta); - if (use_angle_limitation) { + if (is_flippable) { // Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body. Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion(); float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0); diff --git a/godot/scene/3d/look_at_modifier_3d.h b/godot/scene/3d/look_at_modifier_3d.h index 5f3c4e8b..9329edf3 100644 --- a/godot/scene/3d/look_at_modifier_3d.h +++ b/godot/scene/3d/look_at_modifier_3d.h @@ -54,7 +54,8 @@ class LookAtModifier3D : public SkeletonModifier3D { }; private: - int bone = 0; + String bone_name; + int bone = -1; Vector3 forward_vector; Vector3 forward_vector_nrm; @@ -64,6 +65,7 @@ class LookAtModifier3D : public SkeletonModifier3D { bool use_secondary_rotation = true; OriginFrom origin_from = ORIGIN_FROM_SELF; + String origin_bone_name; int origin_bone = -1; NodePath origin_external_node; @@ -123,6 +125,8 @@ class LookAtModifier3D : public SkeletonModifier3D { virtual void _process_modification() override; public: + void set_bone_name(const String &p_bone_name); + String get_bone_name() const; void set_bone(int p_bone); int get_bone() const; @@ -135,6 +139,8 @@ class LookAtModifier3D : public SkeletonModifier3D { void set_origin_from(OriginFrom p_origin_from); OriginFrom get_origin_from() const; + void set_origin_bone_name(const String &p_bone_name); + String get_origin_bone_name() const; void set_origin_bone(int p_bone); int get_origin_bone() const; void set_origin_external_node(const NodePath &p_external_node); diff --git a/godot/scene/3d/physics/collision_shape_3d.cpp b/godot/scene/3d/physics/collision_shape_3d.cpp index 304fa74b..362c6102 100644 --- a/godot/scene/3d/physics/collision_shape_3d.cpp +++ b/godot/scene/3d/physics/collision_shape_3d.cpp @@ -81,12 +81,19 @@ void CollisionShape3D::_update_in_shape_owner(bool p_xform_only) { void CollisionShape3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PARENTED: { +#ifdef DEBUG_ENABLED + if (debug_color == get_placeholder_default_color()) { + debug_color = SceneTree::get_singleton()->get_debug_collisions_color(); + } +#endif // DEBUG_ENABLED + collision_object = Object::cast_to(get_parent()); if (collision_object) { owner_id = collision_object->create_shape_owner(this); if (shape.is_valid()) { collision_object->shape_owner_add_shape(owner_id, shape); } + _update_in_shape_owner(); } } break; @@ -166,11 +173,26 @@ void CollisionShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape); ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape3D::is_disabled); + ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings); ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); + +#ifdef DEBUG_ENABLED + ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape3D::set_debug_color); + ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape3D::get_debug_color); + + ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionShape3D::set_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionShape3D::get_debug_fill_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color"); + // Default value depends on a project setting, override for doc generation purposes. + ADD_PROPERTY_DEFAULT("debug_color", get_placeholder_default_color()); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill"); +#endif // DEBUG_ENABLED } void CollisionShape3D::set_shape(const Ref &p_shape) { @@ -178,11 +200,27 @@ void CollisionShape3D::set_shape(const Ref &p_shape) { return; } if (shape.is_valid()) { + shape->disconnect_changed(callable_mp(this, &CollisionShape3D::shape_changed)); shape->disconnect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); } shape = p_shape; if (shape.is_valid()) { +#ifdef DEBUG_ENABLED + if (shape->get_debug_color() != get_placeholder_default_color()) { + set_debug_color(shape->get_debug_color()); + set_debug_fill_enabled(shape->get_debug_fill()); + } else if (get_debug_color() != get_placeholder_default_color()) { + shape->set_debug_color(debug_color); + shape->set_debug_fill(debug_fill); + } else { + set_debug_color(SceneTree::get_singleton()->get_debug_collisions_color()); + shape->set_debug_color(SceneTree::get_singleton()->get_debug_collisions_color()); + shape->set_debug_fill(debug_fill); + } +#endif // DEBUG_ENABLED + shape->connect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); + shape->connect_changed(callable_mp(this, &CollisionShape3D::shape_changed)); } update_gizmos(); if (collision_object) { @@ -215,6 +253,66 @@ bool CollisionShape3D::is_disabled() const { return disabled; } +#ifdef DEBUG_ENABLED +void CollisionShape3D::set_debug_color(const Color &p_color) { + if (p_color == get_placeholder_default_color()) { + debug_color = SceneTree::get_singleton()->get_debug_collisions_color(); + } else if (debug_color != p_color) { + debug_color = p_color; + + if (shape.is_valid()) { + shape->set_debug_color(p_color); + } + } +} + +Color CollisionShape3D::get_debug_color() const { + return debug_color; +} + +void CollisionShape3D::set_debug_fill_enabled(bool p_enable) { + if (debug_fill == p_enable) { + return; + } + + debug_fill = p_enable; + + if (shape.is_valid()) { + shape->set_debug_fill(p_enable); + } +} + +bool CollisionShape3D::get_debug_fill_enabled() const { + return debug_fill; +} + +bool CollisionShape3D::_property_can_revert(const StringName &p_name) const { + if (p_name == "debug_color") { + return true; + } + return false; +} + +bool CollisionShape3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == "debug_color") { + r_property = SceneTree::get_singleton()->get_debug_collisions_color(); + return true; + } + return false; +} +#endif // DEBUG_ENABLED + +void CollisionShape3D::shape_changed() { +#ifdef DEBUG_ENABLED + if (shape->get_debug_color() != debug_color) { + set_debug_color(shape->get_debug_color()); + } + if (shape->get_debug_fill() != debug_fill) { + set_debug_fill_enabled(shape->get_debug_fill()); + } +#endif // DEBUG_ENABLED +} + CollisionShape3D::CollisionShape3D() { //indicator = RenderingServer::get_singleton()->mesh_create(); set_notify_local_transform(true); diff --git a/godot/scene/3d/physics/collision_shape_3d.h b/godot/scene/3d/physics/collision_shape_3d.h index 15f6ef73..0eaecb9f 100644 --- a/godot/scene/3d/physics/collision_shape_3d.h +++ b/godot/scene/3d/physics/collision_shape_3d.h @@ -43,6 +43,13 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; +#ifdef DEBUG_ENABLED + Color debug_color = get_placeholder_default_color(); + bool debug_fill = true; + + static const Color get_placeholder_default_color() { return Color(0.0, 0.0, 0.0, 0.0); } +#endif // DEBUG_ENABLED + #ifndef DISABLE_DEPRECATED void resource_changed(Ref res); #endif @@ -55,6 +62,13 @@ class CollisionShape3D : public Node3D { void _notification(int p_what); static void _bind_methods(); +#ifdef DEBUG_ENABLED + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; +#endif // DEBUG_ENABLED + + void shape_changed(); + public: void make_convex_from_siblings(); @@ -64,6 +78,14 @@ class CollisionShape3D : public Node3D { void set_disabled(bool p_disabled); bool is_disabled() const; +#ifdef DEBUG_ENABLED + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill_enabled(bool p_enable); + bool get_debug_fill_enabled() const; +#endif // DEBUG_ENABLED + PackedStringArray get_configuration_warnings() const override; CollisionShape3D(); diff --git a/godot/scene/3d/voxel_gi.cpp b/godot/scene/3d/voxel_gi.cpp index 80ff176a..5fec7021 100644 --- a/godot/scene/3d/voxel_gi.cpp +++ b/godot/scene/3d/voxel_gi.cpp @@ -389,6 +389,17 @@ VoxelGI::BakeBeginFunc VoxelGI::bake_begin_function = nullptr; VoxelGI::BakeStepFunc VoxelGI::bake_step_function = nullptr; VoxelGI::BakeEndFunc VoxelGI::bake_end_function = nullptr; +static int voxelizer_plot_bake_base = 0; +static int voxelizer_plot_bake_total = 0; + +static bool voxelizer_plot_bake_step_function(int current, int) { + return VoxelGI::bake_step_function((voxelizer_plot_bake_base + current) * 500 / voxelizer_plot_bake_total, RTR("Plotting Meshes")); +} + +static bool voxelizer_sdf_bake_step_function(int current, int total) { + return VoxelGI::bake_step_function(500 + current * 500 / total, RTR("Generating Distance Field")); +} + Vector3i VoxelGI::get_estimated_cell_size() const { static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 }; int cell_subdiv = subdiv_value[subdiv]; @@ -432,22 +443,27 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { _find_meshes(p_from_node, mesh_list); if (bake_begin_function) { - bake_begin_function(mesh_list.size() + 1); + bake_begin_function(); } - int pmc = 0; + Voxelizer::BakeStepFunc voxelizer_step_func = bake_step_function != nullptr ? voxelizer_plot_bake_step_function : nullptr; + voxelizer_plot_bake_total = voxelizer_plot_bake_base = 0; for (PlotMesh &E : mesh_list) { - if (bake_step_function) { - bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(mesh_list.size())); + voxelizer_plot_bake_total += baker.get_bake_steps(E.mesh); + } + for (PlotMesh &E : mesh_list) { + if (baker.plot_mesh(E.local_xform, E.mesh, E.instance_materials, E.override_material, voxelizer_step_func) != Voxelizer::BAKE_RESULT_OK) { + baker.end_bake(); + if (bake_end_function) { + bake_end_function(); + } + return; } - - pmc++; - - baker.plot_mesh(E.local_xform, E.mesh, E.instance_materials, E.override_material); + voxelizer_plot_bake_base += baker.get_bake_steps(E.mesh); } if (bake_step_function) { - bake_step_function(pmc++, RTR("Finishing Plot")); + bake_step_function(500, RTR("Finishing Plot")); } baker.end_bake(); @@ -476,19 +492,22 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { } if (bake_step_function) { - bake_step_function(pmc++, RTR("Generating Distance Field")); + bake_step_function(500, RTR("Generating Distance Field")); } - Vector df = baker.get_sdf_3d_image(); + voxelizer_step_func = bake_step_function != nullptr ? voxelizer_sdf_bake_step_function : nullptr; - RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data_new->get_rid(), exposure_normalization); + Vector df; + if (baker.get_sdf_3d_image(df, voxelizer_step_func) == Voxelizer::BAKE_RESULT_OK) { + RS::get_singleton()->voxel_gi_set_baked_exposure_normalization(probe_data_new->get_rid(), exposure_normalization); - probe_data_new->allocate(baker.get_to_cell_space_xform(), AABB(-size / 2, size), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); + probe_data_new->allocate(baker.get_to_cell_space_xform(), AABB(-size / 2, size), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); - set_probe_data(probe_data_new); + set_probe_data(probe_data_new); #ifdef TOOLS_ENABLED - probe_data_new->set_edited(true); //so it gets saved + probe_data_new->set_edited(true); //so it gets saved #endif + } } if (bake_end_function) { diff --git a/godot/scene/3d/voxel_gi.h b/godot/scene/3d/voxel_gi.h index 7d7787f7..d7e0d74d 100644 --- a/godot/scene/3d/voxel_gi.h +++ b/godot/scene/3d/voxel_gi.h @@ -108,8 +108,8 @@ class VoxelGI : public VisualInstance3D { }; - typedef void (*BakeBeginFunc)(int); - typedef void (*BakeStepFunc)(int, const String &); + typedef void (*BakeBeginFunc)(); + typedef bool (*BakeStepFunc)(int, const String &); typedef void (*BakeEndFunc)(); private: diff --git a/godot/scene/3d/voxelizer.cpp b/godot/scene/3d/voxelizer.cpp index 99392e9b..1074cad1 100644 --- a/godot/scene/3d/voxelizer.cpp +++ b/godot/scene/3d/voxelizer.cpp @@ -382,8 +382,24 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref p_material return mc; } -void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const Vector> &p_materials, const Ref &p_override_material) { - ERR_FAIL_COND_MSG(!p_xform.is_finite(), "Invalid mesh bake transform."); +int Voxelizer::get_bake_steps(Ref &p_mesh) const { + int bake_total = 0; + for (int i = 0; i < p_mesh->get_surface_count(); i++) { + if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; // Only triangles. + } + Array a = p_mesh->surface_get_arrays(i); + Vector vertices = a[Mesh::ARRAY_VERTEX]; + Vector index = a[Mesh::ARRAY_INDEX]; + bake_total += (index.size() > 0 ? index.size() : vertices.size()) / 3; + } + return bake_total; +} + +Voxelizer::BakeResult Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const Vector> &p_materials, const Ref &p_override_material, BakeStepFunc p_bake_step_func) { + ERR_FAIL_COND_V_MSG(!p_xform.is_finite(), BAKE_RESULT_INVALID_PARAMETER, "Invalid mesh bake transform."); + + int bake_total = get_bake_steps(p_mesh), bake_current = 0; for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { @@ -428,6 +444,13 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const V Vector2 uvs[3]; Vector3 normal[3]; + bake_current++; + if (p_bake_step_func != nullptr && (bake_current & 2047) == 1) { + if (p_bake_step_func(bake_current, bake_total)) { + return BAKE_RESULT_CANCELLED; + } + } + for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[ir[j * 3 + k]]); } @@ -460,6 +483,13 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const V Vector2 uvs[3]; Vector3 normal[3]; + bake_current++; + if (p_bake_step_func != nullptr && (bake_current & 2047) == 1) { + if (p_bake_step_func(bake_current, bake_total)) { + return BAKE_RESULT_CANCELLED; + } + } + for (int k = 0; k < 3; k++) { vtxs[k] = p_xform.xform(vr[j * 3 + k]); } @@ -487,6 +517,8 @@ void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const V } max_original_cells = bake_cells.size(); + + return BAKE_RESULT_OK; } void Voxelizer::_sort() { @@ -821,7 +853,7 @@ static void edt(float *f, int stride, int n) { #undef square -Vector Voxelizer::get_sdf_3d_image() const { +Voxelizer::BakeResult Voxelizer::get_sdf_3d_image(Vector &r_image, BakeStepFunc p_bake_step_function) const { Vector3i octree_size = get_voxel_gi_octree_size(); uint32_t float_count = octree_size.x * octree_size.y * octree_size.z; @@ -849,9 +881,17 @@ Vector Voxelizer::get_sdf_3d_image() const { //process in each direction + int bake_total = octree_size.x * 2 + octree_size.y, bake_current = 0; + //xy->z - for (int i = 0; i < octree_size.x; i++) { + for (int i = 0; i < octree_size.x; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.y; j++) { edt(&work_memory[i + j * y_mult], z_mult, octree_size.z); } @@ -859,23 +899,34 @@ Vector Voxelizer::get_sdf_3d_image() const { //xz->y - for (int i = 0; i < octree_size.x; i++) { + for (int i = 0; i < octree_size.x; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.z; j++) { edt(&work_memory[i + j * z_mult], y_mult, octree_size.y); } } //yz->x - for (int i = 0; i < octree_size.y; i++) { + for (int i = 0; i < octree_size.y; i++, bake_current++) { + if (p_bake_step_function) { + if (p_bake_step_function(bake_current, bake_total)) { + memdelete_arr(work_memory); + return BAKE_RESULT_CANCELLED; + } + } for (int j = 0; j < octree_size.z; j++) { edt(&work_memory[i * y_mult + j * z_mult], 1, octree_size.x); } } - Vector image3d; - image3d.resize(float_count); + r_image.resize(float_count); { - uint8_t *w = image3d.ptrw(); + uint8_t *w = r_image.ptrw(); for (uint32_t i = 0; i < float_count; i++) { uint32_t d = uint32_t(Math::sqrt(work_memory[i])); if (d == 0) { @@ -888,7 +939,7 @@ Vector Voxelizer::get_sdf_3d_image() const { memdelete_arr(work_memory); - return image3d; + return BAKE_RESULT_OK; } #undef INF diff --git a/godot/scene/3d/voxelizer.h b/godot/scene/3d/voxelizer.h index 08d018ee..41e77673 100644 --- a/godot/scene/3d/voxelizer.h +++ b/godot/scene/3d/voxelizer.h @@ -34,6 +34,15 @@ #include "scene/resources/multimesh.h" class Voxelizer { +public: + enum BakeResult { + BAKE_RESULT_OK, + BAKE_RESULT_INVALID_PARAMETER, + BAKE_RESULT_CANCELLED, + }; + + typedef bool (*BakeStepFunc)(int, int); + private: enum : uint32_t { CHILD_EMPTY = 0xFFFFFFFF @@ -112,7 +121,8 @@ class Voxelizer { public: void begin_bake(int p_subdiv, const AABB &p_bounds, float p_exposure_normalization); - void plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const Vector> &p_materials, const Ref &p_override_material); + int get_bake_steps(Ref &p_mesh) const; + BakeResult plot_mesh(const Transform3D &p_xform, Ref &p_mesh, const Vector> &p_materials, const Ref &p_override_material, BakeStepFunc p_bake_step_function); void end_bake(); int get_voxel_gi_octree_depth() const; @@ -121,7 +131,7 @@ class Voxelizer { Vector get_voxel_gi_octree_cells() const; Vector get_voxel_gi_data_cells() const; Vector get_voxel_gi_level_cell_count() const; - Vector get_sdf_3d_image() const; + BakeResult get_sdf_3d_image(Vector &r_image, BakeStepFunc p_bake_step_function) const; Ref create_debug_multimesh(); diff --git a/godot/scene/animation/animation_node_extension.cpp b/godot/scene/animation/animation_node_extension.cpp index 358c339b..8536da9d 100644 --- a/godot/scene/animation/animation_node_extension.cpp +++ b/godot/scene/animation/animation_node_extension.cpp @@ -30,9 +30,6 @@ #include "animation_node_extension.h" -AnimationNodeExtension::AnimationNodeExtension() { -} - AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only) { PackedFloat32Array r_ret; @@ -45,23 +42,17 @@ AnimationNode::NodeTimeInfo AnimationNodeExtension::_process(const AnimationMixe return _array_to_node_time_info(r_ret); } -AnimationTree *AnimationNodeExtension::get_process_tree() const { - ERR_FAIL_NULL_V(process_state, nullptr); - return process_state->tree; -} - bool AnimationNodeExtension::is_looping(const PackedFloat32Array &p_node_info) { return _array_to_node_time_info(p_node_info).is_looping(); } -double AnimationNodeExtension::get_remain(const PackedFloat32Array &p_node_info, bool p_break_loop) { +double AnimationNodeExtension::get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop) { return _array_to_node_time_info(p_node_info).get_remain(p_break_loop); } void AnimationNodeExtension::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_process_tree"), &AnimationNodeExtension::get_process_tree); ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("is_looping", "node_info"), &AnimationNodeExtension::is_looping); - ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("get_remain", "node_info", "break_loop"), &AnimationNodeExtension::get_remain); + ClassDB::bind_static_method("AnimationNodeExtension", D_METHOD("get_remaining_time", "node_info", "break_loop"), &AnimationNodeExtension::get_remaining_time); GDVIRTUAL_BIND(_process, "playback_info", "test_only"); } diff --git a/godot/scene/animation/animation_node_extension.h b/godot/scene/animation/animation_node_extension.h index 8fa76b7a..08e4b4f0 100644 --- a/godot/scene/animation/animation_node_extension.h +++ b/godot/scene/animation/animation_node_extension.h @@ -37,20 +37,15 @@ class AnimationNodeExtension : public AnimationRootNode { GDCLASS(AnimationNodeExtension, AnimationRootNode); public: - AnimationNodeExtension(); - virtual ~AnimationNodeExtension() = default; - virtual NodeTimeInfo _process(const AnimationMixer::PlaybackInfo p_playback_info, bool p_test_only = false) override; - AnimationTree *get_process_tree() const; - static bool is_looping(const PackedFloat32Array &p_node_info); - static double get_remain(const PackedFloat32Array &p_node_info, bool p_break_loop = false); + static double get_remaining_time(const PackedFloat32Array &p_node_info, bool p_break_loop = false); protected: static void _bind_methods(); - GDVIRTUAL2R(PackedFloat32Array, _process, PackedFloat64Array, bool); + GDVIRTUAL2R_REQUIRED(PackedFloat32Array, _process, PackedFloat64Array, bool); private: static AnimationNode::NodeTimeInfo _array_to_node_time_info(const PackedFloat32Array &p_array); diff --git a/godot/scene/animation/animation_player.cpp b/godot/scene/animation/animation_player.cpp index 7d28aead..8d248594 100644 --- a/godot/scene/animation/animation_player.cpp +++ b/godot/scene/animation/animation_player.cpp @@ -164,8 +164,8 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f double delta = p_started ? 0 : p_delta * speed; double next_pos = cd.pos + delta; - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = cd.get_start_time(); + double end = cd.get_end_time(); Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; @@ -389,7 +389,7 @@ void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_na } void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) { - play_section(p_name, p_start_time, p_end_time, -1, true); + play_section(p_name, p_start_time, p_end_time, p_custom_blend, -1, true); } void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) { @@ -495,8 +495,8 @@ void AnimationPlayer::play_section(const StringName &p_name, double p_start_time c.current.start_time = p_start_time; c.current.end_time = p_end_time; - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = playback.current.get_start_time(); + double end = playback.current.get_end_time(); if (!end_reached) { playback_queue.clear(); @@ -660,8 +660,8 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_ } } - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = playback.current.get_start_time(); + double end = playback.current.get_end_time(); // Clamp the seek position. p_time = CLAMP(p_time, start, end); @@ -725,7 +725,7 @@ void AnimationPlayer::set_section(double p_start_time, double p_end_time) { ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time)); playback.current.start_time = p_start_time; playback.current.end_time = p_end_time; - playback.current.pos = CLAMP(playback.current.pos, get_section_start_time(), get_section_end_time()); + playback.current.pos = CLAMP(playback.current.pos, playback.current.get_start_time(), playback.current.get_end_time()); } void AnimationPlayer::reset_section() { @@ -735,18 +735,12 @@ void AnimationPlayer::reset_section() { double AnimationPlayer::get_section_start_time() const { ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation."); - if (Animation::is_less_approx(playback.current.start_time, 0) || playback.current.start_time > playback.current.from->animation->get_length()) { - return 0; - } - return playback.current.start_time; + return playback.current.get_start_time(); } double AnimationPlayer::get_section_end_time() const { ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation."); - if (Animation::is_less_approx(playback.current.end_time, 0) || playback.current.end_time > playback.current.from->animation->get_length()) { - return playback.current.from->animation->get_length(); - } - return playback.current.end_time; + return playback.current.get_end_time(); } bool AnimationPlayer::has_section() const { @@ -777,7 +771,7 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) { _clear_caches(); Playback &c = playback; // c.blend.clear(); - double start = c.current.from ? get_section_start_time() : 0; + double start = c.current.from ? playback.current.get_start_time() : 0; if (p_reset) { c.blend.clear(); if (p_keep_state) { diff --git a/godot/scene/animation/animation_player.h b/godot/scene/animation/animation_player.h index 6d7e8aa9..c87719a2 100644 --- a/godot/scene/animation/animation_player.h +++ b/godot/scene/animation/animation_player.h @@ -70,6 +70,18 @@ class AnimationPlayer : public AnimationMixer { float speed_scale = 1.0; double start_time = 0.0; double end_time = 0.0; + double get_start_time() const { + if (from && (Animation::is_less_approx(start_time, 0) || Animation::is_greater_approx(start_time, from->animation->get_length()))) { + return 0; + } + return start_time; + } + double get_end_time() const { + if (from && (Animation::is_less_approx(end_time, 0) || Animation::is_greater_approx(end_time, from->animation->get_length()))) { + return from->animation->get_length(); + } + return end_time; + } }; struct Blend { diff --git a/godot/scene/animation/animation_tree.cpp b/godot/scene/animation/animation_tree.cpp index f2871cda..c18b3fc7 100644 --- a/godot/scene/animation/animation_tree.cpp +++ b/godot/scene/animation/animation_tree.cpp @@ -419,6 +419,16 @@ bool AnimationNode::is_deletable() const { return closable; } +ObjectID AnimationNode::get_processing_animation_tree_instance_id() const { + ERR_FAIL_NULL_V(process_state, ObjectID()); + return process_state->tree->get_instance_id(); +} + +bool AnimationNode::is_process_testing() const { + ERR_FAIL_NULL_V(process_state, false); + return process_state->is_testing; +} + bool AnimationNode::is_path_filtered(const NodePath &p_path) const { return filter.has(p_path); } @@ -544,6 +554,10 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled); ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled); + ClassDB::bind_method(D_METHOD("get_processing_animation_tree_instance_id"), &AnimationNode::get_processing_animation_tree_instance_id); + + ClassDB::bind_method(D_METHOD("is_process_testing"), &AnimationNode::is_process_testing); + ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); diff --git a/godot/scene/animation/animation_tree.h b/godot/scene/animation/animation_tree.h index 5a2a822f..f4551581 100644 --- a/godot/scene/animation/animation_tree.h +++ b/godot/scene/animation/animation_tree.h @@ -226,6 +226,10 @@ class AnimationNode : public Resource { void set_deletable(bool p_closable); bool is_deletable() const; + ObjectID get_processing_animation_tree_instance_id() const; + + bool is_process_testing() const; + virtual bool has_filter() const; #ifdef TOOLS_ENABLED diff --git a/godot/scene/gui/color_mode.h b/godot/scene/gui/color_mode.h index 0abc90bb..b762097d 100644 --- a/godot/scene/gui/color_mode.h +++ b/godot/scene/gui/color_mode.h @@ -33,8 +33,6 @@ #include "scene/gui/color_picker.h" -struct Color; - class ColorMode { public: ColorPicker *color_picker = nullptr; diff --git a/godot/scene/gui/color_picker.cpp b/godot/scene/gui/color_picker.cpp index 06dff671..72d24f02 100644 --- a/godot/scene/gui/color_picker.cpp +++ b/godot/scene/gui/color_picker.cpp @@ -30,21 +30,25 @@ #include "color_picker.h" -#include "core/input/input.h" #include "core/io/image.h" -#include "core/math/color.h" +#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/color_mode.h" +#include "scene/gui/grid_container.h" +#include "scene/gui/label.h" +#include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/slider.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/texture_rect.h" #include "scene/resources/image_texture.h" #include "scene/resources/style_box_flat.h" #include "scene/resources/style_box_texture.h" #include "scene/theme/theme_db.h" -#include "servers/display_server.h" #include "thirdparty/misc/ok_color_shader.h" -List ColorPicker::preset_cache; -List ColorPicker::recent_preset_cache; - void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -159,10 +163,6 @@ void ColorPicker::_update_theme_item_cache() { theme_cache.base_scale = get_theme_default_base_scale(); } -Ref ColorPicker::wheel_shader; -Ref ColorPicker::circle_shader; -Ref ColorPicker::circle_ok_color_shader; - void ColorPicker::init_shaders() { wheel_shader.instantiate(); wheel_shader->set_code(R"( diff --git a/godot/scene/gui/color_picker.h b/godot/scene/gui/color_picker.h index aedf4aef..c7ad1fb4 100644 --- a/godot/scene/gui/color_picker.h +++ b/godot/scene/gui/color_picker.h @@ -31,28 +31,24 @@ #ifndef COLOR_PICKER_H #define COLOR_PICKER_H -#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" -#include "scene/gui/control.h" -#include "scene/gui/grid_container.h" -#include "scene/gui/label.h" -#include "scene/gui/line_edit.h" -#include "scene/gui/menu_button.h" -#include "scene/gui/option_button.h" -#include "scene/gui/panel.h" #include "scene/gui/popup.h" -#include "scene/gui/separator.h" -#include "scene/gui/slider.h" -#include "scene/gui/spin_box.h" -#include "scene/gui/texture_rect.h" -#include "scene/resources/style_box_flat.h" +class AspectRatioContainer; class ColorMode; -class ColorModeRGB; -class ColorModeHSV; -class ColorModeRAW; -class ColorModeOKHSL; +class ColorPickerShape; +class GridContainer; +class HSlider; +class Label; +class LineEdit; +class MarginContainer; +class MenuButton; +class OptionButton; +class PopupMenu; +class SpinBox; +class StyleBoxFlat; +class TextureRect; class ColorPresetButton : public BaseButton { GDCLASS(ColorPresetButton, BaseButton); @@ -110,11 +106,11 @@ class ColorPicker : public VBoxContainer { static const int SLIDER_COUNT = 4; private: - static Ref wheel_shader; - static Ref circle_shader; - static Ref circle_ok_color_shader; - static List preset_cache; - static List recent_preset_cache; + static inline Ref wheel_shader; + static inline Ref circle_shader; + static inline Ref circle_ok_color_shader; + static inline List preset_cache; + static inline List recent_preset_cache; #ifdef TOOLS_ENABLED Object *editor_settings = nullptr; diff --git a/godot/scene/gui/line_edit.cpp b/godot/scene/gui/line_edit.cpp index b7c73261..b536e23b 100644 --- a/godot/scene/gui/line_edit.cpp +++ b/godot/scene/gui/line_edit.cpp @@ -550,7 +550,9 @@ void LineEdit::gui_input(const Ref &p_event) { pending_select_all_on_focus = false; } - show_virtual_keyboard(); + if (editable) { + show_virtual_keyboard(); + } } queue_redraw(); diff --git a/godot/scene/gui/scroll_bar.cpp b/godot/scene/gui/scroll_bar.cpp index eb69dab4..38a4bb42 100644 --- a/godot/scene/gui/scroll_bar.cpp +++ b/godot/scene/gui/scroll_bar.cpp @@ -93,7 +93,7 @@ void ScrollBar::gui_input(const Ref &p_event) { return; } - ofs -= decr_size; + ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT); if (ofs < grabber_ofs) { if (scrolling) { @@ -151,7 +151,7 @@ void ScrollBar::gui_input(const Ref &p_event) { Ref decr = theme_cache.decrement_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); - ofs -= decr_size; + ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT); double diff = (ofs - drag.pos_at_click) / get_area_size(); @@ -248,8 +248,6 @@ void ScrollBar::_notification(int p_what) { incr = theme_cache.increment_icon; } - Ref bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style; - Ref grabber; if (drag.active) { grabber = theme_cache.grabber_pressed_style; @@ -277,7 +275,11 @@ void ScrollBar::_notification(int p_what) { area.height -= incr->get_height() + decr->get_height(); } - bg->draw(ci, Rect2(ofs, area)); + if (has_focus()) { + theme_cache.scroll_focus_style->draw(ci, Rect2(ofs, area)); + } else { + theme_cache.scroll_style->draw(ci, Rect2(ofs, area)); + } if (orientation == HORIZONTAL) { ofs.width += area.width; @@ -292,11 +294,11 @@ void ScrollBar::_notification(int p_what) { grabber_rect.size.width = get_grabber_size(); grabber_rect.size.height = get_size().height; grabber_rect.position.y = 0; - grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT); + grabber_rect.position.x = get_grabber_offset() + decr->get_width() + theme_cache.scroll_style->get_margin(SIDE_LEFT); } else { grabber_rect.size.width = get_size().width; grabber_rect.size.height = get_grabber_size(); - grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP); + grabber_rect.position.y = get_grabber_offset() + decr->get_height() + theme_cache.scroll_style->get_margin(SIDE_TOP); grabber_rect.position.x = 0; } diff --git a/godot/scene/gui/text_edit.cpp b/godot/scene/gui/text_edit.cpp index b8bb17eb..a33769ea 100644 --- a/godot/scene/gui/text_edit.cpp +++ b/godot/scene/gui/text_edit.cpp @@ -1605,7 +1605,9 @@ void TextEdit::_notification(int p_what) { draw_caret = true; } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -2008,7 +2010,9 @@ void TextEdit::gui_input(const Ref &p_gui_input) { } } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } } diff --git a/godot/scene/main/viewport.cpp b/godot/scene/main/viewport.cpp index a0f39462..dd3ae1ad 100644 --- a/godot/scene/main/viewport.cpp +++ b/godot/scene/main/viewport.cpp @@ -1254,6 +1254,10 @@ Ref Viewport::get_world_2d() const { return world_2d; } +Transform2D Viewport::get_stretch_transform() const { + return stretch_transform; +} + Transform2D Viewport::get_final_transform() const { ERR_READ_THREAD_GUARD_V(Transform2D()); return stretch_transform * global_canvas_transform; @@ -4636,6 +4640,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform); + ClassDB::bind_method(D_METHOD("get_stretch_transform"), &Viewport::get_stretch_transform); ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform); ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform); diff --git a/godot/scene/main/viewport.h b/godot/scene/main/viewport.h index 92691ccb..3a5ad2d8 100644 --- a/godot/scene/main/viewport.h +++ b/godot/scene/main/viewport.h @@ -515,6 +515,7 @@ class Viewport : public Node { void set_global_canvas_transform(const Transform2D &p_transform); Transform2D get_global_canvas_transform() const; + Transform2D get_stretch_transform() const; virtual Transform2D get_final_transform() const; void gui_set_root_order_dirty(); diff --git a/godot/scene/register_scene_types.cpp b/godot/scene/register_scene_types.cpp index 3d077193..a7c7c3a1 100644 --- a/godot/scene/register_scene_types.cpp +++ b/godot/scene/register_scene_types.cpp @@ -514,7 +514,7 @@ void register_scene_types() { GDREGISTER_CLASS(AnimationNodeBlendSpace2D); GDREGISTER_CLASS(AnimationNodeStateMachine); GDREGISTER_CLASS(AnimationNodeStateMachinePlayback); - GDREGISTER_CLASS(AnimationNodeExtension); + GDREGISTER_VIRTUAL_CLASS(AnimationNodeExtension); GDREGISTER_INTERNAL_CLASS(AnimationNodeStartState); GDREGISTER_INTERNAL_CLASS(AnimationNodeEndState); diff --git a/godot/scene/resources/3d/box_shape_3d.cpp b/godot/scene/resources/3d/box_shape_3d.cpp index 313aeb1b..afb03a8b 100644 --- a/godot/scene/resources/3d/box_shape_3d.cpp +++ b/godot/scene/resources/3d/box_shape_3d.cpp @@ -29,6 +29,8 @@ /**************************************************************************/ #include "box_shape_3d.h" + +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector BoxShape3D::get_debug_mesh_lines() const { @@ -47,6 +49,24 @@ Vector BoxShape3D::get_debug_mesh_lines() const { return lines; } +Ref BoxShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array box_array; + box_array.resize(RS::ARRAY_MAX); + BoxMesh::create_mesh_array(box_array, size); + + Vector colors; + const PackedVector3Array &verts = box_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref box_mesh = memnew(ArrayMesh); + box_array[RS::ARRAY_COLOR] = colors; + box_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array); + return box_mesh; +} + real_t BoxShape3D::get_enclosing_radius() const { return size.length() / 2; } diff --git a/godot/scene/resources/3d/box_shape_3d.h b/godot/scene/resources/3d/box_shape_3d.h index 45c1cde5..a9137fdc 100644 --- a/godot/scene/resources/3d/box_shape_3d.h +++ b/godot/scene/resources/3d/box_shape_3d.h @@ -51,6 +51,7 @@ class BoxShape3D : public Shape3D { Vector3 get_size() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; BoxShape3D(); diff --git a/godot/scene/resources/3d/capsule_shape_3d.cpp b/godot/scene/resources/3d/capsule_shape_3d.cpp index 9e168010..b63bf69a 100644 --- a/godot/scene/resources/3d/capsule_shape_3d.cpp +++ b/godot/scene/resources/3d/capsule_shape_3d.cpp @@ -30,6 +30,7 @@ #include "capsule_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector CapsuleShape3D::get_debug_mesh_lines() const { @@ -67,6 +68,24 @@ Vector CapsuleShape3D::get_debug_mesh_lines() const { return points; } +Ref CapsuleShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array capsule_array; + capsule_array.resize(RS::ARRAY_MAX); + CapsuleMesh::create_mesh_array(capsule_array, radius, height, 32, 8); + + Vector colors; + const PackedVector3Array &verts = capsule_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref capsule_mesh = memnew(ArrayMesh); + capsule_array[RS::ARRAY_COLOR] = colors; + capsule_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, capsule_array); + return capsule_mesh; +} + real_t CapsuleShape3D::get_enclosing_radius() const { return height * 0.5; } diff --git a/godot/scene/resources/3d/capsule_shape_3d.h b/godot/scene/resources/3d/capsule_shape_3d.h index 90ee3b58..2ad7fa45 100644 --- a/godot/scene/resources/3d/capsule_shape_3d.h +++ b/godot/scene/resources/3d/capsule_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CapsuleShape3D : public Shape3D { GDCLASS(CapsuleShape3D, Shape3D); float radius = 0.5; @@ -50,6 +52,7 @@ class CapsuleShape3D : public Shape3D { float get_height() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; CapsuleShape3D(); diff --git a/godot/scene/resources/3d/concave_polygon_shape_3d.cpp b/godot/scene/resources/3d/concave_polygon_shape_3d.cpp index 82b12590..1254cc43 100644 --- a/godot/scene/resources/3d/concave_polygon_shape_3d.cpp +++ b/godot/scene/resources/3d/concave_polygon_shape_3d.cpp @@ -30,6 +30,7 @@ #include "concave_polygon_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector ConcavePolygonShape3D::get_debug_mesh_lines() const { @@ -59,6 +60,23 @@ Vector ConcavePolygonShape3D::get_debug_mesh_lines() const { return points; } +Ref ConcavePolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Vector colors; + + for (int i = 0; i < faces.size(); i++) { + colors.push_back(p_modulate); + } + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = faces; + a[RS::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t ConcavePolygonShape3D::get_enclosing_radius() const { Vector data = get_faces(); const Vector3 *read = data.ptr(); diff --git a/godot/scene/resources/3d/concave_polygon_shape_3d.h b/godot/scene/resources/3d/concave_polygon_shape_3d.h index a5e46474..d5e5bc39 100644 --- a/godot/scene/resources/3d/concave_polygon_shape_3d.h +++ b/godot/scene/resources/3d/concave_polygon_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConcavePolygonShape3D : public Shape3D { GDCLASS(ConcavePolygonShape3D, Shape3D); @@ -72,6 +74,7 @@ class ConcavePolygonShape3D : public Shape3D { bool is_backface_collision_enabled() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; ConcavePolygonShape3D(); diff --git a/godot/scene/resources/3d/convex_polygon_shape_3d.cpp b/godot/scene/resources/3d/convex_polygon_shape_3d.cpp index 586d5f46..809c089e 100644 --- a/godot/scene/resources/3d/convex_polygon_shape_3d.cpp +++ b/godot/scene/resources/3d/convex_polygon_shape_3d.cpp @@ -30,6 +30,7 @@ #include "convex_polygon_shape_3d.h" #include "core/math/convex_hull.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector ConvexPolygonShape3D::get_debug_mesh_lines() const { @@ -53,6 +54,44 @@ Vector ConvexPolygonShape3D::get_debug_mesh_lines() const { return Vector(); } +Ref ConvexPolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + const Vector hull_points = get_points(); + + Vector verts; + Vector colors; + Vector indices; + + if (hull_points.size() >= 3) { + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(hull_points, md); + if (err == OK) { + verts = md.vertices; + for (int i = 0; i < verts.size(); i++) { + colors.push_back(p_modulate); + } + for (const Geometry3D::MeshData::Face &face : md.faces) { + const int first_point = face.indices[0]; + const int indices_count = face.indices.size(); + for (int i = 1; i < indices_count - 1; i++) { + indices.push_back(first_point); + indices.push_back(face.indices[i]); + indices.push_back(face.indices[i + 1]); + } + } + } + } + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t ConvexPolygonShape3D::get_enclosing_radius() const { Vector data = get_points(); const Vector3 *read = data.ptr(); diff --git a/godot/scene/resources/3d/convex_polygon_shape_3d.h b/godot/scene/resources/3d/convex_polygon_shape_3d.h index 7d1ac123..2dd4ce66 100644 --- a/godot/scene/resources/3d/convex_polygon_shape_3d.h +++ b/godot/scene/resources/3d/convex_polygon_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConvexPolygonShape3D : public Shape3D { GDCLASS(ConvexPolygonShape3D, Shape3D); Vector points; @@ -47,6 +49,7 @@ class ConvexPolygonShape3D : public Shape3D { Vector get_points() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; ConvexPolygonShape3D(); diff --git a/godot/scene/resources/3d/cylinder_shape_3d.cpp b/godot/scene/resources/3d/cylinder_shape_3d.cpp index a91282fd..700ff588 100644 --- a/godot/scene/resources/3d/cylinder_shape_3d.cpp +++ b/godot/scene/resources/3d/cylinder_shape_3d.cpp @@ -30,6 +30,7 @@ #include "cylinder_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector CylinderShape3D::get_debug_mesh_lines() const { @@ -60,6 +61,24 @@ Vector CylinderShape3D::get_debug_mesh_lines() const { return points; } +Ref CylinderShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array cylinder_array; + cylinder_array.resize(RS::ARRAY_MAX); + CylinderMesh::create_mesh_array(cylinder_array, radius, radius, height, 32); + + Vector colors; + const PackedVector3Array &verts = cylinder_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref cylinder_mesh = memnew(ArrayMesh); + cylinder_array[RS::ARRAY_COLOR] = colors; + cylinder_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + return cylinder_mesh; +} + real_t CylinderShape3D::get_enclosing_radius() const { return Vector2(radius, height * 0.5).length(); } diff --git a/godot/scene/resources/3d/cylinder_shape_3d.h b/godot/scene/resources/3d/cylinder_shape_3d.h index bd57bc2a..9388cab3 100644 --- a/godot/scene/resources/3d/cylinder_shape_3d.h +++ b/godot/scene/resources/3d/cylinder_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CylinderShape3D : public Shape3D { GDCLASS(CylinderShape3D, Shape3D); float radius = 0.5; @@ -49,6 +51,7 @@ class CylinderShape3D : public Shape3D { float get_height() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; CylinderShape3D(); diff --git a/godot/scene/resources/3d/height_map_shape_3d.cpp b/godot/scene/resources/3d/height_map_shape_3d.cpp index 5b55b661..65b14256 100644 --- a/godot/scene/resources/3d/height_map_shape_3d.cpp +++ b/godot/scene/resources/3d/height_map_shape_3d.cpp @@ -31,6 +31,7 @@ #include "height_map_shape_3d.h" #include "core/io/image.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector HeightMapShape3D::get_debug_mesh_lines() const { @@ -82,6 +83,60 @@ Vector HeightMapShape3D::get_debug_mesh_lines() const { return points; } +Ref HeightMapShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Vector verts; + Vector colors; + Vector indices; + + // This will be slow for large maps... + + if ((map_width != 0) && (map_depth != 0)) { + Vector2 size = Vector2(map_width - 1, map_depth - 1) * -0.5; + const real_t *r = map_data.ptr(); + + for (int d = 0; d <= map_depth - 2; d++) { + const int this_row_offset = map_width * d; + const int next_row_offset = this_row_offset + map_width; + + for (int w = 0; w <= map_width - 2; w++) { + const float height_tl = r[next_row_offset + w]; + const float height_bl = r[this_row_offset + w]; + const float height_br = r[this_row_offset + w + 1]; + const float height_tr = r[next_row_offset + w + 1]; + + const int index_offset = verts.size(); + + verts.push_back(Vector3(size.x + w, height_tl, size.y + d + 1)); + verts.push_back(Vector3(size.x + w, height_bl, size.y + d)); + verts.push_back(Vector3(size.x + w + 1, height_br, size.y + d)); + verts.push_back(Vector3(size.x + w + 1, height_tr, size.y + d + 1)); + + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + + indices.push_back(index_offset); + indices.push_back(index_offset + 1); + indices.push_back(index_offset + 2); + indices.push_back(index_offset); + indices.push_back(index_offset + 2); + indices.push_back(index_offset + 3); + } + } + } + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t HeightMapShape3D::get_enclosing_radius() const { return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length(); } diff --git a/godot/scene/resources/3d/height_map_shape_3d.h b/godot/scene/resources/3d/height_map_shape_3d.h index 33ba9c44..b5be5309 100644 --- a/godot/scene/resources/3d/height_map_shape_3d.h +++ b/godot/scene/resources/3d/height_map_shape_3d.h @@ -33,6 +33,7 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; class Image; class HeightMapShape3D : public Shape3D { @@ -62,6 +63,7 @@ class HeightMapShape3D : public Shape3D { void update_map_data_from_image(const Ref &p_image, real_t p_height_min, real_t p_height_max); virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; HeightMapShape3D(); diff --git a/godot/scene/resources/3d/mesh_library.cpp b/godot/scene/resources/3d/mesh_library.cpp index c2f721a8..446abc13 100644 --- a/godot/scene/resources/3d/mesh_library.cpp +++ b/godot/scene/resources/3d/mesh_library.cpp @@ -47,6 +47,24 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { set_item_mesh(idx, p_value); } else if (what == "mesh_transform") { set_item_mesh_transform(idx, p_value); + } else if (what == "mesh_cast_shadow") { + switch ((int)p_value) { + case 0: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF); + } break; + case 1: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + case 2: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED); + } break; + case 3: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY); + } break; + default: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + } } else if (what == "shape") { Vector shapes; ShapeData sd; @@ -91,6 +109,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_mesh(idx); } else if (what == "mesh_transform") { r_ret = get_item_mesh_transform(idx); + } else if (what == "mesh_cast_shadow") { + r_ret = (int)get_item_mesh_cast_shadow(idx); } else if (what == "shapes") { r_ret = _get_item_shapes(idx); } else if (what == "navigation_mesh") { @@ -120,6 +140,7 @@ void MeshLibrary::_get_property_list(List *p_list) const { p_list->push_back(PropertyInfo(Variant::STRING, prop_name + PNAME("name"))); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("mesh_cast_shadow"), PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only")); p_list->push_back(PropertyInfo(Variant::ARRAY, prop_name + PNAME("shapes"))); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navigation_mesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navigation_mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); @@ -154,6 +175,12 @@ void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_trans emit_changed(); } +void MeshLibrary::set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting) { + ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + item_map[p_item].mesh_cast_shadow = p_shadow_casting_setting; + emit_changed(); +} + void MeshLibrary::set_item_shapes(int p_item, const Vector &p_shapes) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].shapes = p_shapes; @@ -200,6 +227,11 @@ Transform3D MeshLibrary::get_item_mesh_transform(int p_item) const { return item_map[p_item].mesh_transform; } +RS::ShadowCastingSetting MeshLibrary::get_item_mesh_cast_shadow(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON, "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + return item_map[p_item].mesh_cast_shadow; +} + Vector MeshLibrary::get_item_shapes(int p_item) const { ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); return item_map[p_item].shapes; @@ -328,6 +360,7 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform); + ClassDB::bind_method(D_METHOD("set_item_mesh_cast_shadow", "id", "shadow_casting_setting"), &MeshLibrary::set_item_mesh_cast_shadow); ClassDB::bind_method(D_METHOD("set_item_navigation_mesh", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh); ClassDB::bind_method(D_METHOD("set_item_navigation_mesh_transform", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh_transform); ClassDB::bind_method(D_METHOD("set_item_navigation_layers", "id", "navigation_layers"), &MeshLibrary::set_item_navigation_layers); @@ -336,6 +369,7 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform); + ClassDB::bind_method(D_METHOD("get_item_mesh_cast_shadow", "id"), &MeshLibrary::get_item_mesh_cast_shadow); ClassDB::bind_method(D_METHOD("get_item_navigation_mesh", "id"), &MeshLibrary::get_item_navigation_mesh); ClassDB::bind_method(D_METHOD("get_item_navigation_mesh_transform", "id"), &MeshLibrary::get_item_navigation_mesh_transform); ClassDB::bind_method(D_METHOD("get_item_navigation_layers", "id"), &MeshLibrary::get_item_navigation_layers); diff --git a/godot/scene/resources/3d/mesh_library.h b/godot/scene/resources/3d/mesh_library.h index f1a1e3e2..fc38b848 100644 --- a/godot/scene/resources/3d/mesh_library.h +++ b/godot/scene/resources/3d/mesh_library.h @@ -35,6 +35,7 @@ #include "core/templates/rb_map.h" #include "scene/3d/navigation_region_3d.h" #include "scene/resources/mesh.h" +#include "servers/rendering_server.h" #include "shape_3d.h" class MeshLibrary : public Resource { @@ -50,6 +51,7 @@ class MeshLibrary : public Resource { String name; Ref mesh; Transform3D mesh_transform; + RS::ShadowCastingSetting mesh_cast_shadow = RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON; Vector shapes; Ref preview; Ref navigation_mesh; @@ -75,6 +77,7 @@ class MeshLibrary : public Resource { void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref &p_mesh); void set_item_mesh_transform(int p_item, const Transform3D &p_transform); + void set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting); void set_item_navigation_mesh(int p_item, const Ref &p_navigation_mesh); void set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform); void set_item_navigation_layers(int p_item, uint32_t p_navigation_layers); @@ -83,6 +86,7 @@ class MeshLibrary : public Resource { String get_item_name(int p_item) const; Ref get_item_mesh(int p_item) const; Transform3D get_item_mesh_transform(int p_item) const; + RS::ShadowCastingSetting get_item_mesh_cast_shadow(int p_item) const; Ref get_item_navigation_mesh(int p_item) const; Transform3D get_item_navigation_mesh_transform(int p_item) const; uint32_t get_item_navigation_layers(int p_item) const; diff --git a/godot/scene/resources/3d/separation_ray_shape_3d.cpp b/godot/scene/resources/3d/separation_ray_shape_3d.cpp index 07e93b8b..55529be6 100644 --- a/godot/scene/resources/3d/separation_ray_shape_3d.cpp +++ b/godot/scene/resources/3d/separation_ray_shape_3d.cpp @@ -30,6 +30,7 @@ #include "separation_ray_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector SeparationRayShape3D::get_debug_mesh_lines() const { @@ -41,6 +42,10 @@ Vector SeparationRayShape3D::get_debug_mesh_lines() const { return points; } +Ref SeparationRayShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + return memnew(ArrayMesh); +} + real_t SeparationRayShape3D::get_enclosing_radius() const { return length; } diff --git a/godot/scene/resources/3d/separation_ray_shape_3d.h b/godot/scene/resources/3d/separation_ray_shape_3d.h index f24f0eae..c1c273c4 100644 --- a/godot/scene/resources/3d/separation_ray_shape_3d.h +++ b/godot/scene/resources/3d/separation_ray_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SeparationRayShape3D : public Shape3D { GDCLASS(SeparationRayShape3D, Shape3D); float length = 1.0; @@ -50,6 +52,7 @@ class SeparationRayShape3D : public Shape3D { bool get_slide_on_slope() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; SeparationRayShape3D(); diff --git a/godot/scene/resources/3d/shape_3d.cpp b/godot/scene/resources/3d/shape_3d.cpp index 259d82b7..a1ee7b85 100644 --- a/godot/scene/resources/3d/shape_3d.cpp +++ b/godot/scene/resources/3d/shape_3d.cpp @@ -66,6 +66,34 @@ void Shape3D::set_margin(real_t p_margin) { PhysicsServer3D::get_singleton()->shape_set_margin(shape, margin); } +#ifdef DEBUG_ENABLED +void Shape3D::set_debug_color(const Color &p_color) { + if (p_color == debug_color) { + return; + } + + debug_color = p_color; + _update_shape(); +} + +Color Shape3D::get_debug_color() const { + return debug_color; +} + +void Shape3D::set_debug_fill(bool p_fill) { + if (p_fill == debug_fill) { + return; + } + + debug_fill = p_fill; + _update_shape(); +} + +bool Shape3D::get_debug_fill() const { + return debug_fill; +} +#endif // DEBUG_ENABLED + Ref Shape3D::get_debug_mesh() { if (debug_mesh_cache.is_valid()) { return debug_mesh_cache; @@ -79,29 +107,57 @@ Ref Shape3D::get_debug_mesh() { //make mesh Vector array; array.resize(lines.size()); - { - Vector3 *w = array.ptrw(); - for (int i = 0; i < lines.size(); i++) { - w[i] = lines[i]; - } + Vector3 *v = array.ptrw(); + + Vector arraycol; + arraycol.resize(lines.size()); + Color *c = arraycol.ptrw(); + + for (int i = 0; i < lines.size(); i++) { + v[i] = lines[i]; + c[i] = debug_color; } - Array arr; - arr.resize(Mesh::ARRAY_MAX); - arr[Mesh::ARRAY_VERTEX] = array; + Array lines_array; + lines_array.resize(Mesh::ARRAY_MAX); + lines_array[Mesh::ARRAY_VERTEX] = array; + lines_array[Mesh::ARRAY_COLOR] = arraycol; - SceneTree *st = Object::cast_to(OS::get_singleton()->get_main_loop()); + Ref material = get_debug_collision_material(); - debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arr); + debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, lines_array); + debug_mesh_cache->surface_set_material(0, material); - if (st) { - debug_mesh_cache->surface_set_material(0, st->get_debug_collision_material()); + if (debug_fill) { + Array solid_array = get_debug_arraymesh_faces(debug_color * Color(1.0, 1.0, 1.0, 0.0625))->surface_get_arrays(0); + debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, solid_array); + debug_mesh_cache->surface_set_material(1, material); } } return debug_mesh_cache; } +Ref Shape3D::get_debug_collision_material() { + if (collision_material.is_valid()) { + return collision_material; + } + + Ref material = memnew(StandardMaterial3D); + material->set_albedo(Color(1.0, 1.0, 1.0)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + material->set_cull_mode(StandardMaterial3D::CULL_BACK); + material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + collision_material = material; + + return collision_material; +} + void Shape3D::_update_shape() { emit_changed(); debug_mesh_cache.unref(); diff --git a/godot/scene/resources/3d/shape_3d.h b/godot/scene/resources/3d/shape_3d.h index 5e6cdbe4..e956f4c3 100644 --- a/godot/scene/resources/3d/shape_3d.h +++ b/godot/scene/resources/3d/shape_3d.h @@ -34,6 +34,7 @@ #include "core/io/resource.h" class ArrayMesh; +class Material; class Shape3D : public Resource { GDCLASS(Shape3D, Resource); @@ -44,6 +45,10 @@ class Shape3D : public Resource { real_t margin = 0.04; Ref debug_mesh_cache; + Ref collision_material; + + Color debug_color = Color(0.0, 0.0, 0.0, 0.0); + bool debug_fill = true; protected: static void _bind_methods(); @@ -51,6 +56,8 @@ class Shape3D : public Resource { _FORCE_INLINE_ RID get_shape() const { return shape; } Shape3D(RID p_shape); + Ref get_debug_collision_material(); + virtual void _update_shape(); public: @@ -58,6 +65,7 @@ class Shape3D : public Resource { Ref get_debug_mesh(); virtual Vector get_debug_mesh_lines() const = 0; // { return Vector(); } + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const = 0; /// Returns the radius of a sphere that fully enclose this shape virtual real_t get_enclosing_radius() const = 0; @@ -69,6 +77,14 @@ class Shape3D : public Resource { real_t get_margin() const; void set_margin(real_t p_margin); +#ifdef DEBUG_ENABLED + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill(bool p_fill); + bool get_debug_fill() const; +#endif // DEBUG_ENABLED + Shape3D(); ~Shape3D(); }; diff --git a/godot/scene/resources/3d/sphere_shape_3d.cpp b/godot/scene/resources/3d/sphere_shape_3d.cpp index 56b78471..bdce41c1 100644 --- a/godot/scene/resources/3d/sphere_shape_3d.cpp +++ b/godot/scene/resources/3d/sphere_shape_3d.cpp @@ -30,6 +30,8 @@ #include "sphere_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" +#include "scene/resources/material.h" #include "servers/physics_server_3d.h" Vector SphereShape3D::get_debug_mesh_lines() const { @@ -54,6 +56,24 @@ Vector SphereShape3D::get_debug_mesh_lines() const { return points; } +Ref SphereShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array sphere_array; + sphere_array.resize(RS::ARRAY_MAX); + SphereMesh::create_mesh_array(sphere_array, radius, radius * 2, 32); + + Vector colors; + const PackedVector3Array &verts = sphere_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref sphere_mesh = memnew(ArrayMesh); + sphere_array[RS::ARRAY_COLOR] = colors; + sphere_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, sphere_array); + return sphere_mesh; +} + real_t SphereShape3D::get_enclosing_radius() const { return radius; } diff --git a/godot/scene/resources/3d/sphere_shape_3d.h b/godot/scene/resources/3d/sphere_shape_3d.h index 8e95cea6..cb068528 100644 --- a/godot/scene/resources/3d/sphere_shape_3d.h +++ b/godot/scene/resources/3d/sphere_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SphereShape3D : public Shape3D { GDCLASS(SphereShape3D, Shape3D); float radius = 0.5f; @@ -47,6 +49,7 @@ class SphereShape3D : public Shape3D { float get_radius() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; SphereShape3D(); diff --git a/godot/scene/resources/3d/world_boundary_shape_3d.cpp b/godot/scene/resources/3d/world_boundary_shape_3d.cpp index beaaddc9..25f060aa 100644 --- a/godot/scene/resources/3d/world_boundary_shape_3d.cpp +++ b/godot/scene/resources/3d/world_boundary_shape_3d.cpp @@ -30,6 +30,7 @@ #include "world_boundary_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector WorldBoundaryShape3D::get_debug_mesh_lines() const { @@ -61,6 +62,53 @@ Vector WorldBoundaryShape3D::get_debug_mesh_lines() const { return points; } +Ref WorldBoundaryShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Plane p = get_plane(); + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4] = { + p.normal * p.d + n1 * 10.0 + n2 * 10.0, + p.normal * p.d + n1 * 10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * 10.0, + }; + + Vector points = { + pface[0], + pface[1], + pface[2], + pface[3], + }; + + Vector colors = { + p_modulate, + p_modulate, + p_modulate, + p_modulate, + }; + + Vector indices = { + 0, + 1, + 2, + 0, + 2, + 3, + }; + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = points; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + void WorldBoundaryShape3D::_update_shape() { PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane); Shape3D::_update_shape(); diff --git a/godot/scene/resources/3d/world_boundary_shape_3d.h b/godot/scene/resources/3d/world_boundary_shape_3d.h index 06cff6aa..456316df 100644 --- a/godot/scene/resources/3d/world_boundary_shape_3d.h +++ b/godot/scene/resources/3d/world_boundary_shape_3d.h @@ -33,6 +33,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class WorldBoundaryShape3D : public Shape3D { GDCLASS(WorldBoundaryShape3D, Shape3D); Plane plane; @@ -46,6 +48,7 @@ class WorldBoundaryShape3D : public Shape3D { const Plane &get_plane() const; virtual Vector get_debug_mesh_lines() const override; + virtual Ref get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override { // Should be infinite? return 0; diff --git a/godot/scene/resources/material.cpp b/godot/scene/resources/material.cpp index ecc1982a..96719a9e 100644 --- a/godot/scene/resources/material.cpp +++ b/godot/scene/resources/material.cpp @@ -2737,13 +2737,13 @@ float BaseMaterial3D::get_grow() const { return grow; } -static Plane _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) { - static const Plane masks[5] = { - Plane(1, 0, 0, 0), - Plane(0, 1, 0, 0), - Plane(0, 0, 1, 0), - Plane(0, 0, 0, 1), - Plane(0.3333333, 0.3333333, 0.3333333, 0), +static Vector4 _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) { + static const Vector4 masks[5] = { + Vector4(1, 0, 0, 0), + Vector4(0, 1, 0, 0), + Vector4(0, 0, 1, 0), + Vector4(0, 0, 0, 1), + Vector4(0.3333333, 0.3333333, 0.3333333, 0), }; return masks[p_channel]; diff --git a/godot/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/godot/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 41df6107..752e0fe4 100644 --- a/godot/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/godot/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -452,7 +452,7 @@ void ClusterBuilderRD::bake_cluster() { // Render elements. { - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer); ClusterBuilderSharedDataRD::ClusterRender::PushConstant push_constant = {}; RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shared->cluster_render.shader_pipelines[use_msaa ? ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_MSAA : ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_NORMAL]); diff --git a/godot/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/godot/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index e6262c83..398718e7 100644 --- a/godot/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/godot/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -356,7 +356,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr ERR_FAIL_COND(shader.is_null()); RID framebuffer = p_buffers.base_weight_fb; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0); @@ -388,7 +388,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; // Pass 1 - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); @@ -412,7 +412,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture; RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1); @@ -430,7 +430,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr framebuffer = p_buffers.base_fb; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1); @@ -463,7 +463,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); @@ -481,7 +481,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr framebuffer = p_buffers.base_fb; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1); diff --git a/godot/servers/rendering/renderer_rd/effects/copy_effects.cpp b/godot/servers/rendering/renderer_rd/effects/copy_effects.cpp index 808c82f7..a3a873a2 100644 --- a/godot/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/godot/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -591,7 +591,7 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector(), 0.0, 0, p_rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, p_rect); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); if (p_secondary.is_valid()) { @@ -658,7 +658,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) { ERR_FAIL_COND(shader.is_null()); // Just copy it back (we use our blur raster shader here).. - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); @@ -731,7 +731,7 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); @@ -833,7 +833,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu ERR_FAIL_COND(shader.is_null()); //HORIZONTAL - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); if (p_auto_exposure.is_valid() && p_first_pass) { @@ -853,7 +853,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu ERR_FAIL_COND(shader.is_null()); //VERTICAL - draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0); @@ -923,7 +923,7 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_set_push_constant(draw_list, &blur_raster.push_constant, sizeof(BlurRasterPushConstant)); @@ -988,7 +988,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector(), 0.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, p_region); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, ©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); @@ -1025,7 +1025,7 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector(), 1.0f, 0, screen_rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, screen_rect); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); @@ -1090,7 +1090,7 @@ void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_fra RID shader = cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); @@ -1169,7 +1169,7 @@ void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebu RID shader = filter.raster_shader.version_get_shader(filter.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1); @@ -1247,7 +1247,7 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f RID shader = roughness.raster_shader.version_get_shader(roughness.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); @@ -1267,7 +1267,7 @@ void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_b RD::get_singleton()->draw_command_begin_label("Merge specular"); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); int mode; if (p_reflection.is_valid()) { diff --git a/godot/servers/rendering/renderer_rd/effects/debug_effects.cpp b/godot/servers/rendering/renderer_rd/effects/debug_effects.cpp index 04afaf63..ac06f28f 100644 --- a/godot/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/godot/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -282,7 +282,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj // And draw our frustum. RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector(), 0.0, 0, rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, rect); RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline); @@ -326,7 +326,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj rect.size.x *= atlas_rect_norm.size.x; rect.size.y *= atlas_rect_norm.size.y; - draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector(), 0.0, 0, rect); + draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, rect); pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline); @@ -351,7 +351,7 @@ void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_f RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_velocity })); RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector({ default_sampler, p_depth })); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass())); Projection correction; diff --git a/godot/servers/rendering/renderer_rd/effects/fsr2.cpp b/godot/servers/rendering/renderer_rd/effects/fsr2.cpp index 551ea5dd..294754f7 100644 --- a/godot/servers/rendering/renderer_rd/effects/fsr2.cpp +++ b/godot/servers/rendering/renderer_rd/effects/fsr2.cpp @@ -235,6 +235,7 @@ static FfxErrorCode create_resource_rd(FfxFsr2Interface *p_backend_interface, co texture_format.height = res_desc.height; texture_format.depth = res_desc.depth; texture_format.mipmaps = res_desc.mipCount; + texture_format.is_discardable = true; RID texture = rd->texture_create(texture_format, RD::TextureView(), initial_data); ERR_FAIL_COND_V(texture.is_null(), FFX_ERROR_BACKEND_API_ERROR); diff --git a/godot/servers/rendering/renderer_rd/effects/luminance.cpp b/godot/servers/rendering/renderer_rd/effects/luminance.cpp index 61b2248b..4727e8fd 100644 --- a/godot/servers/rendering/renderer_rd/effects/luminance.cpp +++ b/godot/servers/rendering/renderer_rd/effects/luminance.cpp @@ -184,7 +184,7 @@ void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] })); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); if (final) { diff --git a/godot/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/godot/servers/rendering/renderer_rd/effects/tone_mapper.cpp index e943071f..83cf2ed7 100644 --- a/godot/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/godot/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -166,7 +166,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass())); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); diff --git a/godot/servers/rendering/renderer_rd/effects/vrs.cpp b/godot/servers/rendering/renderer_rd/effects/vrs.cpp index 94453bf9..9cc22f6f 100644 --- a/godot/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/godot/servers/rendering/renderer_rd/effects/vrs.cpp @@ -94,7 +94,7 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(VRSPushConstant)); diff --git a/godot/servers/rendering/renderer_rd/environment/gi.cpp b/godot/servers/rendering/renderer_rd/environment/gi.cpp index 235aa9f8..c4a89996 100644 --- a/godot/servers/rendering/renderer_rd/environment/gi.cpp +++ b/godot/servers/rendering/renderer_rd/environment/gi.cpp @@ -1721,7 +1721,7 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con SDFGIShader::ProbeDebugMode mode = p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : SDFGIShader::PROBE_DEBUG_PROBES; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer); RD::get_singleton()->draw_command_begin_label("Debug SDFGI"); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); diff --git a/godot/servers/rendering/renderer_rd/environment/sky.cpp b/godot/servers/rendering/renderer_rd/environment/sky.cpp index 83ec52ea..c5933262 100644 --- a/godot/servers/rendering/renderer_rd/environment/sky.cpp +++ b/godot/servers/rendering/renderer_rd/environment/sky.cpp @@ -1315,7 +1315,7 @@ void SkyRD::update_radiance_buffers(Ref p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i]); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1336,7 +1336,7 @@ void SkyRD::update_radiance_buffers(Ref p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i]); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1353,7 +1353,7 @@ void SkyRD::update_radiance_buffers(Ref p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector(), 1.0, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i)); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::SKY_PASS | uint32_t(i)); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1477,7 +1477,7 @@ void SkyRD::update_res_buffers(Ref p_render_buffers, RID p Vector clear_colors; clear_colors.push_back(Color(0.0, 0.0, 0.0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 0.0); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors); _render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1496,7 +1496,7 @@ void SkyRD::update_res_buffers(Ref p_render_buffers, RID p Vector clear_colors; clear_colors.push_back(Color(0.0, 0.0, 0.0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors, 0.0); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_CLEAR_ALL, clear_colors); _render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } diff --git a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index b867e844..fbe19dfe 100644 --- a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -643,11 +643,11 @@ void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_lis } } -void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { +void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField p_draw_flags, const Vector &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer); p_params->framebuffer_format = fb_format; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_draw_flags, p_clear_color_values, p_clear_depth_value, p_clear_stencil_value, p_region); _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); RD::get_singleton()->draw_list_end(); } @@ -1476,7 +1476,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo if (p_render_data->directional_shadows.size()) { //open the pass for directional shadows light_storage->update_directional_shadow_atlas(); - RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, Vector(), 0.0); + RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::DRAW_CLEAR_DEPTH, Vector(), 0.0f); RD::get_singleton()->draw_list_end(); } } @@ -2010,7 +2010,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } if (needs_pre_resolve) { //pre clear the depth framebuffer, as AMD (and maybe others?) use compute for it, and barrier other compute shaders. - RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pass_clear, 0.0); + RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::DRAW_CLEAR_ALL, depth_pass_clear, 0.0f); RD::get_singleton()->draw_list_end(); //start compute processes here, so they run at the same time as depth pre-pass _post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); @@ -2022,7 +2022,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi || ce_pre_opaque_resolved_depth || ce_post_opaque_resolved_depth; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization); - _render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector() : depth_pass_clear); + _render_list_with_draw_list(&render_list_params, depth_framebuffer, RD::DrawFlags(needs_pre_resolve ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_ALL), depth_pass_clear, 0.0f); RD::get_singleton()->draw_command_end_label(); @@ -2084,7 +2084,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co { Vector c; - { + if (!load_color) { Color cc = clear_color.srgb_to_linear(); if (using_separate_specular || rb_data.is_valid()) { // Effects that rely on separate specular, like subsurface scattering, must clear the alpha to zero. @@ -2101,7 +2101,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags; RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization); - _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0); + _render_list_with_draw_list(&render_list_params, opaque_framebuffer, RD::DrawFlags(load_color ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_COLOR_ALL) | (depth_pre_pass ? RD::DRAW_DEFAULT_ALL : RD::DRAW_CLEAR_DEPTH), c, 0.0f); } RD::get_singleton()->draw_command_end_label(); @@ -2109,7 +2109,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (using_motion_pass) { Vector motion_vector_clear_colors; motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0)); - RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, motion_vector_clear_colors); + RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::DRAW_CLEAR_ALL, motion_vector_clear_colors); RD::get_singleton()->draw_list_end(); } @@ -2121,7 +2121,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true); RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization); - _render_list_with_draw_list(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + _render_list_with_draw_list(&render_list_params, color_framebuffer); RD::get_singleton()->draw_command_end_label(); } @@ -2148,7 +2148,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co Projection dc; dc.set_depth_correction(true); Projection cm = (dc * p_render_data->scene_data->cam_projection) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer); RD::get_singleton()->draw_command_begin_label("Debug VoxelGIs"); for (int i = 0; i < (int)p_render_data->voxel_gi_instances->size(); i++) { gi.debug_voxel_gi((*p_render_data->voxel_gi_instances)[i], draw_list, color_only_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION, 1.0); @@ -2171,7 +2171,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("Render Sky"); RD::get_singleton()->draw_command_begin_label("Draw Sky"); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer); sky.draw_sky(draw_list, rb, p_render_data->environment, color_only_framebuffer, time, sky_energy_multiplier); @@ -2236,7 +2236,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("Clear Separate Specular (Canvas Background Mode)"); Vector blank_clear_color; blank_clear_color.push_back(Color(0.0, 0.0, 0.0)); - RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, blank_clear_color); + RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::DRAW_CLEAR_ALL, blank_clear_color); RD::get_singleton()->draw_list_end(); } @@ -2300,7 +2300,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count, 0, base_specialization); - _render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); + _render_list_with_draw_list(&render_list_params, alpha_framebuffer); } RD::get_singleton()->draw_command_end_label(); @@ -2699,7 +2699,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; shadow_pass.framebuffer = p_framebuffer; - shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD); + shadow_pass.clear_depth = p_begin || p_clear_region; shadow_pass.rect = p_rect; scene_state.shadow_passes.push_back(shadow_pass); @@ -2723,7 +2723,7 @@ void RenderForwardClustered::_render_shadow_end() { for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) { RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from); - _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector(), 0.0, 0, shadow_pass.rect); + _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, shadow_pass.clear_depth ? RD::DRAW_CLEAR_DEPTH : RD::DRAW_DEFAULT_ALL, Vector(), 0.0f, 0, shadow_pass.rect); } RD::get_singleton()->draw_command_end_label(); @@ -2770,7 +2770,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con { //regular forward for now RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, 0, true, false, rp_uniform_set); - _render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); + _render_list_with_draw_list(&render_list_params, p_fb); } RD::get_singleton()->draw_command_end_label(); } @@ -2824,7 +2824,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); } @@ -2874,7 +2874,7 @@ void RenderForwardClustered::_render_uv2(const PagedArraydraw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); const int uv_offset_count = 9; static const Vector2 uv_offsets[uv_offset_count] = { @@ -2983,7 +2983,7 @@ void RenderForwardClustered::_render_sdfgi(Ref p_render_bu } RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false); - _render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector(), 0.0, 0, Rect2()); + _render_list_with_draw_list(&render_list_params, E->value); } RD::get_singleton()->draw_command_end_label(); diff --git a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index ebc291e8..728164c3 100644 --- a/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/godot/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -350,7 +350,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct ShadowPass { uint32_t element_from; uint32_t element_count; - bool flip_cull; PassMode pass_mode; RID rp_uniform_set; @@ -358,8 +357,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { float screen_mesh_lod_threshold; RID framebuffer; - RD::InitialAction initial_depth_action; Rect2i rect; + bool clear_depth; + bool flip_cull; }; LocalVector shadow_passes; @@ -385,7 +385,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { template _FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector &p_clear_color_values = Vector(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); + void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField p_draw_flags = RD::DRAW_DEFAULT_ALL, const Vector &p_clear_color_values = Vector(), float p_clear_depth_value = 0.0, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2()); void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); diff --git a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 411f0fe6..ef08c83c 100644 --- a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -689,7 +689,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { if (p_render_data->directional_shadows.size()) { //open the pass for directional shadows light_storage->update_directional_shadow_atlas(); - RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, Vector(), 0.0); + RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::DRAW_CLEAR_DEPTH, Vector(), 0.0f); RD::get_singleton()->draw_list_end(); } } @@ -1051,7 +1051,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color // Set clear colors. Vector c; - { + if (!load_color) { Color cc = clear_color.srgb_to_linear() * inverse_luminance_multiplier; if (rb_data.is_valid()) { cc.a = 0; // For transparent viewport backgrounds. @@ -1068,7 +1068,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 0.0, 0, Rect2(), breadcrumb); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::DRAW_CLEAR_DEPTH : (RD::DRAW_CLEAR_COLOR_0 | RD::DRAW_CLEAR_DEPTH), c, 0.0f, 0, Rect2(), breadcrumb); RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); if (copy_canvas) { @@ -1165,7 +1165,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color render_list_params.framebuffer_format = fb_format; render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector(), 0, 0, Rect2(), breadcrumb); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::DRAW_DEFAULT_ALL, Vector(), 1.0f, 0, Rect2(), breadcrumb); _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); @@ -1445,7 +1445,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; shadow_pass.framebuffer = p_framebuffer; - shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD); + shadow_pass.clear_depth = p_begin || p_clear_region; shadow_pass.rect = p_rect; scene_state.shadow_passes.push_back(shadow_pass); @@ -1469,7 +1469,7 @@ void RenderForwardMobile::_render_shadow_end() { for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) { RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, scene_shader.default_specialization, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from); - _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector(), 0.0, 0, shadow_pass.rect); + _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, shadow_pass.clear_depth ? RD::DRAW_CLEAR_DEPTH : RD::DRAW_DEFAULT_ALL, Vector(), 0.0f, 0, shadow_pass.rect); } RD::get_singleton()->draw_command_end_label(); @@ -1521,7 +1521,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c Color(0, 0, 0, 0), Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); } @@ -1567,7 +1567,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 0.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::DRAW_CLEAR_ALL, clear, 0.0f, 0, p_region); const int uv_offset_count = 9; static const Vector2 uv_offsets[uv_offset_count] = { @@ -1642,7 +1642,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const { //regular forward for now RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, scene_shader.default_specialization); - _render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); + _render_list_with_draw_list(&render_list_params, p_fb); } RD::get_singleton()->draw_command_end_label(); } @@ -2094,11 +2094,11 @@ void RenderForwardMobile::_render_list(RenderingDevice::DrawListID p_draw_list, } } -void RenderForwardMobile::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { +void RenderForwardMobile::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField p_draw_flags, const Vector &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer); p_params->framebuffer_format = fb_format; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_draw_flags, p_clear_color_values, p_clear_depth_value, p_clear_stencil_value, p_region); _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); RD::get_singleton()->draw_list_end(); } diff --git a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 96f535ef..5a4c1140 100644 --- a/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/godot/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -257,8 +257,8 @@ class RenderForwardMobile : public RendererSceneRenderRD { float screen_mesh_lod_threshold; RID framebuffer; - RD::InitialAction initial_depth_action; Rect2i rect; + bool clear_depth; }; LocalVector shadow_passes; @@ -332,7 +332,7 @@ class RenderForwardMobile : public RendererSceneRenderRD { template _FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector &p_clear_color_values = Vector(), float p_clear_depth = 0.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); + void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, BitField p_clear_colors = RD::DRAW_DEFAULT_ALL, const Vector &p_clear_color_values = Vector(), float p_clear_depth_value = 0.0, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2()); RenderList render_list[RENDER_LIST_MAX]; diff --git a/godot/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/godot/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 165168cb..72170575 100644 --- a/godot/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/godot/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -975,6 +975,7 @@ void RendererCanvasRenderRD::_update_shadow_atlas() { tf.height = state.max_lights_per_render * 2; tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; tf.format = RD::DATA_FORMAT_D32_SFLOAT; + tf.is_discardable = true; //chunks to write state.shadow_depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); fb_textures.push_back(state.shadow_depth_texture); @@ -1026,7 +1027,7 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, for (int i = 0; i < 4; i++) { Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect); ShadowRenderPushConstant push_constant; for (int y = 0; y < 4; y++) { @@ -1097,7 +1098,7 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh cc.push_back(Color(1, 1, 1, 1)); Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::DRAW_CLEAR_ALL, cc, 1.0f, 0, rect); Projection projection; projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); @@ -1167,7 +1168,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan Vector cc; cc.push_back(Color(0, 0, 0, 0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::DRAW_CLEAR_ALL, cc); Projection projection; @@ -2174,7 +2175,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors, 1, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::DRAW_CLEAR_COLOR_0 : RD::DRAW_DEFAULT_ALL, clear_colors, 1.0f, 0, Rect2(), RDD::BreadcrumbMarker::UI_PASS); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); diff --git a/godot/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/godot/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 4417f683..7ef42aa7 100644 --- a/godot/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/godot/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -646,7 +646,7 @@ void RendererSceneRenderRD::_render_buffers_post_process_and_tonemap(const Rende if (use_intermediate_fb) { // If we use FSR to upscale we need to write our result into an intermediate buffer. // Note that this is cached so we only create the texture the first time. - RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT); + RID dest_texture = rb->create_texture(SNAME("Tonemapper"), SNAME("destination"), _render_buffers_get_color_format(), RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT, RD::TEXTURE_SAMPLES_1, Size2i(), 0, 1, true, true); dest_fb = FramebufferCacheRD::get_singleton()->get_cache(dest_texture); } else { // If we do a bilinear upscale we just render into our render target and our shader will upscale automatically. diff --git a/godot/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl b/godot/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl index fb35d3cd..553637dc 100644 --- a/godot/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl +++ b/godot/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl @@ -152,10 +152,10 @@ void main() { float depth_scale; if (params.orthogonal) { - depth = ((depth + (params.camera_z_far + params.camera_z_near) / (params.camera_z_far - params.camera_z_near)) * (params.camera_z_far - params.camera_z_near)) / 2.0; + depth = -(depth * (params.camera_z_far - params.camera_z_near) - (params.camera_z_far + params.camera_z_near)) / 2.0; depth_scale = params.unit_size; //remember depth is negative by default in OpenGL } else { - depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near - depth * (params.camera_z_far - params.camera_z_near)); + depth = 2.0 * params.camera_z_near * params.camera_z_far / (params.camera_z_far + params.camera_z_near + depth * (params.camera_z_far - params.camera_z_near)); depth_scale = params.unit_size / depth; //remember depth is negative by default in OpenGL } diff --git a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc index 75b9ee2d..dc49366d 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc +++ b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc @@ -54,6 +54,10 @@ RID RenderSceneBuffersRD::_get_velocity_layer_compat_80214(const uint32_t p_laye return _get_velocity_layer(p_layer, msaa_3d != RS::VIEWPORT_MSAA_DISABLED); } +RID RenderSceneBuffersRD::_create_texture_bind_compat_98670(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique) { + return create_texture(p_context, p_texture_name, p_data_format, p_usage_bits, p_texture_samples, p_size, p_layers, p_mipmaps, p_unique, false); +} + void RenderSceneBuffersRD::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("get_color_texture"), &RenderSceneBuffersRD::_get_color_texture_compat_80214); ClassDB::bind_compatibility_method(D_METHOD("get_color_layer", "layer"), &RenderSceneBuffersRD::_get_color_layer_compat_80214); @@ -61,6 +65,8 @@ void RenderSceneBuffersRD::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("get_depth_layer", "layer"), &RenderSceneBuffersRD::_get_depth_layer_compat_80214); ClassDB::bind_compatibility_method(D_METHOD("get_velocity_texture"), &RenderSceneBuffersRD::_get_velocity_texture_compat_80214); ClassDB::bind_compatibility_method(D_METHOD("get_velocity_layer", "layer"), &RenderSceneBuffersRD::_get_velocity_layer_compat_80214); + + ClassDB::bind_compatibility_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::_create_texture_bind_compat_98670); } #endif // DISABLE_DEPRECATED diff --git a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index ca44f4dd..6e1259ea 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -48,7 +48,7 @@ RenderSceneBuffersRD::~RenderSceneBuffersRD() { void RenderSceneBuffersRD::_bind_methods() { ClassDB::bind_method(D_METHOD("has_texture", "context", "name"), &RenderSceneBuffersRD::has_texture); - ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique"), &RenderSceneBuffersRD::create_texture); + ClassDB::bind_method(D_METHOD("create_texture", "context", "name", "data_format", "usage_bits", "texture_samples", "size", "layers", "mipmaps", "unique", "discardable"), &RenderSceneBuffersRD::create_texture); ClassDB::bind_method(D_METHOD("create_texture_from_format", "context", "name", "format", "view", "unique"), &RenderSceneBuffersRD::_create_texture_from_format); ClassDB::bind_method(D_METHOD("create_texture_view", "context", "name", "view_name", "view"), &RenderSceneBuffersRD::_create_texture_view); ClassDB::bind_method(D_METHOD("get_texture", "context", "name"), &RenderSceneBuffersRD::get_texture); @@ -179,8 +179,8 @@ void RenderSceneBuffersRD::configure(const RenderSceneBuffersConfiguration *p_co texture_samples = RD::TEXTURE_SAMPLES_1; } else { texture_samples = msaa_to_samples(msaa_3d); - create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, base_data_format, get_color_usage_bits(false, true, can_be_storage), texture_samples); - create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, get_depth_format(false, true, can_be_storage), get_depth_usage_bits(false, true, can_be_storage), texture_samples); + create_texture(RB_SCOPE_BUFFERS, RB_TEX_COLOR_MSAA, base_data_format, get_color_usage_bits(false, true, can_be_storage), texture_samples, Size2i(), 0, 1, true, true); + create_texture(RB_SCOPE_BUFFERS, RB_TEX_DEPTH_MSAA, get_depth_format(false, true, can_be_storage), get_depth_usage_bits(false, true, can_be_storage), texture_samples, Size2i(), 0, 1, true, true); } // VRS (note, our vrs object will only be set if VRS is supported) @@ -242,7 +242,7 @@ bool RenderSceneBuffersRD::has_texture(const StringName &p_context, const String return named_textures.has(key); } -RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique) { +RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique, bool p_discardable) { // Keep some useful data, we use default values when these are 0. Size2i size = p_size == Size2i(0, 0) ? internal_size : p_size; uint32_t layers = p_layers == 0 ? view_count : p_layers; @@ -262,6 +262,7 @@ RID RenderSceneBuffersRD::create_texture(const StringName &p_context, const Stri tf.mipmaps = mipmaps; tf.usage_bits = p_usage_bits; tf.samples = p_texture_samples; + tf.is_discardable = p_discardable; return create_texture_from_format(p_context, p_texture_name, tf, RD::TextureView(), p_unique); } diff --git a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index 187dbab4..0af3a752 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/godot/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -195,7 +195,7 @@ class RenderSceneBuffersRD : public RenderSceneBuffers { // Named Textures bool has_texture(const StringName &p_context, const StringName &p_texture_name) const; - RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true); + RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true, bool p_discardable = false); RID create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view = RD::TextureView(), bool p_unique = true); RID create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, RD::TextureView p_view = RD::TextureView()); RID get_texture(const StringName &p_context, const StringName &p_texture_name) const; @@ -423,6 +423,8 @@ class RenderSceneBuffersRD : public RenderSceneBuffers { RID _get_velocity_texture_compat_80214(); RID _get_velocity_layer_compat_80214(const uint32_t p_layer); + RID _create_texture_bind_compat_98670(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples, const Size2i p_size, const uint32_t p_layers, const uint32_t p_mipmaps, bool p_unique); + static void _bind_compatibility_methods(); #endif // DISABLE_DEPRECATED diff --git a/godot/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/godot/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 8a9499df..209be3d8 100644 --- a/godot/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/godot/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -2894,7 +2894,7 @@ void TextureStorage::update_decal_atlas() { Vector cc; cc.push_back(clear_color); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, cc); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::DRAW_CLEAR_ALL, cc); for (const KeyValue &E : decal_atlas.textures) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(E.key); @@ -3575,7 +3575,7 @@ void TextureStorage::render_target_do_msaa_resolve(RID p_render_target) { if (!rt->msaa_needs_resolve) { return; } - RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_begin(rt->get_framebuffer()); RD::get_singleton()->draw_list_end(); rt->msaa_needs_resolve = false; } @@ -3692,7 +3692,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { } Vector clear_colors; clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color); - RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::DRAW_CLEAR_ALL, clear_colors); RD::get_singleton()->draw_list_end(); rt->clear_requested = false; rt->msaa_needs_resolve = false; diff --git a/godot/servers/rendering/rendering_device.compat.inc b/godot/servers/rendering/rendering_device.compat.inc index 77e44bbc..11aede71 100644 --- a/godot/servers/rendering/rendering_device.compat.inc +++ b/godot/servers/rendering/rendering_device.compat.inc @@ -86,11 +86,29 @@ RenderingDevice::FinalAction RenderingDevice::_convert_final_action_84976(FinalA } RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_84976(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray &p_storage_textures) { - return draw_list_begin(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); + return _draw_list_begin_bind_compat_98670(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); } RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { - return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); + return _draw_list_begin_bind_compat_98670(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, RDD::BreadcrumbMarker::NONE); +} + +RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_98670(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, uint32_t p_breadcrumb) { + // Deduce flags from initial and final actions. + BitField draw_flags = RD::DRAW_DEFAULT_ALL; + if (p_initial_color_action == RD::INITIAL_ACTION_CLEAR) { + draw_flags.set_flag(DrawFlags(RD::DRAW_CLEAR_COLOR_ALL)); + } else if (p_initial_color_action == RD::INITIAL_ACTION_DISCARD) { + draw_flags.set_flag(DrawFlags(RD::DRAW_IGNORE_COLOR_ALL)); + } + + if (p_initial_depth_action == RD::INITIAL_ACTION_CLEAR) { + draw_flags.set_flag(DrawFlags(RD::DRAW_CLEAR_DEPTH | RD::DRAW_CLEAR_STENCIL)); + } else if (p_initial_depth_action == RD::INITIAL_ACTION_DISCARD) { + draw_flags.set_flag(DrawFlags(RD::DRAW_IGNORE_DEPTH | RD::DRAW_IGNORE_STENCIL)); + } + + return draw_list_begin(p_framebuffer, draw_flags, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_breadcrumb); } RenderingDevice::ComputeListID RenderingDevice::_compute_list_begin_bind_compat_84976(bool p_allow_draw_overlap) { @@ -146,6 +164,8 @@ void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("screen_get_framebuffer_format"), &RenderingDevice::_screen_get_framebuffer_format_bind_compat_87340); ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::_draw_list_begin_bind_compat_90993, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2())); + + ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::_draw_list_begin_bind_compat_98670, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0)); } #endif diff --git a/godot/servers/rendering/rendering_device.cpp b/godot/servers/rendering/rendering_device.cpp index e9fa3847..0761af92 100644 --- a/godot/servers/rendering/rendering_device.cpp +++ b/godot/servers/rendering/rendering_device.cpp @@ -846,6 +846,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture texture.base_mipmap = 0; texture.base_layer = 0; texture.is_resolve_buffer = format.is_resolve_buffer; + texture.is_discardable = format.is_discardable; texture.usage_flags = format.usage_bits & ~forced_usage_bits; texture.samples = format.samples; texture.allowed_shared_formats = format.shareable_formats; @@ -949,6 +950,7 @@ RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with tracker->texture_size = Size2i(texture.width, texture.height); tracker->texture_subresources = texture.barrier_range(); tracker->texture_usage = alias_format.usage_bits; + tracker->is_discardable = texture.is_discardable; tracker->reference_count = 1; texture.shared_fallback->texture_tracker = tracker; texture.shared_fallback->revision = 0; @@ -1129,6 +1131,7 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, tracker->texture_size = Size2i(texture.width, texture.height); tracker->texture_subresources = slice_range; tracker->texture_usage = slice_format.usage_bits; + tracker->is_discardable = slice_format.is_discardable; tracker->reference_count = 1; texture.shared_fallback->texture_tracker = tracker; texture.shared_fallback->revision = 0; @@ -1889,6 +1892,7 @@ RD::TextureFormat RenderingDevice::texture_get_format(RID p_texture) { tf.usage_bits = tex->usage_flags; tf.shareable_formats = tex->allowed_shared_formats; tf.is_resolve_buffer = tex->is_resolve_buffer; + tf.is_discardable = tex->is_discardable; return tf; } @@ -2029,6 +2033,32 @@ Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_ return OK; } +void RenderingDevice::texture_set_discardable(RID p_texture, bool p_discardable) { + ERR_RENDER_THREAD_GUARD(); + + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL(texture); + + texture->is_discardable = p_discardable; + + if (texture->draw_tracker != nullptr) { + texture->draw_tracker->is_discardable = p_discardable; + } + + if (texture->shared_fallback != nullptr && texture->shared_fallback->texture_tracker != nullptr) { + texture->shared_fallback->texture_tracker->is_discardable = p_discardable; + } +} + +bool RenderingDevice::texture_is_discardable(RID p_texture) { + ERR_RENDER_THREAD_GUARD_V(false); + + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(texture, false); + + return texture->is_discardable; +} + Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers) { ERR_RENDER_THREAD_GUARD_V(ERR_UNAVAILABLE); @@ -2082,31 +2112,7 @@ bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format, /**** FRAMEBUFFER ****/ /*********************/ -static RDD::AttachmentLoadOp initial_action_to_load_op(RenderingDevice::InitialAction p_action) { - switch (p_action) { - case RenderingDevice::INITIAL_ACTION_LOAD: - return RDD::ATTACHMENT_LOAD_OP_LOAD; - case RenderingDevice::INITIAL_ACTION_CLEAR: - return RDD::ATTACHMENT_LOAD_OP_CLEAR; - case RenderingDevice::INITIAL_ACTION_DISCARD: - return RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - default: - ERR_FAIL_V_MSG(RDD::ATTACHMENT_LOAD_OP_DONT_CARE, "Invalid initial action value (" + itos(p_action) + ")"); - } -} - -static RDD::AttachmentStoreOp final_action_to_store_op(RenderingDevice::FinalAction p_action) { - switch (p_action) { - case RenderingDevice::FINAL_ACTION_STORE: - return RDD::ATTACHMENT_STORE_OP_STORE; - case RenderingDevice::FINAL_ACTION_DISCARD: - return RDD::ATTACHMENT_STORE_OP_DONT_CARE; - default: - ERR_FAIL_V_MSG(RDD::ATTACHMENT_STORE_OP_DONT_CARE, "Invalid final action value (" + itos(p_action) + ")"); - } -} - -RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector &p_attachments, const Vector &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector *r_samples) { +RDD::RenderPassID RenderingDevice::_render_pass_create(RenderingDeviceDriver *p_driver, const Vector &p_attachments, const Vector &p_passes, VectorView p_load_ops, VectorView p_store_ops, uint32_t p_view_count, Vector *r_samples) { // NOTE: // Before the refactor to RenderingDevice-RenderingDeviceDriver, there was commented out code to // specify dependencies to external subpasses. Since it had been unused for a long timel it wasn't ported @@ -2116,7 +2122,7 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector 1) { - const RDD::MultiviewCapabilities &capabilities = driver->get_multiview_capabilities(); + const RDD::MultiviewCapabilities &capabilities = p_driver->get_multiview_capabilities(); // This only works with multiview! ERR_FAIL_COND_V_MSG(!capabilities.is_supported, RDD::RenderPassID(), "Multiview not supported"); @@ -2157,17 +2163,17 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vectorrender_pass_create(attachments, subpasses, subpass_dependencies, p_view_count); + RDD::RenderPassID render_pass = p_driver->render_pass_create(attachments, subpasses, subpass_dependencies, p_view_count); ERR_FAIL_COND_V(!render_pass, RDD::RenderPassID()); return render_pass; } +RDD::RenderPassID RenderingDevice::_render_pass_create_from_graph(RenderingDeviceDriver *p_driver, VectorView p_load_ops, VectorView p_store_ops, void *p_user_data) { + DEV_ASSERT(p_driver != nullptr); + DEV_ASSERT(p_user_data != nullptr); + + // The graph delegates the creation of the render pass to the user according to the load and store ops that were determined as necessary after + // resolving the dependencies between commands. This function creates a render pass for the framebuffer accordingly. + Framebuffer *framebuffer = (Framebuffer *)(p_user_data); + const FramebufferFormatKey &key = framebuffer->rendering_device->framebuffer_formats[framebuffer->format_id].E->key(); + return _render_pass_create(p_driver, key.attachments, key.passes, p_load_ops, p_store_ops, framebuffer->view_count); +} + RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create(const Vector &p_format, uint32_t p_view_count) { FramebufferPass pass; for (int i = 0; i < p_format.size(); i++) { @@ -2349,6 +2366,7 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create( passes.push_back(pass); return framebuffer_format_create_multipass(p_format, passes, p_view_count); } + RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_multipass(const Vector &p_attachments, const Vector &p_passes, uint32_t p_view_count) { _THREAD_SAFE_METHOD_ @@ -2364,14 +2382,21 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_ } Vector samples; - RDD::RenderPassID render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, p_view_count, &samples); // Actions don't matter for this use case. + LocalVector load_ops; + LocalVector store_ops; + for (int64_t i = 0; i < p_attachments.size(); i++) { + load_ops.push_back(RDD::ATTACHMENT_LOAD_OP_CLEAR); + store_ops.push_back(RDD::ATTACHMENT_STORE_OP_STORE); + } + RDD::RenderPassID render_pass = _render_pass_create(driver, p_attachments, p_passes, load_ops, store_ops, p_view_count, &samples); // Actions don't matter for this use case. if (!render_pass) { // Was likely invalid. return INVALID_ID; } - FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); + FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT)); E = framebuffer_format_cache.insert(key, id); + FramebufferFormat fb_format; fb_format.E = E; fb_format.render_pass = render_pass; @@ -2438,15 +2463,24 @@ RID RenderingDevice::framebuffer_create_empty(const Size2i &p_size, TextureSampl _THREAD_SAFE_METHOD_ Framebuffer framebuffer; + framebuffer.rendering_device = this; framebuffer.format_id = framebuffer_format_create_empty(p_samples); ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID()); framebuffer.size = p_size; framebuffer.view_count = 1; + RDG::FramebufferCache *framebuffer_cache = RDG::framebuffer_cache_create(); + framebuffer_cache->width = p_size.width; + framebuffer_cache->height = p_size.height; + framebuffer.framebuffer_cache = framebuffer_cache; + RID id = framebuffer_owner.make_rid(framebuffer); #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); #endif + + framebuffer_cache->render_pass_creation_user_data = framebuffer_owner.get_or_null(id); + return id; } @@ -2487,6 +2521,8 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector &p_texture_a _THREAD_SAFE_METHOD_ Vector attachments; + LocalVector textures; + LocalVector trackers; attachments.resize(p_texture_attachments.size()); Size2i size; bool size_set = false; @@ -2495,6 +2531,7 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector &p_texture_a Texture *texture = texture_owner.get_or_null(p_texture_attachments[i]); if (!texture) { af.usage_flags = AttachmentFormat::UNUSED_ATTACHMENT; + trackers.push_back(nullptr); } else { ERR_FAIL_COND_V_MSG(texture->layers != p_view_count, RID(), "Layers of our texture doesn't match view count for this framebuffer"); @@ -2516,6 +2553,11 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector &p_texture_a af.format = texture->format; af.samples = texture->samples; af.usage_flags = texture->usage_flags; + + _texture_make_mutable(texture, p_texture_attachments[i]); + + textures.push_back(texture->driver_id); + trackers.push_back(texture->draw_tracker); } attachments.write[i] = af; } @@ -2531,11 +2573,19 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector &p_texture_a "The format used to check this framebuffer differs from the intended framebuffer format."); Framebuffer framebuffer; + framebuffer.rendering_device = this; framebuffer.format_id = format_id; framebuffer.texture_ids = p_texture_attachments; framebuffer.size = size; framebuffer.view_count = p_view_count; + RDG::FramebufferCache *framebuffer_cache = RDG::framebuffer_cache_create(); + framebuffer_cache->width = size.width; + framebuffer_cache->height = size.height; + framebuffer_cache->textures = textures; + framebuffer_cache->trackers = trackers; + framebuffer.framebuffer_cache = framebuffer_cache; + RID id = framebuffer_owner.make_rid(framebuffer); #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); @@ -2547,6 +2597,8 @@ RID RenderingDevice::framebuffer_create_multipass(const Vector &p_texture_a } } + framebuffer_cache->render_pass_creation_user_data = framebuffer_owner.get_or_null(id); + return id; } @@ -3833,7 +3885,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS clear_value.color = p_clear_color; RDD::RenderPassID render_pass = driver->swap_chain_get_render_pass(sc_it->value); - draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS); + draw_graph.add_draw_list_begin(render_pass, fb_it->value, viewport, RDG::ATTACHMENT_OPERATION_CLEAR, clear_value, true, false, RDD::BreadcrumbMarker::BLIT_PASS); draw_graph.add_draw_list_set_viewport(viewport); draw_graph.add_draw_list_set_scissor(viewport); @@ -3841,150 +3893,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count) { - Framebuffer::VersionKey vk; - vk.initial_color_action = p_initial_color_action; - vk.final_color_action = p_final_color_action; - vk.initial_depth_action = p_initial_depth_action; - vk.final_depth_action = p_final_depth_action; - vk.view_count = p_framebuffer->view_count; - - if (!p_framebuffer->framebuffers.has(vk)) { - // Need to create this version. - Framebuffer::Version version; - - version.render_pass = _render_pass_create(framebuffer_formats[p_framebuffer->format_id].E->key().attachments, framebuffer_formats[p_framebuffer->format_id].E->key().passes, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_framebuffer->view_count); - - LocalVector attachments; - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - if (texture) { - attachments.push_back(texture->driver_id); - if (!(texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT)) { // VRS attachment will be a different size. - ERR_FAIL_COND_V(texture->width != p_framebuffer->size.width, ERR_BUG); - ERR_FAIL_COND_V(texture->height != p_framebuffer->size.height, ERR_BUG); - } - } - } - - version.framebuffer = driver->framebuffer_create(version.render_pass, attachments, p_framebuffer->size.width, p_framebuffer->size.height); - ERR_FAIL_COND_V(!version.framebuffer, ERR_CANT_CREATE); - - version.subpass_count = framebuffer_formats[p_framebuffer->format_id].E->key().passes.size(); - - p_framebuffer->framebuffers.insert(vk, version); - } - const Framebuffer::Version &version = p_framebuffer->framebuffers[vk]; - *r_framebuffer = version.framebuffer; - *r_render_pass = version.render_pass; - *r_subpass_count = version.subpass_count; - - return OK; -} - -Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, uint32_t p_breadcrumb) { - thread_local LocalVector clear_values; - thread_local LocalVector resource_trackers; - thread_local LocalVector resource_usages; - bool uses_color = false; - bool uses_depth = false; - clear_values.clear(); - clear_values.resize(p_framebuffer->texture_ids.size()); - resource_trackers.clear(); - resource_usages.clear(); - int clear_values_count = 0; - { - int color_index = 0; - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - RDD::RenderPassClearValue clear_value; - - RID texture_rid = p_framebuffer->texture_ids[i]; - Texture *texture = texture_owner.get_or_null(texture_rid); - if (!texture) { - color_index++; - continue; - } - - // Indicate the texture will get modified for the shared texture fallback. - _texture_update_shared_fallback(texture_rid, texture, true); - - if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - if (color_index < p_clear_colors.size()) { - ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. - clear_value.color = p_clear_colors[color_index]; - color_index++; - } - - resource_trackers.push_back(texture->draw_tracker); - resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE); - uses_color = true; - } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { - clear_value.depth = p_clear_depth; - clear_value.stencil = p_clear_stencil; - resource_trackers.push_back(texture->draw_tracker); - resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE); - uses_depth = true; - } - - clear_values[clear_values_count++] = clear_value; - } - } - - draw_graph.add_draw_list_begin(p_render_pass, p_framebuffer_driver_id, Rect2i(p_viewport_offset, p_viewport_size), clear_values, uses_color, uses_depth, p_breadcrumb); - draw_graph.add_draw_list_usages(resource_trackers, resource_usages); - - // Mark textures as bound. - draw_list_bound_textures.clear(); - - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - if (!texture) { - continue; - } - texture->bound = true; - draw_list_bound_textures.push_back(p_framebuffer->texture_ids[i]); - } - - return OK; -} - -void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { - LocalVector clear_attachments; - int color_index = 0; - int texture_index = 0; - for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); - - if (!texture) { - texture_index++; - continue; - } - - RDD::AttachmentClear clear_at; - if (p_clear_color && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - Color clear_color = p_clear_colors[texture_index++]; - clear_at.value.color = clear_color; - clear_at.color_attachment = color_index++; - clear_at.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; - } else if (p_clear_depth && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - clear_at.value.depth = p_depth; - clear_at.value.stencil = p_stencil; - clear_at.color_attachment = 0; - clear_at.aspect = RDD::TEXTURE_ASPECT_DEPTH_BIT; - if (format_has_stencil(texture->format)) { - clear_at.aspect.set_flag(RDD::TEXTURE_ASPECT_STENCIL_BIT); - } - } else { - ERR_CONTINUE(true); - } - clear_attachments.push_back(clear_at); - } - - Rect2i rect = Rect2i(p_viewport_offset, p_viewport_size); - draw_graph.add_draw_list_clear_attachments(clear_attachments, rect); -} - -RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, uint32_t p_breadcrumb) { +RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, BitField p_draw_flags, const Vector &p_clear_color_values, float p_clear_depth_value, uint32_t p_clear_stencil_value, const Rect2 &p_region, uint32_t p_breadcrumb) { ERR_RENDER_THREAD_GUARD_V(INVALID_ID); ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); @@ -4008,43 +3917,88 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, viewport_size = regioni.size; } - if (p_initial_color_action == INITIAL_ACTION_CLEAR) { // Check clear values. - int color_count = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - // We only check for our VRS usage bit if this is not the first texture id. - // If it is the first we're likely populating our VRS texture. - // Bit dirty but... - if (!texture || (!(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && !(i != 0 && texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT))) { - if (!texture || !texture->is_resolve_buffer) { - color_count++; - } + thread_local LocalVector operations; + thread_local LocalVector clear_values; + thread_local LocalVector resource_trackers; + thread_local LocalVector resource_usages; + bool uses_color = false; + bool uses_depth = false; + operations.resize(framebuffer->texture_ids.size()); + clear_values.resize(framebuffer->texture_ids.size()); + resource_trackers.clear(); + resource_usages.clear(); + + uint32_t color_index = 0; + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + RID texture_rid = framebuffer->texture_ids[i]; + Texture *texture = texture_owner.get_or_null(texture_rid); + if (texture == nullptr) { + operations[i] = RDG::ATTACHMENT_OPERATION_DEFAULT; + clear_values[i] = RDD::RenderPassClearValue(); + continue; + } + + // Indicate the texture will get modified for the shared texture fallback. + _texture_update_shared_fallback(texture_rid, texture, true); + + RDG::AttachmentOperation operation = RDG::ATTACHMENT_OPERATION_DEFAULT; + RDD::RenderPassClearValue clear_value; + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (p_draw_flags.has_flag(DrawFlags(DRAW_CLEAR_COLOR_0 << color_index))) { + ERR_FAIL_COND_V_MSG(color_index >= p_clear_color_values.size(), INVALID_ID, vformat("Color texture (%d) was specified to be cleared but no color value was provided.", color_index)); + operation = RDG::ATTACHMENT_OPERATION_CLEAR; + clear_value.color = p_clear_color_values[color_index]; + } else if (p_draw_flags.has_flag(DrawFlags(DRAW_IGNORE_COLOR_0 << color_index))) { + operation = RDG::ATTACHMENT_OPERATION_IGNORE; + } + + resource_trackers.push_back(texture->draw_tracker); + resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE); + uses_color = true; + color_index++; + } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + if (p_draw_flags.has_flag(DRAW_CLEAR_DEPTH) || p_draw_flags.has_flag(DRAW_CLEAR_STENCIL)) { + operation = RDG::ATTACHMENT_OPERATION_CLEAR; + clear_value.depth = p_clear_depth_value; + clear_value.stencil = p_clear_stencil_value; + } else if (p_draw_flags.has_flag(DRAW_IGNORE_DEPTH) || p_draw_flags.has_flag(DRAW_IGNORE_STENCIL)) { + operation = RDG::ATTACHMENT_OPERATION_IGNORE; } + + resource_trackers.push_back(texture->draw_tracker); + resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE); + uses_depth = true; } - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, INVALID_ID, "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer color attachments (" + itos(color_count) + ")."); + + operations[i] = operation; + clear_values[i] = clear_value; } - RDD::FramebufferID fb_driver_id; - RDD::RenderPassID render_pass; + draw_graph.add_draw_list_begin(framebuffer->framebuffer_cache, Rect2i(viewport_offset, viewport_size), operations, clear_values, uses_color, uses_depth, p_breadcrumb); + draw_graph.add_draw_list_usages(resource_trackers, resource_usages); - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); - ERR_FAIL_COND_V(err != OK, INVALID_ID); + // Mark textures as bound. + draw_list_bound_textures.clear(); - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, p_breadcrumb); + for (int i = 0; i < framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + if (texture == nullptr) { + continue; + } - if (err != OK) { - return INVALID_ID; + texture->bound = true; + draw_list_bound_textures.push_back(framebuffer->texture_ids[i]); } - draw_list_render_pass = render_pass; - draw_list_vkframebuffer = fb_driver_id; - _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0); #ifdef DEBUG_ENABLED draw_list_framebuffer_format = framebuffer->format_id; #endif draw_list_current_subpass = 0; + const FramebufferFormatKey &key = framebuffer_formats[framebuffer->format_id].E->key(); + draw_list_subpass_count = key.passes.size(); + Rect2i viewport_rect(viewport_offset, viewport_size); draw_graph.add_draw_list_set_viewport(viewport_rect); draw_graph.add_draw_list_set_scissor(viewport_rect); @@ -5443,6 +5397,7 @@ bool RenderingDevice::_texture_make_mutable(Texture *p_texture, RID p_texture_id p_texture->draw_tracker->texture_size = Size2i(p_texture->width, p_texture->height); p_texture->draw_tracker->texture_subresources = p_texture->barrier_range(); p_texture->draw_tracker->texture_usage = p_texture->usage_flags; + p_texture->draw_tracker->is_discardable = p_texture->is_discardable; p_texture->draw_tracker->reference_count = 1; if (p_texture_id.is_valid()) { @@ -5853,13 +5808,7 @@ void RenderingDevice::_free_pending_resources(int p_frame) { // Framebuffers. while (frames[p_frame].framebuffers_to_dispose_of.front()) { Framebuffer *framebuffer = &frames[p_frame].framebuffers_to_dispose_of.front()->get(); - - for (const KeyValue &E : framebuffer->framebuffers) { - // First framebuffer, then render pass because it depends on it. - driver->framebuffer_free(E.value.framebuffer); - driver->render_pass_free(E.value.render_pass); - } - + draw_graph.framebuffer_cache_free(driver, framebuffer->framebuffer_cache); frames[p_frame].framebuffers_to_dispose_of.pop_front(); } @@ -6211,7 +6160,7 @@ Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServ driver->command_buffer_begin(frames[0].command_buffer); // Create draw graph and start it initialized as well. - draw_graph.initialize(driver, device, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME); + draw_graph.initialize(driver, device, &_render_pass_create_from_graph, frames.size(), main_queue_family, SECONDARY_COMMAND_BUFFERS_PER_FRAME); draw_graph.begin(); for (uint32_t i = 0; i < frames.size(); i++) { @@ -6709,6 +6658,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_is_shared", "texture"), &RenderingDevice::texture_is_shared); ClassDB::bind_method(D_METHOD("texture_is_valid", "texture"), &RenderingDevice::texture_is_valid); + ClassDB::bind_method(D_METHOD("texture_set_discardable", "texture", "discardable"), &RenderingDevice::texture_set_discardable); + ClassDB::bind_method(D_METHOD("texture_is_discardable", "texture"), &RenderingDevice::texture_is_discardable); + ClassDB::bind_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer"), &RenderingDevice::texture_copy); ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count"), &RenderingDevice::texture_clear); ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture"), &RenderingDevice::texture_resolve_multisample); @@ -6770,7 +6722,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color())); - ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "breadcrumb"), &RenderingDevice::draw_list_begin, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "draw_flags", "clear_color_values", "clear_depth_value", "clear_stencil_value", "region", "breadcrumb"), &RenderingDevice::draw_list_begin, DEFVAL(DRAW_DEFAULT_ALL), DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(0)); #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray())); #endif @@ -7294,22 +7246,20 @@ void RenderingDevice::_bind_methods() { BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_WRITE_MASK); BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_REFERENCE); +#ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(INITIAL_ACTION_LOAD); BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR); BIND_ENUM_CONSTANT(INITIAL_ACTION_DISCARD); BIND_ENUM_CONSTANT(INITIAL_ACTION_MAX); -#ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION); BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION_CONTINUE); BIND_ENUM_CONSTANT(INITIAL_ACTION_KEEP); BIND_ENUM_CONSTANT(INITIAL_ACTION_DROP); BIND_ENUM_CONSTANT(INITIAL_ACTION_CONTINUE); -#endif BIND_ENUM_CONSTANT(FINAL_ACTION_STORE); BIND_ENUM_CONSTANT(FINAL_ACTION_DISCARD); BIND_ENUM_CONSTANT(FINAL_ACTION_MAX); -#ifndef DISABLE_DEPRECATED BIND_ENUM_CONSTANT(FINAL_ACTION_READ); BIND_ENUM_CONSTANT(FINAL_ACTION_CONTINUE); #endif @@ -7391,6 +7341,34 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(BLIT_PASS); BIND_ENUM_CONSTANT(UI_PASS); BIND_ENUM_CONSTANT(DEBUG_PASS); + + BIND_BITFIELD_FLAG(DRAW_DEFAULT_ALL); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_0); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_1); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_2); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_3); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_4); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_5); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_6); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_7); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_MASK); + BIND_BITFIELD_FLAG(DRAW_CLEAR_COLOR_ALL); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_0); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_1); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_2); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_3); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_4); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_5); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_6); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_7); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_MASK); + BIND_BITFIELD_FLAG(DRAW_IGNORE_COLOR_ALL); + BIND_BITFIELD_FLAG(DRAW_CLEAR_DEPTH); + BIND_BITFIELD_FLAG(DRAW_IGNORE_DEPTH); + BIND_BITFIELD_FLAG(DRAW_CLEAR_STENCIL); + BIND_BITFIELD_FLAG(DRAW_IGNORE_STENCIL); + BIND_BITFIELD_FLAG(DRAW_CLEAR_ALL); + BIND_BITFIELD_FLAG(DRAW_IGNORE_ALL); } void RenderingDevice::make_current() { diff --git a/godot/servers/rendering/rendering_device.h b/godot/servers/rendering/rendering_device.h index ccfe5104..4e54e4ca 100644 --- a/godot/servers/rendering/rendering_device.h +++ b/godot/servers/rendering/rendering_device.h @@ -251,6 +251,7 @@ class RenderingDevice : public RenderingDeviceCommons { Vector allowed_shared_formats; bool is_resolve_buffer = false; + bool is_discardable = false; bool has_initial_data = false; BitField read_aspect_flags; @@ -287,6 +288,7 @@ class RenderingDevice : public RenderingDeviceCommons { tf.usage_bits = usage_flags; tf.shareable_formats = allowed_shared_formats; tf.is_resolve_buffer = is_resolve_buffer; + tf.is_discardable = is_discardable; return tf; } }; @@ -349,33 +351,8 @@ class RenderingDevice : public RenderingDeviceCommons { Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers); Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture); - /************************/ - /**** DRAW LISTS (I) ****/ - /************************/ - - enum InitialAction { - INITIAL_ACTION_LOAD, - INITIAL_ACTION_CLEAR, - INITIAL_ACTION_DISCARD, - INITIAL_ACTION_MAX, -#ifndef DISABLE_DEPRECATED - INITIAL_ACTION_CLEAR_REGION = INITIAL_ACTION_CLEAR, - INITIAL_ACTION_CLEAR_REGION_CONTINUE = INITIAL_ACTION_CLEAR, - INITIAL_ACTION_KEEP = INITIAL_ACTION_LOAD, - INITIAL_ACTION_DROP = INITIAL_ACTION_DISCARD, - INITIAL_ACTION_CONTINUE = INITIAL_ACTION_LOAD, -#endif - }; - - enum FinalAction { - FINAL_ACTION_STORE, - FINAL_ACTION_DISCARD, - FINAL_ACTION_MAX, -#ifndef DISABLE_DEPRECATED - FINAL_ACTION_READ = FINAL_ACTION_STORE, - FINAL_ACTION_CONTINUE = FINAL_ACTION_STORE, -#endif - }; + void texture_set_discardable(RID p_texture, bool p_discardable); + bool texture_is_discardable(RID p_texture); /*********************/ /**** FRAMEBUFFER ****/ @@ -523,7 +500,8 @@ class RenderingDevice : public RenderingDeviceCommons { } }; - RDD::RenderPassID _render_pass_create(const Vector &p_attachments, const Vector &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count = 1, Vector *r_samples = nullptr); + static RDD::RenderPassID _render_pass_create(RenderingDeviceDriver *p_driver, const Vector &p_attachments, const Vector &p_passes, VectorView p_load_ops, VectorView p_store_ops, uint32_t p_view_count = 1, Vector *r_samples = nullptr); + static RDD::RenderPassID _render_pass_create_from_graph(RenderingDeviceDriver *p_driver, VectorView p_load_ops, VectorView p_store_ops, void *p_user_data); // This is a cache and it's never freed, it ensures // IDs for a given format are always unique. @@ -538,47 +516,13 @@ class RenderingDevice : public RenderingDeviceCommons { HashMap framebuffer_formats; struct Framebuffer { + RenderingDevice *rendering_device = nullptr; FramebufferFormatID format_id; - struct VersionKey { - InitialAction initial_color_action; - FinalAction final_color_action; - InitialAction initial_depth_action; - FinalAction final_depth_action; - uint32_t view_count; - - bool operator<(const VersionKey &p_key) const { - if (initial_color_action == p_key.initial_color_action) { - if (final_color_action == p_key.final_color_action) { - if (initial_depth_action == p_key.initial_depth_action) { - if (final_depth_action == p_key.final_depth_action) { - return view_count < p_key.view_count; - } else { - return final_depth_action < p_key.final_depth_action; - } - } else { - return initial_depth_action < p_key.initial_depth_action; - } - } else { - return final_color_action < p_key.final_color_action; - } - } else { - return initial_color_action < p_key.initial_color_action; - } - } - }; - uint32_t storage_mask = 0; Vector texture_ids; InvalidationCallback invalidated_callback = nullptr; void *invalidated_callback_userdata = nullptr; - - struct Version { - RDD::FramebufferID framebuffer; - RDD::RenderPassID render_pass; // This one is owned. - uint32_t subpass_count = 1; - }; - - RBMap framebuffers; + RDG::FramebufferCache *framebuffer_cache = nullptr; Size2 size; uint32_t view_count; }; @@ -826,6 +770,26 @@ class RenderingDevice : public RenderingDeviceCommons { BARRIER_MASK_NO_BARRIER = 0x8000, }; + enum InitialAction { + INITIAL_ACTION_LOAD, + INITIAL_ACTION_CLEAR, + INITIAL_ACTION_DISCARD, + INITIAL_ACTION_MAX, + INITIAL_ACTION_CLEAR_REGION = INITIAL_ACTION_CLEAR, + INITIAL_ACTION_CLEAR_REGION_CONTINUE = INITIAL_ACTION_CLEAR, + INITIAL_ACTION_KEEP = INITIAL_ACTION_LOAD, + INITIAL_ACTION_DROP = INITIAL_ACTION_DISCARD, + INITIAL_ACTION_CONTINUE = INITIAL_ACTION_LOAD, + }; + + enum FinalAction { + FINAL_ACTION_STORE, + FINAL_ACTION_DISCARD, + FINAL_ACTION_MAX, + FINAL_ACTION_READ = FINAL_ACTION_STORE, + FINAL_ACTION_CONTINUE = FINAL_ACTION_STORE, + }; + void barrier(BitField p_from = BARRIER_MASK_ALL_BARRIERS, BitField p_to = BARRIER_MASK_ALL_BARRIERS); void full_barrier(); void draw_command_insert_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); @@ -854,7 +818,9 @@ class RenderingDevice : public RenderingDeviceCommons { FramebufferFormatID _screen_get_framebuffer_format_bind_compat_87340() const; - DrawListID _draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values = Vector(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); + DrawListID _draw_list_begin_bind_compat_90993(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region); + + DrawListID _draw_list_begin_bind_compat_98670(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, uint32_t p_breadcrumb); #endif public: @@ -1158,8 +1124,6 @@ class RenderingDevice : public RenderingDeviceCommons { DrawList *draw_list = nullptr; uint32_t draw_list_subpass_count = 0; - RDD::RenderPassID draw_list_render_pass; - RDD::FramebufferID draw_list_vkframebuffer; #ifdef DEBUG_ENABLED FramebufferFormatID draw_list_framebuffer_format = INVALID_ID; #endif @@ -1167,16 +1131,43 @@ class RenderingDevice : public RenderingDeviceCommons { Vector draw_list_bound_textures; - void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); - Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, uint32_t p_breadcrumb); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass); void _draw_list_free(Rect2i *r_last_viewport = nullptr); public: + enum DrawFlags { + DRAW_DEFAULT_ALL = 0, + DRAW_CLEAR_COLOR_0 = (1 << 0), + DRAW_CLEAR_COLOR_1 = (1 << 1), + DRAW_CLEAR_COLOR_2 = (1 << 2), + DRAW_CLEAR_COLOR_3 = (1 << 3), + DRAW_CLEAR_COLOR_4 = (1 << 4), + DRAW_CLEAR_COLOR_5 = (1 << 5), + DRAW_CLEAR_COLOR_6 = (1 << 6), + DRAW_CLEAR_COLOR_7 = (1 << 7), + DRAW_CLEAR_COLOR_MASK = 0xFF, + DRAW_CLEAR_COLOR_ALL = DRAW_CLEAR_COLOR_MASK, + DRAW_IGNORE_COLOR_0 = (1 << 8), + DRAW_IGNORE_COLOR_1 = (1 << 9), + DRAW_IGNORE_COLOR_2 = (1 << 10), + DRAW_IGNORE_COLOR_3 = (1 << 11), + DRAW_IGNORE_COLOR_4 = (1 << 12), + DRAW_IGNORE_COLOR_5 = (1 << 13), + DRAW_IGNORE_COLOR_6 = (1 << 14), + DRAW_IGNORE_COLOR_7 = (1 << 15), + DRAW_IGNORE_COLOR_MASK = 0xFF00, + DRAW_IGNORE_COLOR_ALL = DRAW_IGNORE_COLOR_MASK, + DRAW_CLEAR_DEPTH = (1 << 16), + DRAW_IGNORE_DEPTH = (1 << 17), + DRAW_CLEAR_STENCIL = (1 << 18), + DRAW_IGNORE_STENCIL = (1 << 19), + DRAW_CLEAR_ALL = DRAW_CLEAR_COLOR_ALL | DRAW_CLEAR_DEPTH | DRAW_CLEAR_STENCIL, + DRAW_IGNORE_ALL = DRAW_IGNORE_COLOR_ALL | DRAW_IGNORE_DEPTH | DRAW_IGNORE_STENCIL + }; + DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()); - DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_color_values = Vector(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), uint32_t p_breadcrumb = 0); + DrawListID draw_list_begin(RID p_framebuffer, BitField p_draw_flags = DRAW_DEFAULT_ALL, const Vector &p_clear_color_values = Vector(), float p_clear_depth_value = 1.0f, uint32_t p_clear_stencil_value = 0, const Rect2 &p_region = Rect2(), uint32_t p_breadcrumb = 0); void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); @@ -1569,15 +1560,16 @@ VARIANT_ENUM_CAST(RenderingDevice::BlendFactor) VARIANT_ENUM_CAST(RenderingDevice::BlendOperation) VARIANT_BITFIELD_CAST(RenderingDevice::PipelineDynamicStateFlags) VARIANT_ENUM_CAST(RenderingDevice::PipelineSpecializationConstantType) -VARIANT_ENUM_CAST(RenderingDevice::InitialAction) -VARIANT_ENUM_CAST(RenderingDevice::FinalAction) VARIANT_ENUM_CAST(RenderingDevice::Limit) VARIANT_ENUM_CAST(RenderingDevice::MemoryType) VARIANT_ENUM_CAST(RenderingDevice::Features) VARIANT_ENUM_CAST(RenderingDevice::BreadcrumbMarker) +VARIANT_BITFIELD_CAST(RenderingDevice::DrawFlags); #ifndef DISABLE_DEPRECATED VARIANT_BITFIELD_CAST(RenderingDevice::BarrierMask); +VARIANT_ENUM_CAST(RenderingDevice::InitialAction) +VARIANT_ENUM_CAST(RenderingDevice::FinalAction) #endif typedef RenderingDevice RD; diff --git a/godot/servers/rendering/rendering_device_binds.h b/godot/servers/rendering/rendering_device_binds.h index 4d9b5650..89fed7ff 100644 --- a/godot/servers/rendering/rendering_device_binds.h +++ b/godot/servers/rendering/rendering_device_binds.h @@ -69,6 +69,8 @@ class RDTextureFormat : public RefCounted { RD_SETGET(RD::TextureType, texture_type) RD_SETGET(RD::TextureSamples, samples) RD_SETGET(BitField, usage_bits) + RD_SETGET(bool, is_resolve_buffer) + RD_SETGET(bool, is_discardable) void add_shareable_format(RD::DataFormat p_format) { base.shareable_formats.push_back(p_format); } void remove_shareable_format(RD::DataFormat p_format) { base.shareable_formats.erase(p_format); } @@ -84,6 +86,9 @@ class RDTextureFormat : public RefCounted { RD_BIND(Variant::INT, RDTextureFormat, texture_type); RD_BIND(Variant::INT, RDTextureFormat, samples); RD_BIND(Variant::INT, RDTextureFormat, usage_bits); + RD_BIND(Variant::BOOL, RDTextureFormat, is_resolve_buffer); + RD_BIND(Variant::BOOL, RDTextureFormat, is_discardable); + ClassDB::bind_method(D_METHOD("add_shareable_format", "format"), &RDTextureFormat::add_shareable_format); ClassDB::bind_method(D_METHOD("remove_shareable_format", "format"), &RDTextureFormat::remove_shareable_format); } diff --git a/godot/servers/rendering/rendering_device_commons.h b/godot/servers/rendering/rendering_device_commons.h index d516d968..9d01b695 100644 --- a/godot/servers/rendering/rendering_device_commons.h +++ b/godot/servers/rendering/rendering_device_commons.h @@ -373,6 +373,7 @@ class RenderingDeviceCommons : public Object { uint32_t usage_bits = 0; Vector shareable_formats; bool is_resolve_buffer = false; + bool is_discardable = false; bool operator==(const TextureFormat &b) const { if (format != b.format) { @@ -395,6 +396,10 @@ class RenderingDeviceCommons : public Object { return false; } else if (shareable_formats != b.shareable_formats) { return false; + } else if (is_resolve_buffer != b.is_resolve_buffer) { + return false; + } else if (is_discardable != b.is_discardable) { + return false; } else { return true; } diff --git a/godot/servers/rendering/rendering_device_graph.cpp b/godot/servers/rendering/rendering_device_graph.cpp index ebfe3283..b2779af6 100644 --- a/godot/servers/rendering/rendering_device_graph.cpp +++ b/godot/servers/rendering/rendering_device_graph.cpp @@ -248,6 +248,40 @@ RenderingDeviceGraph::ComputeListInstruction *RenderingDeviceGraph::_allocate_co return reinterpret_cast(&compute_instruction_list.data[compute_list_data_offset]); } +void RenderingDeviceGraph::_check_discardable_attachment_dependency(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) { + if (!p_resource_tracker->is_discardable) { + return; + } + + // Check if the command is a a draw list that clears the attachment completely. If it is, we don't need to modify the previous draw list. + uint32_t command_offset = command_data_offsets[p_command_index]; + RecordedDrawListCommand *draw_list_command = reinterpret_cast(&command_data[command_offset]); + if (draw_list_command->type == RecordedCommand::TYPE_DRAW_LIST) { + ResourceTracker **trackers = draw_list_command->trackers(); + for (uint32_t i = 0; i < draw_list_command->trackers_count; i++) { + if (trackers[i] == p_resource_tracker && draw_list_command->load_ops()[i] == RDD::ATTACHMENT_LOAD_OP_CLEAR) { + return; + } + } + } + + // Check if the previous command is a draw list. + uint32_t previous_command_offset = command_data_offsets[p_previous_command_index]; + RecordedDrawListCommand *previous_draw_list_command = reinterpret_cast(&command_data[previous_command_offset]); + if (previous_draw_list_command->type != RecordedCommand::TYPE_DRAW_LIST) { + return; + } + + // Search for the tracker inside the draw list command and modify the store operation accordingly. + ResourceTracker **trackers = previous_draw_list_command->trackers(); + for (uint32_t i = 0; i < previous_draw_list_command->trackers_count; i++) { + if (trackers[i] == p_resource_tracker) { + previous_draw_list_command->store_ops()[i] = RDD::ATTACHMENT_STORE_OP_STORE; + return; + } + } +} + void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command) { // Assign the next stages derived from the stages the command requires first. r_command->next_stages = r_command->self_stages; @@ -502,6 +536,8 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr if (write_list_node.command_index == p_command_index) { ERR_FAIL_COND_MSG(!resource_has_parent, "Command can't have itself as a dependency."); } else if (!write_list_node.partial_coverage || _check_command_intersection(resource_tracker, write_list_node.command_index, p_command_index)) { + _check_discardable_attachment_dependency(search_tracker, write_list_node.command_index, p_command_index); + // Command is dependent on this command. Add this command to the adjacency list of the write command. _add_adjacent_command(write_list_node.command_index, p_command_index, r_command); @@ -528,6 +564,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr if (search_tracker->write_command_or_list_index == p_command_index) { ERR_FAIL_MSG("Command can't have itself as a dependency."); } else { + _check_discardable_attachment_dependency(search_tracker, search_tracker->write_command_or_list_index, p_command_index); _add_adjacent_command(search_tracker->write_command_or_list_index, p_command_index, r_command); } } @@ -698,6 +735,38 @@ void RenderingDeviceGraph::_run_compute_list_command(RDD::CommandBufferID p_comm } } +void RenderingDeviceGraph::_get_draw_list_render_pass_and_framebuffer(const RecordedDrawListCommand *p_draw_list_command, RDD::RenderPassID &r_render_pass, RDD::FramebufferID &r_framebuffer) { + DEV_ASSERT(p_draw_list_command->trackers_count <= 21 && "Max number of attachments that can be encoded into the key."); + + // Build a unique key from the load and store ops for each attachment. + const RDD::AttachmentLoadOp *load_ops = p_draw_list_command->load_ops(); + const RDD::AttachmentStoreOp *store_ops = p_draw_list_command->store_ops(); + uint64_t key = 0; + for (uint32_t i = 0; i < p_draw_list_command->trackers_count; i++) { + key |= uint64_t(load_ops[i]) << (i * 3); + key |= uint64_t(store_ops[i]) << (i * 3 + 2); + } + + // Check the storage map if the render pass and the framebuffer needs to be created. + FramebufferCache *framebuffer_cache = p_draw_list_command->framebuffer_cache; + HashMap::Iterator it = framebuffer_cache->storage_map.find(key); + if (it == framebuffer_cache->storage_map.end()) { + FramebufferStorage storage; + VectorView load_ops_view(load_ops, p_draw_list_command->trackers_count); + VectorView store_ops_view(store_ops, p_draw_list_command->trackers_count); + storage.render_pass = render_pass_creation_function(driver, load_ops_view, store_ops_view, framebuffer_cache->render_pass_creation_user_data); + ERR_FAIL_COND(!storage.render_pass); + + storage.framebuffer = driver->framebuffer_create(storage.render_pass, framebuffer_cache->textures, framebuffer_cache->width, framebuffer_cache->height); + ERR_FAIL_COND(!storage.framebuffer); + + it = framebuffer_cache->storage_map.insert(key, storage); + } + + r_render_pass = it->value.render_pass; + r_framebuffer = it->value.framebuffer; +} + void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) { uint32_t instruction_data_cursor = 0; while (instruction_data_cursor < p_instruction_data_size) { @@ -805,6 +874,37 @@ void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command } } +void RenderingDeviceGraph::_add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) { + DEV_ASSERT(p_attachment_operations.size() == p_attachment_clear_values.size()); + + draw_instruction_list.clear(); + draw_instruction_list.index++; + draw_instruction_list.framebuffer_cache = p_framebuffer_cache; + draw_instruction_list.render_pass = p_render_pass; + draw_instruction_list.framebuffer = p_framebuffer; + draw_instruction_list.region = p_region; + draw_instruction_list.attachment_operations.resize(p_attachment_operations.size()); + draw_instruction_list.attachment_clear_values.resize(p_attachment_clear_values.size()); + + for (uint32_t i = 0; i < p_attachment_operations.size(); i++) { + draw_instruction_list.attachment_operations[i] = p_attachment_operations[i]; + draw_instruction_list.attachment_clear_values[i] = p_attachment_clear_values[i]; + } + + if (p_uses_color) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + } + + if (p_uses_depth) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); + } + +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + draw_instruction_list.breadcrumb = p_breadcrumb; +#endif +} + void RenderingDeviceGraph::_run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary) { driver->command_buffer_begin_secondary(p_secondary->command_buffer, p_secondary->render_pass, 0, p_secondary->framebuffer); _run_draw_list_command(p_secondary->command_buffer, p_secondary->instruction_data.ptr(), p_secondary->instruction_data.size()); @@ -825,7 +925,7 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC for (uint32_t i = 0; i < p_sorted_commands_count; i++) { const uint32_t command_index = p_sorted_commands[i].index; const uint32_t command_data_offset = command_data_offsets[command_index]; - const RecordedCommand *command = reinterpret_cast(&command_data[command_data_offset]); + const RecordedCommand *command = reinterpret_cast(&command_data[command_data_offset]); _run_label_command_change(r_command_buffer, command->label_index, p_level, false, true, &p_sorted_commands[i], p_sorted_commands_count - i, r_current_label_index, r_current_label_level); switch (command->type) { @@ -883,9 +983,20 @@ void RenderingDeviceGraph::_run_render_commands(int32_t p_level, const RecordedC #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) driver->command_insert_breadcrumb(r_command_buffer, draw_list_command->breadcrumb); #endif - driver->command_begin_render_pass(r_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); - _run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); - driver->command_end_render_pass(r_command_buffer); + RDD::RenderPassID render_pass; + RDD::FramebufferID framebuffer; + if (draw_list_command->framebuffer_cache != nullptr) { + _get_draw_list_render_pass_and_framebuffer(draw_list_command, render_pass, framebuffer); + } else { + render_pass = draw_list_command->render_pass; + framebuffer = draw_list_command->framebuffer; + } + + if (framebuffer && render_pass) { + driver->command_begin_render_pass(r_command_buffer, render_pass, framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); + _run_draw_list_command(r_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); + driver->command_end_render_pass(r_command_buffer); + } } break; case RecordedCommand::TYPE_TEXTURE_CLEAR: { const RecordedTextureClearCommand *texture_clear_command = reinterpret_cast(command); @@ -1338,9 +1449,14 @@ void RenderingDeviceGraph::_print_compute_list(const uint8_t *p_instruction_data } } -void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) { +void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Device p_device, RenderPassCreationFunction p_render_pass_creation_function, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame) { + DEV_ASSERT(p_driver != nullptr); + DEV_ASSERT(p_render_pass_creation_function != nullptr); + DEV_ASSERT(p_frame_count > 0); + driver = p_driver; device = p_device; + render_pass_creation_function = p_render_pass_creation_function; frames.resize(p_frame_count); for (uint32_t i = 0; i < p_frame_count; i++) { @@ -1566,28 +1682,12 @@ void RenderingDeviceGraph::add_compute_list_end() { _add_command_to_graph(compute_instruction_list.command_trackers.ptr(), compute_instruction_list.command_tracker_usages.ptr(), compute_instruction_list.command_trackers.size(), command_index, command); } -void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) { - draw_instruction_list.clear(); - draw_instruction_list.index++; - draw_instruction_list.render_pass = p_render_pass; - draw_instruction_list.framebuffer = p_framebuffer; - draw_instruction_list.region = p_region; -#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) - draw_instruction_list.breadcrumb = p_breadcrumb; -#endif - draw_instruction_list.clear_values.resize(p_clear_values.size()); - for (uint32_t i = 0; i < p_clear_values.size(); i++) { - draw_instruction_list.clear_values[i] = p_clear_values[i]; - } - - if (p_uses_color) { - draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); - } +void RenderingDeviceGraph::add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) { + _add_draw_list_begin(p_framebuffer_cache, RDD::RenderPassID(), RDD::FramebufferID(), p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb); +} - if (p_uses_depth) { - draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); - draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); - } +void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb) { + _add_draw_list_begin(nullptr, p_render_pass, p_framebuffer, p_region, p_attachment_operations, p_attachment_clear_values, p_uses_color, p_uses_depth, p_breadcrumb); } void RenderingDeviceGraph::add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset) { @@ -1768,51 +1868,62 @@ void RenderingDeviceGraph::add_draw_list_usages(VectorView p_ } void RenderingDeviceGraph::add_draw_list_end() { - // Arbitrary size threshold to evaluate if it'd be best to record the draw list on the background as a secondary buffer. - const uint32_t instruction_data_threshold_for_secondary = 16384; - RDD::CommandBufferType command_buffer_type; - uint32_t &secondary_buffers_used = frames[frame].secondary_command_buffers_used; - if (draw_instruction_list.data.size() > instruction_data_threshold_for_secondary && secondary_buffers_used < frames[frame].secondary_command_buffers.size()) { - // Copy the current instruction list data into another array that will be used by the secondary command buffer worker. - SecondaryCommandBuffer &secondary = frames[frame].secondary_command_buffers[secondary_buffers_used]; - secondary.render_pass = draw_instruction_list.render_pass; - secondary.framebuffer = draw_instruction_list.framebuffer; - secondary.instruction_data.resize(draw_instruction_list.data.size()); - memcpy(secondary.instruction_data.ptr(), draw_instruction_list.data.ptr(), draw_instruction_list.data.size()); - - // Run a background task for recording the secondary command buffer. - secondary.task = WorkerThreadPool::get_singleton()->add_template_task(this, &RenderingDeviceGraph::_run_secondary_command_buffer_task, &secondary, true); - - // Clear the instruction list and add a single command for executing the secondary command buffer instead. - draw_instruction_list.data.clear(); - add_draw_list_execute_commands(secondary.command_buffer); - secondary_buffers_used++; - - command_buffer_type = RDD::COMMAND_BUFFER_TYPE_SECONDARY; - } else { - command_buffer_type = RDD::COMMAND_BUFFER_TYPE_PRIMARY; - } - + FramebufferCache *framebuffer_cache = draw_instruction_list.framebuffer_cache; int32_t command_index; - uint32_t clear_values_size = sizeof(RDD::RenderPassClearValue) * draw_instruction_list.clear_values.size(); + uint32_t clear_values_size = sizeof(RDD::RenderPassClearValue) * draw_instruction_list.attachment_clear_values.size(); + uint32_t trackers_count = framebuffer_cache != nullptr ? framebuffer_cache->trackers.size() : 0; + uint32_t trackers_and_ops_size = (sizeof(ResourceTracker *) + sizeof(RDD::AttachmentLoadOp) + sizeof(RDD::AttachmentStoreOp)) * trackers_count; uint32_t instruction_data_size = draw_instruction_list.data.size(); - uint32_t command_size = sizeof(RecordedDrawListCommand) + clear_values_size + instruction_data_size; + uint32_t command_size = sizeof(RecordedDrawListCommand) + clear_values_size + trackers_and_ops_size + instruction_data_size; RecordedDrawListCommand *command = static_cast(_allocate_command(command_size, command_index)); command->type = RecordedCommand::TYPE_DRAW_LIST; command->self_stages = draw_instruction_list.stages; - command->instruction_data_size = instruction_data_size; + command->framebuffer_cache = framebuffer_cache; command->render_pass = draw_instruction_list.render_pass; command->framebuffer = draw_instruction_list.framebuffer; - command->command_buffer_type = command_buffer_type; + command->instruction_data_size = instruction_data_size; + command->command_buffer_type = RDD::COMMAND_BUFFER_TYPE_PRIMARY; command->region = draw_instruction_list.region; #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) command->breadcrumb = draw_instruction_list.breadcrumb; #endif - command->clear_values_count = draw_instruction_list.clear_values.size(); + command->clear_values_count = draw_instruction_list.attachment_clear_values.size(); + command->trackers_count = trackers_count; + + // Initialize the load and store operations to their default behaviors. The store behavior will be modified if a command depends on the result of this render pass. + uint32_t attachment_op_count = draw_instruction_list.attachment_operations.size(); + ResourceTracker **trackers = command->trackers(); + RDD::AttachmentLoadOp *load_ops = command->load_ops(); + RDD::AttachmentStoreOp *store_ops = command->store_ops(); + for (uint32_t i = 0; i < command->trackers_count; i++) { + ResourceTracker *resource_tracker = framebuffer_cache->trackers[i]; + if (resource_tracker != nullptr) { + if (i < command->clear_values_count && i < attachment_op_count && draw_instruction_list.attachment_operations[i] == ATTACHMENT_OPERATION_CLEAR) { + load_ops[i] = RDD::ATTACHMENT_LOAD_OP_CLEAR; + } else if (i < attachment_op_count && draw_instruction_list.attachment_operations[i] == ATTACHMENT_OPERATION_IGNORE) { + load_ops[i] = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else if (resource_tracker->is_discardable) { + bool resource_has_parent = resource_tracker->parent != nullptr; + ResourceTracker *search_tracker = resource_has_parent ? resource_tracker->parent : resource_tracker; + search_tracker->reset_if_outdated(tracking_frame); + bool resource_was_modified_this_frame = search_tracker->write_command_or_list_index >= 0; + load_ops[i] = resource_was_modified_this_frame ? RDD::ATTACHMENT_LOAD_OP_LOAD : RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + } else { + load_ops[i] = RDD::ATTACHMENT_LOAD_OP_LOAD; + } + + store_ops[i] = resource_tracker->is_discardable ? RDD::ATTACHMENT_STORE_OP_DONT_CARE : RDD::ATTACHMENT_STORE_OP_STORE; + } else { + load_ops[i] = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + store_ops[i] = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + } + + trackers[i] = resource_tracker; + } RDD::RenderPassClearValue *clear_values = command->clear_values(); for (uint32_t i = 0; i < command->clear_values_count; i++) { - clear_values[i] = draw_instruction_list.clear_values[i]; + clear_values[i] = draw_instruction_list.attachment_clear_values[i]; } memcpy(command->instruction_data(), draw_instruction_list.data.ptr(), instruction_data_size); @@ -2182,20 +2293,20 @@ RenderingDeviceGraph::ResourceTracker *RenderingDeviceGraph::resource_tracker_cr return memnew(ResourceTracker); } -void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *tracker) { - if (tracker == nullptr) { +void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *p_tracker) { + if (p_tracker == nullptr) { return; } - if (tracker->in_parent_dirty_list) { + if (p_tracker->in_parent_dirty_list) { // Delete the tracker from the parent's dirty linked list. - if (tracker->parent->dirty_shared_list == tracker) { - tracker->parent->dirty_shared_list = tracker->next_shared; + if (p_tracker->parent->dirty_shared_list == p_tracker) { + p_tracker->parent->dirty_shared_list = p_tracker->next_shared; } else { - ResourceTracker *node = tracker->parent->dirty_shared_list; + ResourceTracker *node = p_tracker->parent->dirty_shared_list; while (node != nullptr) { - if (node->next_shared == tracker) { - node->next_shared = tracker->next_shared; + if (node->next_shared == p_tracker) { + node->next_shared = p_tracker->next_shared; node = nullptr; } else { node = node->next_shared; @@ -2204,9 +2315,28 @@ void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *tracker) { } } - memdelete(tracker); + memdelete(p_tracker); #if PRINT_RESOURCE_TRACKER_TOTAL print_line("Resource trackers:", --resource_tracker_total); #endif } + +RenderingDeviceGraph::FramebufferCache *RenderingDeviceGraph::framebuffer_cache_create() { + return memnew(FramebufferCache); +} + +void RenderingDeviceGraph::framebuffer_cache_free(RDD *p_driver, FramebufferCache *p_cache) { + DEV_ASSERT(p_driver != nullptr); + + if (p_cache == nullptr) { + return; + } + + for (KeyValue &E : p_cache->storage_map) { + p_driver->framebuffer_free(E.value.framebuffer); + p_driver->render_pass_free(E.value.render_pass); + } + + memdelete(p_cache); +} diff --git a/godot/servers/rendering/rendering_device_graph.h b/godot/servers/rendering/rendering_device_graph.h index f7d90271..adfbb47e 100644 --- a/godot/servers/rendering/rendering_device_graph.h +++ b/godot/servers/rendering/rendering_device_graph.h @@ -176,6 +176,7 @@ class RenderingDeviceGraph { Rect2i texture_slice_or_dirty_rect; bool in_parent_dirty_list = false; bool write_command_list_enabled = false; + bool is_discardable = false; _FORCE_INLINE_ void reset_if_outdated(int64_t new_command_frame) { if (new_command_frame != command_frame) { @@ -193,6 +194,22 @@ class RenderingDeviceGraph { } }; + typedef RDD::RenderPassID (*RenderPassCreationFunction)(RenderingDeviceDriver *p_driver, VectorView p_load_ops, VectorView p_store_ops, void *p_user_data); + + struct FramebufferStorage { + RDD::FramebufferID framebuffer; + RDD::RenderPassID render_pass; + }; + + struct FramebufferCache { + uint32_t width = 0; + uint32_t height = 0; + LocalVector textures; + LocalVector trackers; + HashMap storage_map; + void *render_pass_creation_user_data = nullptr; + }; + struct CommandBufferPool { // Provided by RenderingDevice. RDD::CommandPoolID pool; @@ -207,6 +224,15 @@ class RenderingDeviceGraph { bool draw_list_found = false; }; + enum AttachmentOperation { + // Loads or ignores if the attachment is discardable. + ATTACHMENT_OPERATION_DEFAULT, + // Clear the attachment to a value. + ATTACHMENT_OPERATION_CLEAR, + // Ignore any contents from the attachment. + ATTACHMENT_OPERATION_IGNORE, + }; + private: struct InstructionList { LocalVector data; @@ -230,13 +256,16 @@ class RenderingDeviceGraph { }; struct DrawInstructionList : InstructionList { + FramebufferCache *framebuffer_cache = nullptr; RDD::RenderPassID render_pass; RDD::FramebufferID framebuffer; Rect2i region; + LocalVector attachment_operations; + LocalVector attachment_clear_values; + #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) uint32_t breadcrumb; #endif - LocalVector clear_values; }; struct RecordedCommandSort { @@ -320,15 +349,18 @@ class RenderingDeviceGraph { }; struct RecordedDrawListCommand : RecordedCommand { - uint32_t instruction_data_size = 0; - RDD::RenderPassID render_pass; + FramebufferCache *framebuffer_cache = nullptr; RDD::FramebufferID framebuffer; + RDD::RenderPassID render_pass; + uint32_t instruction_data_size = 0; RDD::CommandBufferType command_buffer_type; Rect2i region; + uint32_t clear_values_count = 0; + uint32_t trackers_count = 0; + #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) uint32_t breadcrumb = 0; #endif - uint32_t clear_values_count = 0; _FORCE_INLINE_ RDD::RenderPassClearValue *clear_values() { return reinterpret_cast(&this[1]); @@ -338,12 +370,36 @@ class RenderingDeviceGraph { return reinterpret_cast(&this[1]); } + _FORCE_INLINE_ ResourceTracker **trackers() { + return reinterpret_cast(&clear_values()[clear_values_count]); + } + + _FORCE_INLINE_ ResourceTracker *const *trackers() const { + return reinterpret_cast(&clear_values()[clear_values_count]); + } + + _FORCE_INLINE_ RDD::AttachmentLoadOp *load_ops() { + return reinterpret_cast(&trackers()[trackers_count]); + } + + _FORCE_INLINE_ const RDD::AttachmentLoadOp *load_ops() const { + return reinterpret_cast(&trackers()[trackers_count]); + } + + _FORCE_INLINE_ RDD::AttachmentStoreOp *store_ops() { + return reinterpret_cast(&load_ops()[trackers_count]); + } + + _FORCE_INLINE_ const RDD::AttachmentStoreOp *store_ops() const { + return reinterpret_cast(&load_ops()[trackers_count]); + } + _FORCE_INLINE_ uint8_t *instruction_data() { - return reinterpret_cast(&clear_values()[clear_values_count]); + return reinterpret_cast(&store_ops()[trackers_count]); } _FORCE_INLINE_ const uint8_t *instruction_data() const { - return reinterpret_cast(&clear_values()[clear_values_count]); + return reinterpret_cast(&store_ops()[trackers_count]); } }; @@ -616,6 +672,7 @@ class RenderingDeviceGraph { RDD *driver = nullptr; RenderingContextDriver::Device device; + RenderPassCreationFunction render_pass_creation_function = nullptr; int64_t tracking_frame = 0; LocalVector command_data; LocalVector command_data_offsets; @@ -660,13 +717,16 @@ class RenderingDeviceGraph { RecordedCommand *_allocate_command(uint32_t p_command_size, int32_t &r_command_index); DrawListInstruction *_allocate_draw_list_instruction(uint32_t p_instruction_size); ComputeListInstruction *_allocate_compute_list_instruction(uint32_t p_instruction_size); + void _check_discardable_attachment_dependency(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index); void _add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command); void _add_texture_barrier_to_command(RDD::TextureID p_texture_id, BitField p_src_access, BitField p_dst_access, ResourceUsage p_prev_usage, ResourceUsage p_next_usage, RDD::TextureSubresourceRange p_subresources, LocalVector &r_barrier_vector, int32_t &r_barrier_index, int32_t &r_barrier_count); #if USE_BUFFER_BARRIERS void _add_buffer_barrier_to_command(RDD::BufferID p_buffer_id, BitField p_src_access, BitField p_dst_access, int32_t &r_barrier_index, int32_t &r_barrier_count); #endif void _run_compute_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + void _get_draw_list_render_pass_and_framebuffer(const RecordedDrawListCommand *p_draw_list_command, RDD::RenderPassID &r_render_pass, RDD::FramebufferID &r_framebuffer); void _run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + void _add_draw_list_begin(FramebufferCache *p_framebuffer_cache, RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb); void _run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary); void _wait_for_secondary_command_buffer_tasks(); void _run_render_commands(int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool, int32_t &r_current_label_index, int32_t &r_current_label_level); @@ -680,7 +740,7 @@ class RenderingDeviceGraph { public: RenderingDeviceGraph(); ~RenderingDeviceGraph(); - void initialize(RDD *p_driver, RenderingContextDriver::Device p_device, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame); + void initialize(RDD *p_driver, RenderingContextDriver::Device p_device, RenderPassCreationFunction p_render_pass_creation_function, uint32_t p_frame_count, RDD::CommandQueueFamilyID p_secondary_command_queue_family, uint32_t p_secondary_command_buffers_per_frame); void finalize(); void begin(); void add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size); @@ -697,7 +757,8 @@ class RenderingDeviceGraph { void add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage); void add_compute_list_usages(VectorView p_trackers, VectorView p_usages); void add_compute_list_end(); - void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0); + void add_draw_list_begin(FramebufferCache *p_framebuffer_cache, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0); + void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView p_attachment_operations, VectorView p_attachment_clear_values, bool p_uses_color, bool p_uses_depth, uint32_t p_breadcrumb = 0); void add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset); void add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField p_pipeline_stage_bits); void add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); @@ -729,7 +790,9 @@ class RenderingDeviceGraph { void end_label(); void end(bool p_reorder_commands, bool p_full_barriers, RDD::CommandBufferID &r_command_buffer, CommandBufferPool &r_command_buffer_pool); static ResourceTracker *resource_tracker_create(); - static void resource_tracker_free(ResourceTracker *tracker); + static void resource_tracker_free(ResourceTracker *p_tracker); + static FramebufferCache *framebuffer_cache_create(); + static void framebuffer_cache_free(RDD *p_driver, FramebufferCache *p_cache); }; using RDG = RenderingDeviceGraph; diff --git a/godot/tests/core/io/test_udp_server.h b/godot/tests/core/io/test_udp_server.h new file mode 100644 index 00000000..4ba385a0 --- /dev/null +++ b/godot/tests/core/io/test_udp_server.h @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* test_udp_server.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 TEST_UDP_SERVER_H +#define TEST_UDP_SERVER_H + +#include "core/io/packet_peer_udp.h" +#include "core/io/udp_server.h" +#include "tests/test_macros.h" + +namespace TestUDPServer { + +const int PORT = 12345; +const IPAddress LOCALHOST("127.0.0.1"); +const uint32_t SLEEP_DURATION = 1000; +const uint64_t MAX_WAIT_USEC = 100000; + +Ref create_server(const IPAddress &p_address, int p_port) { + Ref server; + server.instantiate(); + + Error err = server->listen(PORT, LOCALHOST); + REQUIRE_EQ(Error::OK, err); + REQUIRE(server->is_listening()); + CHECK_FALSE(server->is_connection_available()); + CHECK_EQ(server->get_max_pending_connections(), 16); + + return server; +} + +Ref create_client(const IPAddress &p_address, int p_port) { + Ref client; + client.instantiate(); + + Error err = client->connect_to_host(LOCALHOST, PORT); + REQUIRE_EQ(Error::OK, err); + CHECK(client->is_bound()); + CHECK(client->is_socket_connected()); + + return client; +} + +Ref accept_connection(Ref &p_server) { + // Required to get the connection properly established. + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + while (!p_server->is_connection_available() && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + p_server->poll(); + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + + CHECK_EQ(p_server->poll(), Error::OK); + REQUIRE(p_server->is_connection_available()); + Ref client_from_server = p_server->take_connection(); + REQUIRE(client_from_server.is_valid()); + CHECK(client_from_server->is_bound()); + CHECK(client_from_server->is_socket_connected()); + + return client_from_server; +} + +Error poll(Ref p_server, Error p_err) { + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + Error err = p_server->poll(); + while (err != p_err && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + err = p_server->poll(); + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + return err; +} + +TEST_CASE("[UDPServer] Instantiation") { + Ref server; + server.instantiate(); + + REQUIRE(server.is_valid()); + CHECK_EQ(false, server->is_listening()); +} + +TEST_CASE("[UDPServer] Accept a connection and receive/send data") { + Ref server = create_server(LOCALHOST, PORT); + Ref client = create_client(LOCALHOST, PORT); + + // Sending data from client to server. + const String hello_world = "Hello World!"; + CHECK_EQ(client->put_var(hello_world), Error::OK); + + Variant hello_world_received; + Ref client_from_server = accept_connection(server); + CHECK_EQ(client_from_server->get_var(hello_world_received), Error::OK); + CHECK_EQ(String(hello_world_received), hello_world); + + // Sending data from server to client. + const Variant pi = 3.1415; + CHECK_EQ(client_from_server->put_var(pi), Error::OK); + + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + // get_available_packet_count() is the recommended way to call _poll(), because there is no public poll(). + while (client->get_available_packet_count() == 0 && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + server->poll(); + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + + CHECK_EQ(server->poll(), Error::OK); + CHECK_GT(client->get_available_packet_count(), 0); + + Variant pi_received; + CHECK_EQ(client->get_var(pi_received), Error::OK); + CHECK_EQ(pi_received, pi); + + client->close(); + server->stop(); + CHECK_FALSE(server->is_listening()); +} + +TEST_CASE("[UDPServer] Handle multiple clients at the same time") { + Ref server = create_server(LOCALHOST, PORT); + + Vector> clients; + for (int i = 0; i < 5; i++) { + Ref c = create_client(LOCALHOST, PORT); + + // Sending data from client to server. + const String hello_client = "Hello " + itos(i); + CHECK_EQ(c->put_var(hello_client), Error::OK); + + clients.push_back(c); + } + + for (int i = 0; i < clients.size(); i++) { + Ref cfs = accept_connection(server); + + Variant hello_world_received; + CHECK_EQ(cfs->get_var(hello_world_received), Error::OK); + CHECK_EQ(String(hello_world_received), "Hello " + itos(i)); + + // Sending data from server to client. + const Variant pi = 3.1415 + i; + CHECK_EQ(cfs->put_var(pi), Error::OK); + } + + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + for (int i = 0; i < clients.size(); i++) { + Ref c = clients[i]; + // get_available_packet_count() is the recommended way to call _poll(), because there is no public poll(). + // Because `time` is defined outside the for, we will wait the max amount of time only for the first client. + while (c->get_available_packet_count() == 0 && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + server->poll(); + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + + // The recommended way to call _poll(), because there is no public poll(). + CHECK_GT(c->get_available_packet_count(), 0); + + Variant pi_received; + const Variant pi = 3.1415 + i; + CHECK_EQ(c->get_var(pi_received), Error::OK); + CHECK_EQ(pi_received, pi); + } + + for (Ref &c : clients) { + c->close(); + } + server->stop(); +} + +TEST_CASE("[UDPServer] When stopped shouldn't accept new connections") { + Ref server = create_server(LOCALHOST, PORT); + Ref client = create_client(LOCALHOST, PORT); + + // Sending data from client to server. + const String hello_world = "Hello World!"; + CHECK_EQ(client->put_var(hello_world), Error::OK); + + Variant hello_world_received; + Ref client_from_server = accept_connection(server); + CHECK_EQ(client_from_server->get_var(hello_world_received), Error::OK); + CHECK_EQ(String(hello_world_received), hello_world); + + client->close(); + server->stop(); + CHECK_FALSE(server->is_listening()); + + Ref new_client = create_client(LOCALHOST, PORT); + CHECK_EQ(new_client->put_var(hello_world), Error::OK); + + REQUIRE_EQ(poll(server, Error::ERR_UNCONFIGURED), Error::ERR_UNCONFIGURED); + CHECK_FALSE(server->is_connection_available()); + + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + // get_available_packet_count() is the recommended way to call _poll(), because there is no public poll(). + while (new_client->get_available_packet_count() == 0 && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + + const int packet_count = new_client->get_available_packet_count(); + CHECK((packet_count == 0 || packet_count == -1)); +} + +TEST_CASE("[UDPServer] Should disconnect client") { + Ref server = create_server(LOCALHOST, PORT); + Ref client = create_client(LOCALHOST, PORT); + + // Sending data from client to server. + const String hello_world = "Hello World!"; + CHECK_EQ(client->put_var(hello_world), Error::OK); + + Variant hello_world_received; + Ref client_from_server = accept_connection(server); + CHECK_EQ(client_from_server->get_var(hello_world_received), Error::OK); + CHECK_EQ(String(hello_world_received), hello_world); + + server->stop(); + CHECK_FALSE(server->is_listening()); + CHECK_FALSE(client_from_server->is_bound()); + CHECK_FALSE(client_from_server->is_socket_connected()); + + // Sending data from client to server. + CHECK_EQ(client->put_var(hello_world), Error::OK); + + const uint64_t time = OS::get_singleton()->get_ticks_usec(); + // get_available_packet_count() is the recommended way to call _poll(), because there is no public poll(). + while (client->get_available_packet_count() == 0 && (OS::get_singleton()->get_ticks_usec() - time) < MAX_WAIT_USEC) { + OS::get_singleton()->delay_usec(SLEEP_DURATION); + } + + const int packet_count = client->get_available_packet_count(); + CHECK((packet_count == 0 || packet_count == -1)); + + client->close(); +} + +TEST_CASE("[UDPServer] Should drop new connections when pending max connection is reached") { + Ref server = create_server(LOCALHOST, PORT); + server->set_max_pending_connections(3); + + Vector> clients; + for (int i = 0; i < 5; i++) { + Ref c = create_client(LOCALHOST, PORT); + + // Sending data from client to server. + const String hello_client = "Hello " + itos(i); + CHECK_EQ(c->put_var(hello_client), Error::OK); + + clients.push_back(c); + } + + for (int i = 0; i < server->get_max_pending_connections(); i++) { + Ref cfs = accept_connection(server); + + Variant hello_world_received; + CHECK_EQ(cfs->get_var(hello_world_received), Error::OK); + CHECK_EQ(String(hello_world_received), "Hello " + itos(i)); + } + + CHECK_EQ(poll(server, Error::OK), Error::OK); + + REQUIRE_FALSE(server->is_connection_available()); + Ref client_from_server = server->take_connection(); + REQUIRE_FALSE_MESSAGE(client_from_server.is_valid(), "A Packet Peer UDP from the UDP Server should be a null pointer because the pending connection was dropped."); + + server->stop(); +} + +} // namespace TestUDPServer + +#endif // TEST_UDP_SERVER_H diff --git a/godot/tests/scene/test_camera_3d.h b/godot/tests/scene/test_camera_3d.h index 830c6672..360736b7 100644 --- a/godot/tests/scene/test_camera_3d.h +++ b/godot/tests/scene/test_camera_3d.h @@ -231,10 +231,13 @@ TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") { test_camera->set_orthogonal(5.0f, 0.5f, 1000.0f); // Center. CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f))); + CHECK(test_camera->project_position(Vector2(200, 100), test_camera->get_far()).is_equal_approx(Vector3(0, 0, -test_camera->get_far()))); // Top left. CHECK(test_camera->project_position(Vector2(0, 0), 1.5f).is_equal_approx(Vector3(-5.0f, 2.5f, -1.5f))); + CHECK(test_camera->project_position(Vector2(0, 0), test_camera->get_near()).is_equal_approx(Vector3(-5.0f, 2.5f, -test_camera->get_near()))); // Bottom right. CHECK(test_camera->project_position(Vector2(400, 200), 5.0f).is_equal_approx(Vector3(5.0f, -2.5f, -5.0f))); + CHECK(test_camera->project_position(Vector2(400, 200), test_camera->get_far()).is_equal_approx(Vector3(5.0f, -2.5f, -test_camera->get_far()))); } SUBCASE("Perspective projection") { @@ -242,12 +245,15 @@ TEST_CASE("[SceneTree][Camera3D] Project/Unproject position") { // Center. CHECK(test_camera->project_position(Vector2(200, 100), 0.5f).is_equal_approx(Vector3(0, 0, -0.5f))); CHECK(test_camera->project_position(Vector2(200, 100), 100.0f).is_equal_approx(Vector3(0, 0, -100.0f))); + CHECK(test_camera->project_position(Vector2(200, 100), test_camera->get_far()).is_equal_approx(Vector3(0, 0, -1.0f) * test_camera->get_far())); // 3/4th way to Top left. CHECK(test_camera->project_position(Vector2(100, 50), 0.5f).is_equal_approx(Vector3(-SQRT3 * 0.5f, SQRT3 * 0.25f, -0.5f))); CHECK(test_camera->project_position(Vector2(100, 50), 1.0f).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f))); + CHECK(test_camera->project_position(Vector2(100, 50), test_camera->get_near()).is_equal_approx(Vector3(-SQRT3, SQRT3 * 0.5f, -1.0f) * test_camera->get_near())); // 3/4th way to Bottom right. CHECK(test_camera->project_position(Vector2(300, 150), 0.5f).is_equal_approx(Vector3(SQRT3 * 0.5f, -SQRT3 * 0.25f, -0.5f))); CHECK(test_camera->project_position(Vector2(300, 150), 1.0f).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f))); + CHECK(test_camera->project_position(Vector2(300, 150), test_camera->get_far()).is_equal_approx(Vector3(SQRT3, -SQRT3 * 0.5f, -1.0f) * test_camera->get_far())); } } diff --git a/godot/tests/test_main.cpp b/godot/tests/test_main.cpp index 9acdc98b..a0925f9a 100644 --- a/godot/tests/test_main.cpp +++ b/godot/tests/test_main.cpp @@ -55,6 +55,7 @@ #include "tests/core/io/test_resource.h" #include "tests/core/io/test_stream_peer.h" #include "tests/core/io/test_stream_peer_buffer.h" +#include "tests/core/io/test_udp_server.h" #include "tests/core/io/test_xml_parser.h" #include "tests/core/math/test_aabb.h" #include "tests/core/math/test_astar.h" diff --git a/godot/thirdparty/README.md b/godot/thirdparty/README.md index c4200a8c..b0f03eb6 100644 --- a/godot/thirdparty/README.md +++ b/godot/thirdparty/README.md @@ -111,7 +111,7 @@ Files extracted from upstream source: ## clipper2 - Upstream: https://github.com/AngusJohnson/Clipper2 -- Version: 1.3.0 (98db5662e8dd1808a5a7b50c5605a2289bb390e8, 2023) +- Version: 1.4.0 (736ddb0b53d97fd5f65dd3d9bbf8a0993eaf387c, 2024) - License: BSL 1.0 Files extracted from upstream source: @@ -624,7 +624,7 @@ in the MSVC debugger. ## manifold - Upstream: https://github.com/elalish/manifold -- Version: master (12b43f50c44988cf3bc650df9fda180c8cc06163, 2024) +- Version: 3.0.0 (5d127e57fbfb89225a8e905d0d914ccc86c139c8, 2024) - License: Apache 2.0 File extracted from upstream source: @@ -632,6 +632,7 @@ File extracted from upstream source: - `src/` - `AUTHORS`, `LICENSE` + ## mbedtls - Upstream: https://github.com/Mbed-TLS/mbedtls diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.core.h b/godot/thirdparty/clipper2/include/clipper2/clipper.core.h index 0de7c372..0f69bf2d 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.core.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.core.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 24 November 2023 * +* Date : 12 May 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : Core Clipper Library structures and functions * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -19,6 +19,7 @@ #include #include #include +#include #include "clipper2/clipper.version.h" #define CLIPPER2_THROW(exception) std::abort() @@ -51,19 +52,19 @@ namespace Clipper2Lib // error codes (2^n) const int precision_error_i = 1; // non-fatal - const int scale_error_i = 2; // non-fatal - const int non_pair_error_i = 4; // non-fatal - const int undefined_error_i = 32; // fatal + const int scale_error_i = 2; // non-fatal + const int non_pair_error_i = 4; // non-fatal + const int undefined_error_i = 32; // fatal const int range_error_i = 64; #ifndef PI static const double PI = 3.141592653589793238; #endif -#ifdef CLIPPER2_MAX_PRECISION - const int MAX_DECIMAL_PRECISION = CLIPPER2_MAX_PRECISION; +#ifdef CLIPPER2_MAX_DECIMAL_PRECISION + const int CLIPPER2_MAX_DEC_PRECISION = CLIPPER2_MAX_DECIMAL_PRECISION; #else - const int MAX_DECIMAL_PRECISION = 8; // see Discussions #564 + const int CLIPPER2_MAX_DEC_PRECISION = 8; // see Discussions #564 #endif static const int64_t MAX_COORD = INT64_MAX >> 2; @@ -74,7 +75,7 @@ namespace Clipper2Lib static const double MAX_DBL = (std::numeric_limits::max)(); - static void DoError(int error_code) + static void DoError([[maybe_unused]] int error_code) { #if (defined(__cpp_exceptions) && __cpp_exceptions) || (defined(__EXCEPTIONS) && __EXCEPTIONS) switch (error_code) @@ -95,6 +96,13 @@ namespace Clipper2Lib #endif } + // can we call std::round on T? (default false) (#824) + template + struct is_round_invocable : std::false_type {}; + + template + struct is_round_invocable()))>> : std::true_type {}; + //By far the most widely used filling rules for polygons are EvenOdd //and NonZero, sometimes called Alternate and Winding respectively. @@ -113,8 +121,8 @@ namespace Clipper2Lib template inline void Init(const T2 x_ = 0, const T2 y_ = 0, const int64_t z_ = 0) { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) { x = static_cast(std::round(x_)); y = static_cast(std::round(y_)); @@ -143,6 +151,12 @@ namespace Clipper2Lib Init(p.x, p.y, p.z); } + template + explicit Point(const Point& p, int64_t z_) + { + Init(p.x, p.y, z_); + } + Point operator * (const double scale) const { return Point(x * scale, y * scale, z); @@ -161,8 +175,8 @@ namespace Clipper2Lib template inline void Init(const T2 x_ = 0, const T2 y_ = 0) { - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) { x = static_cast(std::round(x_)); y = static_cast(std::round(y_)); @@ -244,6 +258,14 @@ namespace Clipper2Lib (std::numeric_limits::max)(), (std::numeric_limits::max)()); + template + static inline Point MidPoint(const Point& p1, const Point& p2) + { + Point result; + result.x = (p1.x + p2.x) / 2; + result.y = (p1.y + p2.y) / 2; + return result; + } // Rect ------------------------------------------------------------------------ @@ -275,10 +297,19 @@ namespace Clipper2Lib else { left = top = (std::numeric_limits::max)(); - right = bottom = (std::numeric_limits::lowest)(); + right = bottom = std::numeric_limits::lowest(); } } + static Rect InvalidRect() + { + return { + (std::numeric_limits::max)(), + (std::numeric_limits::max)(), + std::numeric_limits::lowest(), + std::numeric_limits::lowest() }; + } + bool IsValid() const { return left != (std::numeric_limits::max)(); } T Width() const { return right - left; } @@ -329,7 +360,7 @@ namespace Clipper2Lib }; bool operator==(const Rect& other) const { - return left == other.left && right == other.right && + return left == other.left && right == other.right && top == other.top && bottom == other.bottom; } @@ -344,8 +375,8 @@ namespace Clipper2Lib { Rect result; - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) + if constexpr (std::is_integral_v && + is_round_invocable::value && !std::is_integral_v) { result.left = static_cast(std::round(rect.left * scale)); result.top = static_cast(std::round(rect.top * scale)); @@ -354,32 +385,24 @@ namespace Clipper2Lib } else { - result.left = rect.left * scale; - result.top = rect.top * scale; - result.right = rect.right * scale; - result.bottom = rect.bottom * scale; + result.left = static_cast(rect.left * scale); + result.top = static_cast(rect.top * scale); + result.right = static_cast(rect.right * scale); + result.bottom = static_cast(rect.bottom * scale); } return result; } - static const Rect64 InvalidRect64 = Rect64( - (std::numeric_limits::max)(), - (std::numeric_limits::max)(), - (std::numeric_limits::lowest)(), - (std::numeric_limits::lowest)()); - static const RectD InvalidRectD = RectD( - (std::numeric_limits::max)(), - (std::numeric_limits::max)(), - (std::numeric_limits::lowest)(), - (std::numeric_limits::lowest)()); + static const Rect64 InvalidRect64 = Rect64::InvalidRect(); + static const RectD InvalidRectD = RectD::InvalidRect(); template Rect GetBounds(const Path& path) { - auto xmin = (std::numeric_limits::max)(); - auto ymin = (std::numeric_limits::max)(); - auto xmax = std::numeric_limits::lowest(); - auto ymax = std::numeric_limits::lowest(); + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); for (const auto& p : path) { if (p.x < xmin) xmin = p.x; @@ -393,17 +416,52 @@ namespace Clipper2Lib template Rect GetBounds(const Paths& paths) { - auto xmin = (std::numeric_limits::max)(); - auto ymin = (std::numeric_limits::max)(); - auto xmax = std::numeric_limits::lowest(); - auto ymax = std::numeric_limits::lowest(); + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); for (const Path& path : paths) for (const Point& p : path) { - if (p.x < xmin) xmin = p.x; - if (p.x > xmax) xmax = p.x; - if (p.y < ymin) ymin = p.y; - if (p.y > ymax) ymax = p.y; + if (p.x < xmin) xmin = p.x; + if (p.x > xmax) xmax = p.x; + if (p.y < ymin) ymin = p.y; + if (p.y > ymax) ymax = p.y; + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + Rect GetBounds(const Path& path) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const auto& p : path) + { + if (p.x < xmin) xmin = static_cast(p.x); + if (p.x > xmax) xmax = static_cast(p.x); + if (p.y < ymin) ymin = static_cast(p.y); + if (p.y > ymax) ymax = static_cast(p.y); + } + return Rect(xmin, ymin, xmax, ymax); + } + + template + Rect GetBounds(const Paths& paths) + { + T xmin = (std::numeric_limits::max)(); + T ymin = (std::numeric_limits::max)(); + T xmax = std::numeric_limits::lowest(); + T ymax = std::numeric_limits::lowest(); + for (const Path& path : paths) + for (const Point& p : path) + { + if (p.x < xmin) xmin = static_cast(p.x); + if (p.x > xmax) xmax = static_cast(p.x); + if (p.y < ymin) ymin = static_cast(p.y); + if (p.y > ymax) ymax = static_cast(p.y); } return Rect(xmin, ymin, xmax, ymax); } @@ -431,7 +489,7 @@ namespace Clipper2Lib template - inline Path ScalePath(const Path& path, + inline Path ScalePath(const Path& path, double scale_x, double scale_y, int& error_code) { Path result; @@ -447,11 +505,11 @@ namespace Clipper2Lib result.reserve(path.size()); #ifdef USINGZ std::transform(path.begin(), path.end(), back_inserter(result), - [scale_x, scale_y](const auto& pt) + [scale_x, scale_y](const auto& pt) { return Point(pt.x * scale_x, pt.y * scale_y, pt.z); }); #else std::transform(path.begin(), path.end(), back_inserter(result), - [scale_x, scale_y](const auto& pt) + [scale_x, scale_y](const auto& pt) { return Point(pt.x * scale_x, pt.y * scale_y); }); #endif return result; @@ -465,20 +523,19 @@ namespace Clipper2Lib } template - inline Paths ScalePaths(const Paths& paths, + inline Paths ScalePaths(const Paths& paths, double scale_x, double scale_y, int& error_code) { Paths result; - if constexpr (std::numeric_limits::is_integer && - !std::numeric_limits::is_integer) + if constexpr (std::is_integral_v) { - RectD r = GetBounds(paths); + RectD r = GetBounds(paths); if ((r.left * scale_x) < min_coord || (r.right * scale_x) > max_coord || (r.top * scale_y) < min_coord || (r.bottom * scale_y) > max_coord) - { + { error_code |= range_error_i; DoError(range_error_i); return result; // empty path @@ -493,7 +550,7 @@ namespace Clipper2Lib } template - inline Paths ScalePaths(const Paths& paths, + inline Paths ScalePaths(const Paths& paths, double scale, int& error_code) { return ScalePaths(paths, scale, scale, error_code); @@ -590,20 +647,94 @@ namespace Clipper2Lib // Miscellaneous ------------------------------------------------------------ - inline void CheckPrecision(int& precision, int& error_code) + inline void CheckPrecisionRange(int& precision, int& error_code) { - if (precision >= -MAX_DECIMAL_PRECISION && precision <= MAX_DECIMAL_PRECISION) return; + if (precision >= -CLIPPER2_MAX_DEC_PRECISION && + precision <= CLIPPER2_MAX_DEC_PRECISION) return; error_code |= precision_error_i; // non-fatal error - DoError(precision_error_i); // does nothing unless exceptions enabled - precision = precision > 0 ? MAX_DECIMAL_PRECISION : -MAX_DECIMAL_PRECISION; + DoError(precision_error_i); // does nothing when exceptions are disabled + precision = precision > 0 ? CLIPPER2_MAX_DEC_PRECISION : -CLIPPER2_MAX_DEC_PRECISION; } - inline void CheckPrecision(int& precision) + inline void CheckPrecisionRange(int& precision) { int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); + } + + inline int TriSign(int64_t x) // returns 0, 1 or -1 + { + return (x > 0) - (x < 0); + } + + struct MultiplyUInt64Result + { + const uint64_t result = 0; + const uint64_t carry = 0; + + bool operator==(const MultiplyUInt64Result& other) const + { + return result == other.result && carry == other.carry; + }; + }; + + inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835 + { + const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; }; + const auto hi = [](uint64_t x) { return x >> 32; }; + + const uint64_t x1 = lo(a) * lo(b); + const uint64_t x2 = hi(a) * lo(b) + hi(x1); + const uint64_t x3 = lo(a) * hi(b) + lo(x2); + const uint64_t result = lo(x3) << 32 | lo(x1); + const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3); + + return { result, carry }; + } + + // returns true if (and only if) a * b == c * d + inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d) + { +// -- GODOT start -- +// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX +// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b); +// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d); +// return ab == cd; +// #else +// -- GODOT end -- + // nb: unsigned values needed for calculating overflow carry + const auto abs_a = static_cast(std::abs(a)); + const auto abs_b = static_cast(std::abs(b)); + const auto abs_c = static_cast(std::abs(c)); + const auto abs_d = static_cast(std::abs(d)); + + const auto abs_ab = Multiply(abs_a, abs_b); + const auto abs_cd = Multiply(abs_c, abs_d); + + // nb: it's important to differentiate 0 values here from other values + const auto sign_ab = TriSign(a) * TriSign(b); + const auto sign_cd = TriSign(c) * TriSign(d); + + return abs_ab == abs_cd && sign_ab == sign_cd; +// -- GODOT start -- +// #endif +// -- GODOT end -- + } + + template + inline bool IsCollinear(const Point& pt1, + const Point& sharedPt, const Point& pt2) // #777 + { + const auto a = sharedPt.x - pt1.x; + const auto b = pt2.y - sharedPt.y; + const auto c = sharedPt.y - pt1.y; + const auto d = pt2.x - sharedPt.x; + // When checking for collinearity with very large coordinate values + // then ProductsAreEqual is more accurate than using CrossProduct. + return ProductsAreEqual(a, b, c, d); } + template inline double CrossProduct(const Point& pt1, const Point& pt2, const Point& pt3) { return (static_cast(pt2.x - pt1.x) * static_cast(pt3.y - @@ -635,15 +766,17 @@ namespace Clipper2Lib } template - inline double DistanceFromLineSqrd(const Point& pt, const Point& ln1, const Point& ln2) + inline double PerpendicDistFromLineSqrd(const Point& pt, + const Point& line1, const Point& line2) { //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) //see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = static_cast(ln1.y - ln2.y); - double B = static_cast(ln2.x - ln1.x); - double C = A * ln1.x + B * ln1.y; - C = A * pt.x + B * pt.y - C; - return (C * C) / (A * A + B * B); + double a = static_cast(pt.x - line1.x); + double b = static_cast(pt.y - line1.y); + double c = static_cast(line2.x - line1.x); + double d = static_cast(line2.y - line1.y); + if (c == 0 && d == 0) return 0; + return Sqr(a * d - c * b) / (c * c + d * d); } template @@ -663,7 +796,7 @@ namespace Clipper2Lib } if (cnt & 1) a += static_cast(it2->y + it1->y) * (it2->x - it1->x); - return a * 0.5; + return (a * 0.5); } template @@ -681,16 +814,73 @@ namespace Clipper2Lib template inline bool IsPositive(const Path& poly) { - // A curve has positive orientation [and area] if a region 'R' + // A curve has positive orientation [and area] if a region 'R' // is on the left when traveling around the outside of 'R'. //https://mathworld.wolfram.com/CurveOrientation.html //nb: This statement is premised on using Cartesian coordinates return Area(poly) >= 0; } - - inline bool GetIntersectPoint(const Point64& ln1a, const Point64& ln1b, - const Point64& ln2a, const Point64& ln2b, Point64& ip) - { + +#if CLIPPER2_HI_PRECISION + // caution: this will compromise performance + // https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253 + // See also CPP/BenchMark/GetIntersectPtBenchmark.cpp + #define CC_MIN(x,y) ((x)>(y)?(y):(x)) + #define CC_MAX(x,y) ((x)<(y)?(y):(x)) + template + inline bool GetSegmentIntersectPt(const Point& ln1a, const Point& ln1b, + const Point& ln2a, const Point& ln2b, Point& ip) + { + double ln1dy = static_cast(ln1b.y - ln1a.y); + double ln1dx = static_cast(ln1a.x - ln1b.x); + double ln2dy = static_cast(ln2b.y - ln2a.y); + double ln2dx = static_cast(ln2a.x - ln2b.x); + double det = (ln2dy * ln1dx) - (ln1dy * ln2dx); + if (det == 0.0) return false; + T bb0minx = CC_MIN(ln1a.x, ln1b.x); + T bb0miny = CC_MIN(ln1a.y, ln1b.y); + T bb0maxx = CC_MAX(ln1a.x, ln1b.x); + T bb0maxy = CC_MAX(ln1a.y, ln1b.y); + T bb1minx = CC_MIN(ln2a.x, ln2b.x); + T bb1miny = CC_MIN(ln2a.y, ln2b.y); + T bb1maxx = CC_MAX(ln2a.x, ln2b.x); + T bb1maxy = CC_MAX(ln2a.y, ln2b.y); + + if constexpr (std::is_integral_v) + { + int64_t originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) >> 1; + int64_t originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) >> 1; + double ln0c = (ln1dy * static_cast(ln1a.x - originx)) + + (ln1dx * static_cast(ln1a.y - originy)); + double ln1c = (ln2dy * static_cast(ln2a.x - originx)) + + (ln2dx * static_cast(ln2a.y - originy)); + double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det; + double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det; + + ip.x = originx + (T)nearbyint(hitx); + ip.y = originy + (T)nearbyint(hity); + } + else + { + double originx = (CC_MIN(bb0maxx, bb1maxx) + CC_MAX(bb0minx, bb1minx)) / 2.0; + double originy = (CC_MIN(bb0maxy, bb1maxy) + CC_MAX(bb0miny, bb1miny)) / 2.0; + double ln0c = (ln1dy * static_cast(ln1a.x - originx)) + + (ln1dx * static_cast(ln1a.y - originy)); + double ln1c = (ln2dy * static_cast(ln2a.x - originx)) + + (ln2dx * static_cast(ln2a.y - originy)); + double hitx = ((ln1dx * ln1c) - (ln2dx * ln0c)) / det; + double hity = ((ln2dy * ln0c) - (ln1dy * ln1c)) / det; + + ip.x = originx + static_cast(hitx); + ip.y = originy + static_cast(hity); + } + return true; +} +#else + template + inline bool GetSegmentIntersectPt(const Point& ln1a, const Point& ln1b, + const Point& ln2a, const Point& ln2b, Point& ip) + { // https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection double dx1 = static_cast(ln1b.x - ln1a.x); double dy1 = static_cast(ln1b.y - ln1a.y); @@ -700,15 +890,44 @@ namespace Clipper2Lib double det = dy1 * dx2 - dy2 * dx1; if (det == 0.0) return false; double t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det; - if (t <= 0.0) ip = ln1a; // ?? check further (see also #568) - else if (t >= 1.0) ip = ln1b; // ?? check further + if (t <= 0.0) ip = ln1a; + else if (t >= 1.0) ip = ln1b; else { - ip.x = static_cast(ln1a.x + t * dx1); - ip.y = static_cast(ln1a.y + t * dy1); - } + ip.x = static_cast(ln1a.x + t * dx1); + ip.y = static_cast(ln1a.y + t * dy1); + } return true; } +#endif + + template + inline Point TranslatePoint(const Point& pt, double dx, double dy) + { +#ifdef USINGZ + return Point(pt.x + dx, pt.y + dy, pt.z); +#else + return Point(pt.x + dx, pt.y + dy); +#endif + } + + + template + inline Point ReflectPoint(const Point& pt, const Point& pivot) + { +#ifdef USINGZ + return Point(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z); +#else + return Point(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y)); +#endif + } + + template + inline int GetSign(const T& val) + { + if (!val) return 0; + return (val > 0) ? 1 : -1; + } inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b, const Point64& seg2a, const Point64& seg2b, bool inclusive = false) @@ -724,10 +943,10 @@ namespace Clipper2Lib return (res1 || res2 || res3 || res4); // ensures not collinear } else { - return (CrossProduct(seg1a, seg2a, seg2b) * - CrossProduct(seg1b, seg2a, seg2b) < 0) && - (CrossProduct(seg2a, seg1a, seg1b) * - CrossProduct(seg2b, seg1a, seg1b) < 0); + return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) * + GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) && + (GetSign(CrossProduct(seg2a, seg1a, seg1b)) * + GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0); } } @@ -743,7 +962,7 @@ namespace Clipper2Lib static_cast(offPt.y - seg1.y) * dy) / (Sqr(dx) + Sqr(dy)); if (q < 0) q = 0; else if (q > 1) q = 1; - if constexpr (std::numeric_limits::is_integer) + if constexpr (std::is_integral_v) return Point( seg1.x + static_cast(nearbyint(q * dx)), seg1.y + static_cast(nearbyint(q * dy))); @@ -770,7 +989,7 @@ namespace Clipper2Lib return PointInPolygonResult::IsOutside; bool is_above = first->y < pt.y, starting_above = is_above; - curr = first +1; + curr = first +1; while (true) { if (curr == cend) @@ -779,7 +998,7 @@ namespace Clipper2Lib cend = first; curr = cbegin; } - + if (is_above) { while (curr != cend && curr->y < pt.y) ++curr; @@ -791,14 +1010,14 @@ namespace Clipper2Lib if (curr == cend) continue; } - if (curr == cbegin) + if (curr == cbegin) prev = polygon.cend() - 1; //nb: NOT cend (since might equal first) - else + else prev = curr - 1; if (curr->y == pt.y) { - if (curr->x == pt.x || + if (curr->x == pt.x || (curr->y == prev->y && ((pt.x < prev->x) != (pt.x < curr->x)))) return PointInPolygonResult::IsOn; @@ -822,7 +1041,7 @@ namespace Clipper2Lib is_above = !is_above; ++curr; } - + if (is_above != starting_above) { cend = polygon.cend(); diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.engine.h b/godot/thirdparty/clipper2/include/clipper2/clipper.engine.h index 13c7f069..f6108832 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.engine.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.engine.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 November 2023 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -33,7 +33,7 @@ namespace Clipper2Lib { //Note: all clipping operations except for Difference are commutative. enum class ClipType { None, Intersection, Union, Difference, Xor }; - + enum class PathType { Subject, Clip }; enum class JoinWith { None, Left, Right }; @@ -41,7 +41,7 @@ namespace Clipper2Lib { None = 0, OpenStart = 1, OpenEnd = 2, LocalMax = 4, LocalMin = 8 }; - constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) + constexpr enum VertexFlags operator &(enum VertexFlags a, enum VertexFlags b) { return (enum VertexFlags)(uint32_t(a) & uint32_t(b)); } @@ -95,7 +95,7 @@ namespace Clipper2Lib { Path64 path; bool is_open = false; - ~OutRec() { + ~OutRec() { if (splits) delete splits; // nb: don't delete the split pointers // as these are owned by ClipperBase's outrec_list_ @@ -106,7 +106,7 @@ namespace Clipper2Lib { //Important: UP and DOWN here are premised on Y-axis positive down //displays, which is the orientation used in Clipper's development. /////////////////////////////////////////////////////////////////// - + struct Active { Point64 bot; Point64 top; @@ -230,7 +230,7 @@ namespace Clipper2Lib { inline bool PopHorz(Active *&e); inline OutPt* StartOpenPath(Active &e, const Point64& pt); inline void UpdateEdgeIntoAEL(Active *e); - OutPt* IntersectEdges(Active &e1, Active &e2, const Point64& pt); + void IntersectEdges(Active &e1, Active &e2, const Point64& pt); inline void DeleteFromAEL(Active &e); inline void AdjustCurrXAndCopyToSEL(const int64_t top_y); void DoIntersections(const int64_t top_y); @@ -240,7 +240,7 @@ namespace Clipper2Lib { void SwapPositionsInAEL(Active& edge1, Active& edge2); OutRec* NewOutRec(); OutPt* AddOutPt(const Active &e, const Point64& pt); - OutPt* AddLocalMinPoly(Active &e1, Active &e2, + OutPt* AddLocalMinPoly(Active &e1, Active &e2, const Point64& pt, bool is_new = false); OutPt* AddLocalMaxPoly(Active &e1, Active &e2, const Point64& pt); void DoHorizontal(Active &horz); @@ -251,13 +251,13 @@ namespace Clipper2Lib { void JoinOutrecPaths(Active &e1, Active &e2); void FixSelfIntersects(OutRec* outrec); void DoSplitOp(OutRec* outRec, OutPt* splitOp); - + inline void AddTrialHorzJoin(OutPt* op); void ConvertHorzSegsToJoins(); void ProcessHorzJoins(); void Split(Active& e, const Point64& pt); - inline void CheckJoinLeft(Active& e, + inline void CheckJoinLeft(Active& e, const Point64& pt, bool check_curr_x = false); inline void CheckJoinRight(Active& e, const Point64& pt, bool check_curr_x = false); @@ -326,12 +326,12 @@ namespace Clipper2Lib { const PolyPath* Parent() const { return parent_; } - bool IsHole() const + bool IsHole() const { unsigned lvl = Level(); //Even levels except level 0 return lvl && !(lvl & 1); - } + } }; typedef typename std::vector> PolyPath64List; @@ -343,15 +343,16 @@ namespace Clipper2Lib { Path64 polygon_; public: explicit PolyPath64(PolyPath64* parent = nullptr) : PolyPath(parent) {} + explicit PolyPath64(PolyPath64* parent, const Path64& path) : PolyPath(parent) { polygon_ = path; } ~PolyPath64() { childs_.resize(0); } PolyPath64* operator [] (size_t index) const - { + { return childs_[index].get(); //std::unique_ptr - } + } PolyPath64* Child(size_t index) const { @@ -363,10 +364,7 @@ namespace Clipper2Lib { PolyPath64* AddChild(const Path64& path) override { - auto p = std::make_unique(this); - auto* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override @@ -401,12 +399,25 @@ namespace Clipper2Lib { scale_ = parent ? parent->scale_ : 1.0; } + explicit PolyPathD(PolyPathD* parent, const Path64& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + int error_code = 0; + polygon_ = ScalePath(path, scale_, error_code); + } + + explicit PolyPathD(PolyPathD* parent, const PathD& path) : PolyPath(parent) + { + scale_ = parent ? parent->scale_ : 1.0; + polygon_ = path; + } + ~PolyPathD() { childs_.resize(0); } PolyPathD* operator [] (size_t index) const - { + { return childs_[index].get(); } @@ -420,22 +431,15 @@ namespace Clipper2Lib { void SetScale(double value) { scale_ = value; } double Scale() const { return scale_; } - + PolyPathD* AddChild(const Path64& path) override { - int error_code = 0; - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = ScalePath(path, scale_, error_code); - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } PolyPathD* AddChild(const PathD& path) { - auto p = std::make_unique(this); - PolyPathD* result = childs_.emplace_back(std::move(p)).get(); - result->polygon_ = path; - return result; + return childs_.emplace_back(std::make_unique(this, path)).get(); } void Clear() override @@ -488,7 +492,7 @@ namespace Clipper2Lib { return Execute(clip_type, fill_rule, closed_paths, dummy); } - bool Execute(ClipType clip_type, FillRule fill_rule, + bool Execute(ClipType clip_type, FillRule fill_rule, Paths64& closed_paths, Paths64& open_paths) { closed_paths.clear(); @@ -530,7 +534,7 @@ namespace Clipper2Lib { public: explicit ClipperD(int precision = 2) : ClipperBase() { - CheckPrecision(precision, error_code_); + CheckPrecisionRange(precision, error_code_); // to optimize scaling / descaling precision // set the scale to a power of double's radix (2) (#25) scale_ = std::pow(std::numeric_limits::radix, @@ -560,12 +564,12 @@ namespace Clipper2Lib { void CheckCallback() { if(zCallbackD_) - // if the user defined float point callback has been assigned + // if the user defined float point callback has been assigned // then assign the proxy callback function - ClipperBase::zCallback_ = + ClipperBase::zCallback_ = std::bind(&ClipperD::ZCB, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, - std::placeholders::_4, std::placeholders::_5); + std::placeholders::_4, std::placeholders::_5); else ClipperBase::zCallback_ = nullptr; } @@ -632,6 +636,6 @@ namespace Clipper2Lib { }; -} // namespace +} // namespace #endif // CLIPPER_ENGINE_H diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.export.h b/godot/thirdparty/clipper2/include/clipper2/clipper.export.h index d7286132..53a44536 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.export.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.export.h @@ -1,14 +1,14 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 26 November 2023 * +* Date : 14 May 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : This module exports the Clipper2 Library (ie DLL/so) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ -/* +/* Boolean clipping: cliptype: None=0, Intersection=1, Union=2, Difference=3, Xor=4 fillrule: EvenOdd=0, NonZero=1, Positive=2, Negative=3 @@ -19,12 +19,12 @@ The path structures used extensively in other parts of this library are all based on std::vector classes. Since C++ classes can't be accessed by other -languages, these paths must be converted into simple C data structures that -can be understood by just about any programming language. And these C style -path structures are simple arrays of int64_t (CPath64) and double (CPathD). +languages, these paths are converted into very simple array data structures +(of either int64_t for CPath64 or double for CPathD) that can be parsed by +just about any programming language. CPath64 and CPathD: -These are arrays of consecutive x and y path coordinates preceeded by +These are arrays of consecutive x and y path coordinates preceeded by a pair of values containing the path's length (N) and a 0 value. __________________________________ |counter|coord1|coord2|...|coordN| @@ -34,23 +34,24 @@ __________________________________ CPaths64 and CPathsD: These are also arrays containing any number of consecutive CPath64 or CPathD structures. But preceeding these consecutive paths, there is pair of -values that contain the total length of the array (A) structure and -the number (C) of CPath64 or CPathD it contains. +values that contain the total length of the array structure (A) and the +number of CPath64 or CPathD it contains (C). The space these structures will +occupy in memory = A * sizeof(int64_t) or A * sizeof(double) respectively. _______________________________ |counter|path1|path2|...|pathC| |A , C | | _______________________________ CPolytree64 and CPolytreeD: -These are also arrays consisting of CPolyPath structures that represent +These are also arrays consisting of CPolyPath structures that represent individual paths in a tree structure. However, the very first (ie top) -CPolyPath is just the tree container that won't have a path. And because +CPolyPath is just the tree container that doesn't have a path. And because of that, its structure will be very slightly different from the remaining CPolyPath. This difference will be discussed below. CPolyPath64 and CPolyPathD: -These are simple arrays consisting of a series of path coordinates followed -by any number of child (ie nested) CPolyPath. Preceeding these are two values +These are simple arrays consisting of a series of path coordinates followed +by any number of child (ie nested) CPolyPath. Preceeding these are two values indicating the length of the path (N) and the number of child CPolyPath (C). ____________________________________________________________ |counter|coord1|coord2|...|coordN| child1|child2|...|childC| @@ -58,19 +59,20 @@ ____________________________________________________________ ____________________________________________________________ As mentioned above, the very first CPolyPath structure is just a container -that owns (both directly and indirectly) every other CPolyPath in the tree. +that owns (both directly and indirectly) every other CPolyPath in the tree. Since this first CPolyPath has no path, instead of a path length, its very -first value will contain the total length of the CPolytree array structure. - -All theses exported structures (CPaths64, CPathsD, CPolyTree64 & CPolyTreeD) -are arrays of type int64_t or double. And the first value in these arrays -will always contain the length of that array. - -These array structures are allocated in heap memory which will eventually -need to be released. But since applications dynamically linking to these -functions may use different memory managers, the only safe way to free up -this memory is to use the exported DisposeArray64 and DisposeArrayD -functions below. +first value will contain the total length of the CPolytree array (not its +total bytes length). + +Again, all theses exported structures (CPaths64, CPathsD, CPolyTree64 & +CPolyTreeD) are arrays of either type int64_t or double, and the first +value in these arrays will always be the length of that array. + +These array structures are allocated in heap memory which will eventually +need to be released. However, since applications dynamically linking to +these functions may use different memory managers, the only safe way to +free up this memory is to use the exported DisposeArray64 and +DisposeArrayD functions (see below). */ @@ -128,7 +130,7 @@ inline Rect CRectToRect(const CRect& rect) #ifdef _WIN32 #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) #else - #define EXTERN_DLL_EXPORT extern "C" + #define EXTERN_DLL_EXPORT extern "C" #endif @@ -173,8 +175,8 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, bool preserve_collinear = true, bool reverse_solution = false); EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, - double delta, uint8_t jointype, uint8_t endtype, - double miter_limit = 2.0, double arc_tolerance = 0.0, + double delta, uint8_t jointype, uint8_t endtype, + double miter_limit = 2.0, double arc_tolerance = 0.0, bool reverse_solution = false); EXTERN_DLL_EXPORT CPathsD InflatePathsD(const CPathsD paths, double delta, uint8_t jointype, uint8_t endtype, @@ -219,10 +221,10 @@ static size_t GetPolyPath64ArrayLen(const PolyPath64& pp) return result; } -static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, +static void GetPolytreeCountAndCStorageSize(const PolyTree64& tree, size_t& cnt, size_t& array_len) { - cnt = tree.Count(); // nb: top level count only + cnt = tree.Count(); // nb: top level count only array_len = GetPolyPath64ArrayLen(tree); } @@ -271,17 +273,34 @@ CPathsD CreateCPathsDFromPaths64(const Paths64& paths, double scale) return result; } +template +static Path ConvertCPath(T* path) +{ + Path result; + if (!path) return result; + T* v = path; + size_t cnt = static_cast(*v); + v += 2; // skip 0 value + result.reserve(cnt); + for (size_t j = 0; j < cnt; ++j) + { + T x = *v++, y = *v++; + result.push_back(Point(x, y)); + } + return result; +} + template static Paths ConvertCPaths(T* paths) { Paths result; if (!paths) return result; T* v = paths; ++v; - size_t cnt = *v++; + size_t cnt = static_cast(*v++); result.reserve(cnt); for (size_t i = 0; i < cnt; ++i) { - size_t cnt2 = *v; + size_t cnt2 = static_cast(*v); v += 2; Path path; path.reserve(cnt2); @@ -300,17 +319,17 @@ static Paths64 ConvertCPathsDToPaths64(const CPathsD paths, double scale) { Paths64 result; if (!paths) return result; - double* v = paths; + double* v = paths; ++v; // skip the first value (0) - int64_t cnt = (int64_t)*v++; + size_t cnt = static_cast(*v++); result.reserve(cnt); - for (int i = 0; i < cnt; ++i) + for (size_t i = 0; i < cnt; ++i) { - int64_t cnt2 = (int64_t)*v; + size_t cnt2 = static_cast(*v); v += 2; Path64 path; path.reserve(cnt2); - for (int j = 0; j < cnt2; ++j) + for (size_t j = 0; j < cnt2; ++j) { double x = *v++ * scale; double y = *v++ * scale; @@ -362,7 +381,7 @@ EXTERN_DLL_EXPORT const char* Version() return CLIPPER2_VERSION; } -EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, +EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, uint8_t fillrule, const CPaths64 subjects, const CPaths64 subjects_open, const CPaths64 clips, CPaths64& solution, CPaths64& solution_open, @@ -370,7 +389,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, { if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - + Paths64 sub, sub_open, clp, sol, sol_open; sub = ConvertCPaths(subjects); sub_open = ConvertCPaths(subjects_open); @@ -382,7 +401,7 @@ EXTERN_DLL_EXPORT int BooleanOp64(uint8_t cliptype, if (sub.size() > 0) clipper.AddSubject(sub); if (sub_open.size() > 0) clipper.AddOpenSubject(sub_open); if (clp.size() > 0) clipper.AddClip(clp); - if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) + if (!clipper.Execute(ClipType(cliptype), FillRule(fillrule), sol, sol_open)) return -1; // clipping bug - should never happen :) solution = CreateCPaths(sol); solution_open = CreateCPaths(sol_open); @@ -455,7 +474,7 @@ EXTERN_DLL_EXPORT int BooleanOp_PolyTreeD(uint8_t cliptype, if (precision < -8 || precision > 8) return -5; if (cliptype > static_cast(ClipType::Xor)) return -4; if (fillrule > static_cast(FillRule::Negative)) return -3; - + double scale = std::pow(10, precision); int err = 0; @@ -485,10 +504,10 @@ EXTERN_DLL_EXPORT CPaths64 InflatePaths64(const CPaths64 paths, { Paths64 pp; pp = ConvertCPaths(paths); - ClipperOffset clip_offset( miter_limit, + ClipperOffset clip_offset( miter_limit, arc_tolerance, reverse_solution); clip_offset.AddPaths(pp, JoinType(jointype), EndType(endtype)); - Paths64 result; + Paths64 result; clip_offset.Execute(delta, result); return CreateCPaths(result); } @@ -560,6 +579,22 @@ EXTERN_DLL_EXPORT CPathsD RectClipLinesD(const CRectD& rect, return CreateCPathsDFromPaths64(result, 1 / scale); } +EXTERN_DLL_EXPORT CPaths64 MinkowskiSum64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) +{ + Path64 path = ConvertCPath(cpath); + Path64 pattern = ConvertCPath(cpattern); + Paths64 solution = MinkowskiSum(pattern, path, is_closed); + return CreateCPaths(solution); +} + +EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath64& cpath, bool is_closed) +{ + Path64 path = ConvertCPath(cpath); + Path64 pattern = ConvertCPath(cpattern); + Paths64 solution = MinkowskiDiff(pattern, path, is_closed); + return CreateCPaths(solution); +} + } // end Clipper2Lib namespace - + #endif // CLIPPER2_EXPORT_H diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.h b/godot/thirdparty/clipper2/include/clipper2/clipper.h index 0f516b60..a2fe5c3c 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 18 November 2023 * +* Date : 27 April 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : This module provides a simple interface to the Clipper Library * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -24,7 +24,7 @@ namespace Clipper2Lib { inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule, const Paths64& subjects, const Paths64& clips) - { + { Paths64 result; Clipper64 clipper; clipper.AddSubject(subjects); @@ -47,7 +47,7 @@ namespace Clipper2Lib { const PathsD& subjects, const PathsD& clips, int precision = 2) { int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); PathsD result; if (error_code) return result; ClipperD clipper(precision); @@ -58,12 +58,12 @@ namespace Clipper2Lib { } inline void BooleanOp(ClipType cliptype, FillRule fillrule, - const PathsD& subjects, const PathsD& clips, + const PathsD& subjects, const PathsD& clips, PolyTreeD& polytree, int precision = 2) { polytree.Clear(); int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (error_code) return; ClipperD clipper(precision); clipper.AddSubject(subjects); @@ -75,7 +75,7 @@ namespace Clipper2Lib { { return BooleanOp(ClipType::Intersection, fillrule, subjects, clips); } - + inline PathsD Intersect(const PathsD& subjects, const PathsD& clips, FillRule fillrule, int decimal_prec = 2) { return BooleanOp(ClipType::Intersection, fillrule, subjects, clips, decimal_prec); @@ -104,7 +104,7 @@ namespace Clipper2Lib { { PathsD result; int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (error_code) return result; ClipperD clipper(precision); clipper.AddSubject(subjects); @@ -145,11 +145,11 @@ namespace Clipper2Lib { } inline PathsD InflatePaths(const PathsD& paths, double delta, - JoinType jt, EndType et, double miter_limit = 2.0, + JoinType jt, EndType et, double miter_limit = 2.0, int precision = 2, double arc_tolerance = 0.0) { int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (!delta) return paths; if (error_code) return PathsD(); const double scale = std::pow(10, precision); @@ -219,13 +219,13 @@ namespace Clipper2Lib { { if (rect.IsEmpty() || paths.empty()) return PathsD(); int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (error_code) return PathsD(); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); RectClip64 rc(r); Paths64 pp = ScalePaths(paths, scale, error_code); - if (error_code) return PathsD(); // ie: error_code result is lost + if (error_code) return PathsD(); // ie: error_code result is lost return ScalePaths( rc.Execute(pp), 1 / scale, error_code); } @@ -251,7 +251,7 @@ namespace Clipper2Lib { { if (rect.IsEmpty() || lines.empty()) return PathsD(); int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (error_code) return PathsD(); const double scale = std::pow(10, precision); Rect64 r = ScaleRect(rect, scale); @@ -290,8 +290,8 @@ namespace Clipper2Lib { { // return false if this child isn't fully contained by its parent - // checking for a single vertex outside is a bit too crude since - // it doesn't account for rounding errors. It's better to check + // checking for a single vertex outside is a bit too crude since + // it doesn't account for rounding errors. It's better to check // for consecutive vertices found outside the parent's polygon. int outsideCnt = 0; @@ -311,7 +311,7 @@ namespace Clipper2Lib { return true; } - static void OutlinePolyPath(std::ostream& os, + static void OutlinePolyPath(std::ostream& os, size_t idx, bool isHole, size_t count, const std::string& preamble) { std::string plural = (count == 1) ? "." : "s."; @@ -342,19 +342,19 @@ namespace Clipper2Lib { } template - inline constexpr void MakePathGeneric(const T an_array, + inline constexpr void MakePathGeneric(const T an_array, size_t array_size, std::vector& result) { result.reserve(array_size / 2); for (size_t i = 0; i < array_size; i +=2) #ifdef USINGZ - result.push_back( U{ an_array[i], an_array[i +1], 0} ); + result.push_back( U{ an_array[i], an_array[i + 1], 0} ); #else result.push_back( U{ an_array[i], an_array[i + 1]} ); #endif } - } // end details namespace + } // end details namespace inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp) { @@ -398,7 +398,7 @@ namespace Clipper2Lib { inline bool CheckPolytreeFullyContainsChildren(const PolyTree64& polytree) { for (const auto& child : polytree) - if (child->Count() > 0 && + if (child->Count() > 0 && !details::PolyPath64ContainsChildren(*child)) return false; return true; @@ -471,7 +471,7 @@ namespace Clipper2Lib { std::size_t size = N / 3; Path64 result(size); for (size_t i = 0; i < size; ++i) - result[i] = Point64(list[i * 3], + result[i] = Point64(list[i * 3], list[i * 3 + 1], list[i * 3 + 2]); return result; } @@ -489,7 +489,7 @@ namespace Clipper2Lib { list[i * 3 + 1], list[i * 3 + 2]); else for (size_t i = 0; i < size; ++i) - result[i] = PointD(list[i * 3], list[i * 3 + 1], + result[i] = PointD(list[i * 3], list[i * 3 + 1], static_cast(list[i * 3 + 2])); return result; } @@ -510,9 +510,9 @@ namespace Clipper2Lib { if (!is_open_path) { - while (srcIt != stop && !CrossProduct(*stop, *srcIt, *(srcIt + 1))) + while (srcIt != stop && IsCollinear(*stop, *srcIt, *(srcIt + 1))) ++srcIt; - while (srcIt != stop && !CrossProduct(*(stop - 1), *stop, *srcIt)) + while (srcIt != stop && IsCollinear(*(stop - 1), *stop, *srcIt)) --stop; if (srcIt == stop) return Path64(); } @@ -521,7 +521,7 @@ namespace Clipper2Lib { dst.push_back(*prevIt); for (; srcIt != stop; ++srcIt) { - if (CrossProduct(*prevIt, *srcIt, *(srcIt + 1))) + if (!IsCollinear(*prevIt, *srcIt, *(srcIt + 1))) { prevIt = srcIt; dst.push_back(*prevIt); @@ -530,12 +530,12 @@ namespace Clipper2Lib { if (is_open_path) dst.push_back(*srcIt); - else if (CrossProduct(*prevIt, *stop, dst[0])) + else if (!IsCollinear(*prevIt, *stop, dst[0])) dst.push_back(*stop); else { while (dst.size() > 2 && - !CrossProduct(dst[dst.size() - 1], dst[dst.size() - 2], dst[0])) + IsCollinear(dst[dst.size() - 1], dst[dst.size() - 2], dst[0])) dst.pop_back(); if (dst.size() < 3) return Path64(); } @@ -545,7 +545,7 @@ namespace Clipper2Lib { inline PathD TrimCollinear(const PathD& path, int precision, bool is_open_path = false) { int error_code = 0; - CheckPrecision(precision, error_code); + CheckPrecisionRange(precision, error_code); if (error_code) return PathD(); const double scale = std::pow(10, precision); Path64 p = ScalePath(path, scale, error_code); @@ -580,23 +580,23 @@ namespace Clipper2Lib { double cp = std::abs(CrossProduct(pt1, pt2, pt3)); return (cp * cp) / (DistanceSqr(pt1, pt2) * DistanceSqr(pt2, pt3)) < sin_sqrd_min_angle_rads; } - + template - inline Path Ellipse(const Rect& rect, int steps = 0) + inline Path Ellipse(const Rect& rect, size_t steps = 0) { - return Ellipse(rect.MidPoint(), - static_cast(rect.Width()) *0.5, + return Ellipse(rect.MidPoint(), + static_cast(rect.Width()) *0.5, static_cast(rect.Height()) * 0.5, steps); } template inline Path Ellipse(const Point& center, - double radiusX, double radiusY = 0, int steps = 0) + double radiusX, double radiusY = 0, size_t steps = 0) { if (radiusX <= 0) return Path(); if (radiusY <= 0) radiusY = radiusX; if (steps <= 2) - steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); + steps = static_cast(PI * sqrt((radiusX + radiusY) / 2)); double si = std::sin(2 * PI / steps); double co = std::cos(2 * PI / steps); @@ -604,7 +604,7 @@ namespace Clipper2Lib { Path result; result.reserve(steps); result.push_back(Point(center.x + radiusX, static_cast(center.y))); - for (int i = 1; i < steps; ++i) + for (size_t i = 1; i < steps; ++i) { result.push_back(Point(center.x + radiusX * dx, center.y + radiusY * dy)); double x = dx * co - dy * si; @@ -614,19 +614,7 @@ namespace Clipper2Lib { return result; } - template - inline double PerpendicDistFromLineSqrd(const Point& pt, - const Point& line1, const Point& line2) - { - double a = static_cast(pt.x - line1.x); - double b = static_cast(pt.y - line1.y); - double c = static_cast(line2.x - line1.x); - double d = static_cast(line2.y - line1.y); - if (c == 0 && d == 0) return 0; - return Sqr(a * d - c * b) / (c * c + d * d); - } - - inline size_t GetNext(size_t current, size_t high, + inline size_t GetNext(size_t current, size_t high, const std::vector& flags) { ++current; @@ -637,7 +625,7 @@ namespace Clipper2Lib { return current; } - inline size_t GetPrior(size_t current, size_t high, + inline size_t GetPrior(size_t current, size_t high, const std::vector& flags) { if (current == 0) current = high; @@ -650,7 +638,7 @@ namespace Clipper2Lib { } template - inline Path SimplifyPath(const Path &path, + inline Path SimplifyPath(const Path &path, double epsilon, bool isClosedPath = true) { const size_t len = path.size(), high = len -1; @@ -665,7 +653,7 @@ namespace Clipper2Lib { distSqr[0] = PerpendicDistFromLineSqrd(path[0], path[high], path[1]); distSqr[high] = PerpendicDistFromLineSqrd(path[high], path[0], path[high - 1]); } - else + else { distSqr[0] = MAX_DBL; distSqr[high] = MAX_DBL; @@ -684,7 +672,7 @@ namespace Clipper2Lib { } while (curr != start && distSqr[curr] > epsSqr); if (curr == start) break; } - + prior = GetPrior(curr, high, flags); next = GetNext(curr, high, flags); if (next == prior) break; @@ -699,7 +687,7 @@ namespace Clipper2Lib { } else prior2 = GetPrior(prior, high, flags); - + flags[curr] = true; curr = next; next = GetNext(next, high, flags); @@ -717,7 +705,7 @@ namespace Clipper2Lib { } template - inline Paths SimplifyPaths(const Paths &paths, + inline Paths SimplifyPaths(const Paths &paths, double epsilon, bool isClosedPath = true) { Paths result; diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.minkowski.h b/godot/thirdparty/clipper2/include/clipper2/clipper.minkowski.h index ebddd08a..a3ddcf86 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.minkowski.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.minkowski.h @@ -15,7 +15,7 @@ #include #include "clipper2/clipper.core.h" -namespace Clipper2Lib +namespace Clipper2Lib { namespace detail diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.offset.h b/godot/thirdparty/clipper2/include/clipper2/clipper.offset.h index 30992bfa..bb075a6d 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.offset.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.offset.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 19 November 2023 * +* Date : 24 March 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -34,9 +34,7 @@ class ClipperOffset { class Group { public: Paths64 paths_in; - std::vector is_hole_list; - std::vector bounds_list; - int lowest_path_idx = -1; + std::optional lowest_path_idx{}; bool is_reversed = false; JoinType join_type; EndType end_type; @@ -52,7 +50,8 @@ class ClipperOffset { double step_cos_ = 0.0; PathD norms; Path64 path_out; - Paths64 solution; + Paths64* solution = nullptr; + PolyTree64* solution_tree = nullptr; std::vector groups_; JoinType join_type_ = JoinType::Bevel; EndType end_type_ = EndType::Polygon; @@ -64,9 +63,10 @@ class ClipperOffset { #ifdef USINGZ ZCallback64 zCallback64_ = nullptr; + void ZCB(const Point64& bot1, const Point64& top1, + const Point64& bot2, const Point64& top2, Point64& ip); #endif DeltaCallback64 deltaCallback64_ = nullptr; - size_t CalcSolutionCapacity(); bool CheckReverseOrientation(); void DoBevel(const Path64& path, size_t j, size_t k); @@ -83,7 +83,7 @@ class ClipperOffset { public: explicit ClipperOffset(double miter_limit = 2.0, double arc_tolerance = 0.0, - bool preserve_collinear = false, + bool preserve_collinear = false, bool reverse_solution = false) : miter_limit_(miter_limit), arc_tolerance_(arc_tolerance), preserve_collinear_(preserve_collinear), @@ -91,7 +91,7 @@ class ClipperOffset { ~ClipperOffset() { Clear(); }; - int ErrorCode() { return error_code_; }; + int ErrorCode() const { return error_code_; }; void AddPath(const Path64& path, JoinType jt_, EndType et_); void AddPaths(const Paths64& paths, JoinType jt_, EndType et_); void Clear() { groups_.clear(); norms.clear(); }; diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.rectclip.h b/godot/thirdparty/clipper2/include/clipper2/clipper.rectclip.h index ff043f25..bfcfacf2 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.rectclip.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.rectclip.h @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 1 November 2023 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -18,6 +18,7 @@ namespace Clipper2Lib { + // Location: the order is important here, see StartLocsIsClockwise() enum class Location { Left, Top, Right, Bottom, Inside }; class OutPt2; @@ -26,10 +27,10 @@ namespace Clipper2Lib class OutPt2 { public: Point64 pt; - size_t owner_idx; - OutPt2List* edge; - OutPt2* next; - OutPt2* prev; + size_t owner_idx = 0; + OutPt2List* edge = nullptr; + OutPt2* next = nullptr; + OutPt2* prev = nullptr; }; //------------------------------------------------------------------------------ @@ -50,9 +51,9 @@ namespace Clipper2Lib OutPt2List edges_[8]; // clockwise and counter-clockwise std::vector start_locs_; void CheckEdges(); - void TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw); + void TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw); void GetNextLocation(const Path64& path, - Location& loc, int& i, int highI); + Location& loc, size_t& i, size_t highI); OutPt2* Add(Point64 pt, bool start_new = false); void AddCorner(Location prev, Location curr); void AddCorner(Location& loc, bool isClockwise); diff --git a/godot/thirdparty/clipper2/include/clipper2/clipper.version.h b/godot/thirdparty/clipper2/include/clipper2/clipper.version.h index d7644067..61464095 100644 --- a/godot/thirdparty/clipper2/include/clipper2/clipper.version.h +++ b/godot/thirdparty/clipper2/include/clipper2/clipper.version.h @@ -1,6 +1,6 @@ #ifndef CLIPPER_VERSION_H #define CLIPPER_VERSION_H -constexpr auto CLIPPER2_VERSION = "1.3.0"; +constexpr auto CLIPPER2_VERSION = "1.4.0"; #endif // CLIPPER_VERSION_H diff --git a/godot/thirdparty/clipper2/patches/clipper2-exceptions.patch b/godot/thirdparty/clipper2/patches/clipper2-exceptions.patch index 0e1c6585..44c2b028 100644 --- a/godot/thirdparty/clipper2/patches/clipper2-exceptions.patch +++ b/godot/thirdparty/clipper2/patches/clipper2-exceptions.patch @@ -1,9 +1,9 @@ diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h -index b3dddeeaa2..a77cdad5f4 100644 +index 925c04685e..d0d159b949 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h +++ b/thirdparty/clipper2/include/clipper2/clipper.core.h -@@ -21,6 +21,8 @@ - #include +@@ -22,6 +22,8 @@ + #include #include "clipper2/clipper.version.h" +#define CLIPPER2_THROW(exception) std::abort() @@ -11,7 +11,7 @@ index b3dddeeaa2..a77cdad5f4 100644 namespace Clipper2Lib { -@@ -78,18 +80,18 @@ namespace Clipper2Lib +@@ -79,18 +81,18 @@ namespace Clipper2Lib switch (error_code) { case precision_error_i: diff --git a/godot/thirdparty/clipper2/patches/gcc14-warning.patch b/godot/thirdparty/clipper2/patches/gcc14-warning.patch deleted file mode 100644 index a4f06ef3..00000000 --- a/godot/thirdparty/clipper2/patches/gcc14-warning.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h -index a77cdad5f4..0de7c3720e 100644 ---- a/thirdparty/clipper2/include/clipper2/clipper.core.h -+++ b/thirdparty/clipper2/include/clipper2/clipper.core.h -@@ -138,7 +138,7 @@ namespace Clipper2Lib - } - - template -- explicit Point(const Point& p) -+ explicit Point(const Point& p) - { - Init(p.x, p.y, p.z); - } -@@ -180,7 +180,7 @@ namespace Clipper2Lib - Point(const T2 x_, const T2 y_) { Init(x_, y_); } - - template -- explicit Point(const Point& p) { Init(p.x, p.y); } -+ explicit Point(const Point& p) { Init(p.x, p.y); } - - Point operator * (const double scale) const - { diff --git a/godot/thirdparty/clipper2/patches/llvm-error.patch b/godot/thirdparty/clipper2/patches/llvm-error.patch new file mode 100644 index 00000000..e326d73e --- /dev/null +++ b/godot/thirdparty/clipper2/patches/llvm-error.patch @@ -0,0 +1,34 @@ +diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h +index 67dd731af6..0f69bf2d9f 100644 +--- a/thirdparty/clipper2/include/clipper2/clipper.core.h ++++ b/thirdparty/clipper2/include/clipper2/clipper.core.h +@@ -695,11 +695,13 @@ namespace Clipper2Lib + // returns true if (and only if) a * b == c * d + inline bool ProductsAreEqual(int64_t a, int64_t b, int64_t c, int64_t d) + { +-#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX +- const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b); +- const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d); +- return ab == cd; +-#else ++// -- GODOT start -- ++// #if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX ++// const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b); ++// const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d); ++// return ab == cd; ++// #else ++// -- GODOT end -- + // nb: unsigned values needed for calculating overflow carry + const auto abs_a = static_cast(std::abs(a)); + const auto abs_b = static_cast(std::abs(b)); +@@ -714,7 +716,9 @@ namespace Clipper2Lib + const auto sign_cd = TriSign(c) * TriSign(d); + + return abs_ab == abs_cd && sign_ab == sign_cd; +-#endif ++// -- GODOT start -- ++// #endif ++// -- GODOT end -- + } + + template diff --git a/godot/thirdparty/clipper2/src/clipper.engine.cpp b/godot/thirdparty/clipper2/src/clipper.engine.cpp index 9358b74b..8f120267 100644 --- a/godot/thirdparty/clipper2/src/clipper.engine.cpp +++ b/godot/thirdparty/clipper2/src/clipper.engine.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 22 November 2023 * +* Date : 27 April 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : This is the main polygon clipping module * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -31,11 +31,11 @@ namespace Clipper2Lib { static const Rect64 invalid_rect = Rect64(false); - // Every closed path (or polygon) is made up of a series of vertices forming - // edges that alternate between going up (relative to the Y-axis) and going - // down. Edges consecutively going up or consecutively going down are called - // 'bounds' (ie sides if they're simple polygons). 'Local Minima' refer to - // vertices where descending bounds become ascending ones. + // Every closed path (ie polygon) is made up of a series of vertices forming edge + // 'bounds' that alternate between ascending bounds (containing edges going up + // relative to the Y-axis) and descending bounds. 'Local Minima' refers to + // vertices where ascending and descending bounds join at the bottom, and + // 'Local Maxima' are where ascending and descending bounds join at the top. struct Scanline { int64_t y = 0; @@ -63,6 +63,7 @@ namespace Clipper2Lib { } }; + inline bool IsOdd(int val) { return (val & 1) ? true : false; @@ -188,7 +189,7 @@ namespace Clipper2Lib { } //PrevPrevVertex: useful to get the (inverted Y-axis) top of the - //alternate edge (ie left or right bound) during edge insertion. + //alternate edge (ie left or right bound) during edge insertion. inline Vertex* PrevPrevVertex(const Active& ae) { if (ae.wind_dx > 0) @@ -233,15 +234,15 @@ namespace Clipper2Lib { Vertex* result = e.vertex_top; if (e.wind_dx > 0) while ((result->next->pt.y == result->pt.y) && - ((result->flags & (VertexFlags::OpenEnd | + ((result->flags & (VertexFlags::OpenEnd | VertexFlags::LocalMax)) == VertexFlags::None)) result = result->next; else while (result->prev->pt.y == result->pt.y && - ((result->flags & (VertexFlags::OpenEnd | + ((result->flags & (VertexFlags::OpenEnd | VertexFlags::LocalMax)) == VertexFlags::None)) result = result->prev; - if (!IsMaxima(*result)) result = nullptr; // not a maxima + if (!IsMaxima(*result)) result = nullptr; // not a maxima return result; } @@ -252,7 +253,7 @@ namespace Clipper2Lib { while (result->next->pt.y == result->pt.y) result = result->next; else while (result->prev->pt.y == result->pt.y) result = result->prev; - if (!IsMaxima(*result)) result = nullptr; // not a maxima + if (!IsMaxima(*result)) result = nullptr; // not a maxima return result; } @@ -613,13 +614,13 @@ namespace Clipper2Lib { list.push_back(std::make_unique (&vert, polytype, is_open)); } - void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, + void AddPaths_(const Paths64& paths, PathType polytype, bool is_open, std::vector& vertexLists, LocalMinimaList& locMinList) { const auto total_vertex_count = - std::accumulate(paths.begin(), paths.end(), 0, + std::accumulate(paths.begin(), paths.end(), size_t(0), [](const auto& a, const Path64& path) - {return a + static_cast(path.size()); }); + {return a + path.size(); }); if (total_vertex_count == 0) return; Vertex* vertices = new Vertex[total_vertex_count], * v = vertices; @@ -810,7 +811,7 @@ namespace Clipper2Lib { void ClipperBase::SetZ(const Active& e1, const Active& e2, Point64& ip) { if (!zCallback_) return; - // prioritize subject over clip vertices by passing + // prioritize subject over clip vertices by passing // subject vertices before clip vertices in the callback if (GetPolyType(e1) == PathType::Subject) { @@ -845,11 +846,11 @@ namespace Clipper2Lib { if (is_open) has_open_paths_ = true; minima_list_sorted_ = false; AddPaths_(paths, polytype, is_open, vertex_lists_, minima_list_); - } + } - void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) + void ClipperBase::AddReuseableData(const ReuseableDataContainer64& reuseable_data) { - // nb: reuseable_data will continue to own the vertices + // nb: reuseable_data will continue to own the vertices // and remains responsible for their clean up. succeeded_ = false; minima_list_sorted_ = false; @@ -1117,7 +1118,6 @@ namespace Clipper2Lib { } } - bool IsValidAelOrder(const Active& resident, const Active& newcomer) { if (newcomer.curr_x != resident.curr_x) @@ -1149,8 +1149,8 @@ namespace Clipper2Lib { //resident must also have just been inserted else if (resident.is_left_bound != newcomerIsLeft) return newcomerIsLeft; - else if (CrossProduct(PrevPrevVertex(resident)->pt, - resident.bot, resident.top) == 0) return true; + else if (IsCollinear(PrevPrevVertex(resident)->pt, + resident.bot, resident.top)) return true; else //compare turning direction of the alternate bound return (CrossProduct(PrevPrevVertex(resident)->pt, @@ -1385,7 +1385,7 @@ namespace Clipper2Lib { { if (IsJoined(e1)) Split(e1, pt); if (IsJoined(e2)) Split(e2, pt); - + if (IsFront(e1) == IsFront(e2)) { if (IsOpenEnd(e1)) @@ -1409,7 +1409,7 @@ namespace Clipper2Lib { { Active* e = GetPrevHotEdge(e1); if (!e) - outrec.owner = nullptr; + outrec.owner = nullptr; else SetOwner(&outrec, e->outrec); // nb: outRec.owner here is likely NOT the real @@ -1476,7 +1476,7 @@ namespace Clipper2Lib { e2.outrec->pts = e1.outrec->pts; e1.outrec->pts = nullptr; } - else + else SetOwner(e2.outrec, e1.outrec); //and e1 and e2 are maxima and are about to be dropped from the Actives list. @@ -1526,7 +1526,6 @@ namespace Clipper2Lib { return new_op; } - void ClipperBase::CleanCollinear(OutRec* outrec) { outrec = GetRealOutRec(outrec); @@ -1541,7 +1540,7 @@ namespace Clipper2Lib { for (; ; ) { //NB if preserveCollinear == true, then only remove 180 deg. spikes - if ((CrossProduct(op2->prev->pt, op2->pt, op2->next->pt) == 0) && + if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt) && (op2->pt == op2->prev->pt || op2->pt == op2->next->pt || !preserve_collinear_ || DotProduct(op2->prev->pt, op2->pt, op2->next->pt) < 0)) @@ -1566,14 +1565,14 @@ namespace Clipper2Lib { void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp) { - // splitOp.prev -> splitOp && + // splitOp.prev -> splitOp && // splitOp.next -> splitOp.next.next are intersecting OutPt* prevOp = splitOp->prev; OutPt* nextNextOp = splitOp->next->next; outrec->pts = prevOp; Point64 ip; - GetIntersectPoint(prevOp->pt, splitOp->pt, + GetSegmentIntersectPt(prevOp->pt, splitOp->pt, splitOp->next->pt, nextNextOp->pt, ip); #ifdef USINGZ @@ -1617,7 +1616,7 @@ namespace Clipper2Lib { { OutRec* newOr = NewOutRec(); newOr->owner = outrec->owner; - + splitOp->outrec = newOr; splitOp->next->outrec = newOr; OutPt* newOp = new OutPt(ip, newOr); @@ -1772,12 +1771,12 @@ namespace Clipper2Lib { } - OutPt* ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) + void ClipperBase::IntersectEdges(Active& e1, Active& e2, const Point64& pt) { //MANAGE OPEN PATH INTERSECTIONS SEPARATELY ... if (has_open_paths_ && (IsOpen(e1) || IsOpen(e2))) { - if (IsOpen(e1) && IsOpen(e2)) return nullptr; + if (IsOpen(e1) && IsOpen(e2)) return; Active* edge_o, * edge_c; if (IsOpen(e1)) { @@ -1791,29 +1790,40 @@ namespace Clipper2Lib { } if (IsJoined(*edge_c)) Split(*edge_c, pt); // needed for safety - if (abs(edge_c->wind_cnt) != 1) return nullptr; + if (abs(edge_c->wind_cnt) != 1) return; switch (cliptype_) { case ClipType::Union: - if (!IsHotEdge(*edge_c)) return nullptr; + if (!IsHotEdge(*edge_c)) return; break; default: if (edge_c->local_min->polytype == PathType::Subject) - return nullptr; + return; } switch (fillrule_) { - case FillRule::Positive: if (edge_c->wind_cnt != 1) return nullptr; break; - case FillRule::Negative: if (edge_c->wind_cnt != -1) return nullptr; break; - default: if (std::abs(edge_c->wind_cnt) != 1) return nullptr; break; + case FillRule::Positive: + if (edge_c->wind_cnt != 1) return; + break; + case FillRule::Negative: + if (edge_c->wind_cnt != -1) return; + break; + default: + if (std::abs(edge_c->wind_cnt) != 1) return; } +#ifdef USINGZ OutPt* resultOp; +#endif //toggle contribution ... if (IsHotEdge(*edge_o)) { +#ifdef USINGZ resultOp = AddOutPt(*edge_o, pt); +#else + AddOutPt(*edge_o, pt); +#endif if (IsFront(*edge_o)) edge_o->outrec->front_edge = nullptr; else edge_o->outrec->back_edge = nullptr; edge_o->outrec = nullptr; @@ -1833,18 +1843,26 @@ namespace Clipper2Lib { SetSides(*e3->outrec, *edge_o, *e3); else SetSides(*e3->outrec, *e3, *edge_o); - return e3->outrec->pts; + return; } else +#ifdef USINGZ resultOp = StartOpenPath(*edge_o, pt); +#else + StartOpenPath(*edge_o, pt); +#endif } else +#ifdef USINGZ resultOp = StartOpenPath(*edge_o, pt); +#else + StartOpenPath(*edge_o, pt); +#endif #ifdef USINGZ if (zCallback_) SetZ(*edge_o, *edge_c, resultOp->pt); #endif - return resultOp; + return; } // end of an open path intersection //MANAGING CLOSED PATHS FROM HERE ON @@ -1913,22 +1931,25 @@ namespace Clipper2Lib { const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1; const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1; - if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || (!IsHotEdge(e2) && !e2_windcnt_in_01)) - { - return nullptr; - } + if ((!IsHotEdge(e1) && !e1_windcnt_in_01) || + (!IsHotEdge(e2) && !e2_windcnt_in_01)) + return; //NOW PROCESS THE INTERSECTION ... +#ifdef USINGZ OutPt* resultOp = nullptr; +#endif //if both edges are 'hot' ... if (IsHotEdge(e1) && IsHotEdge(e2)) { if ((old_e1_windcnt != 0 && old_e1_windcnt != 1) || (old_e2_windcnt != 0 && old_e2_windcnt != 1) || (e1.local_min->polytype != e2.local_min->polytype && cliptype_ != ClipType::Xor)) { - resultOp = AddLocalMaxPoly(e1, e2, pt); #ifdef USINGZ + resultOp = AddLocalMaxPoly(e1, e2, pt); if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); +#else + AddLocalMaxPoly(e1, e2, pt); #endif } else if (IsFront(e1) || (e1.outrec == e2.outrec)) @@ -1937,19 +1958,20 @@ namespace Clipper2Lib { //it's sensible to split polygons that ony touch at //a common vertex (not at common edges). - resultOp = AddLocalMaxPoly(e1, e2, pt); #ifdef USINGZ + resultOp = AddLocalMaxPoly(e1, e2, pt); OutPt* op2 = AddLocalMinPoly(e1, e2, pt); if (zCallback_ && resultOp) SetZ(e1, e2, resultOp->pt); if (zCallback_) SetZ(e1, e2, op2->pt); #else + AddLocalMaxPoly(e1, e2, pt); AddLocalMinPoly(e1, e2, pt); #endif } else { - resultOp = AddOutPt(e1, pt); #ifdef USINGZ + resultOp = AddOutPt(e1, pt); OutPt* op2 = AddOutPt(e2, pt); if (zCallback_) { @@ -1957,6 +1979,7 @@ namespace Clipper2Lib { SetZ(e1, e2, op2->pt); } #else + AddOutPt(e1, pt); AddOutPt(e2, pt); #endif SwapOutrecs(e1, e2); @@ -1964,17 +1987,21 @@ namespace Clipper2Lib { } else if (IsHotEdge(e1)) { - resultOp = AddOutPt(e1, pt); #ifdef USINGZ + resultOp = AddOutPt(e1, pt); if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddOutPt(e1, pt); #endif SwapOutrecs(e1, e2); } else if (IsHotEdge(e2)) { - resultOp = AddOutPt(e2, pt); #ifdef USINGZ + resultOp = AddOutPt(e2, pt); if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddOutPt(e2, pt); #endif SwapOutrecs(e1, e2); } @@ -2004,33 +2031,53 @@ namespace Clipper2Lib { if (!IsSamePolyType(e1, e2)) { - resultOp = AddLocalMinPoly(e1, e2, pt, false); #ifdef USINGZ + resultOp = AddLocalMinPoly(e1, e2, pt, false); if (zCallback_) SetZ(e1, e2, resultOp->pt); +#else + AddLocalMinPoly(e1, e2, pt, false); #endif } else if (old_e1_windcnt == 1 && old_e2_windcnt == 1) { +#ifdef USINGZ resultOp = nullptr; +#endif switch (cliptype_) { case ClipType::Union: if (e1Wc2 <= 0 && e2Wc2 <= 0) +#ifdef USINGZ resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif break; case ClipType::Difference: if (((GetPolyType(e1) == PathType::Clip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || ((GetPolyType(e1) == PathType::Subject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) { +#ifdef USINGZ resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif } break; case ClipType::Xor: +#ifdef USINGZ resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif break; default: if (e1Wc2 > 0 && e2Wc2 > 0) +#ifdef USINGZ resultOp = AddLocalMinPoly(e1, e2, pt, false); +#else + AddLocalMinPoly(e1, e2, pt, false); +#endif break; } #ifdef USINGZ @@ -2038,7 +2085,6 @@ namespace Clipper2Lib { #endif } } - return resultOp; } inline void ClipperBase::DeleteFromAEL(Active& e) @@ -2065,7 +2111,7 @@ namespace Clipper2Lib { e->next_in_sel = e->next_in_ael; e->jump = e->next_in_sel; if (e->join_with == JoinWith::Left) - e->curr_x = e->prev_in_ael->curr_x; // also avoids complications + e->curr_x = e->prev_in_ael->curr_x; // also avoids complications else e->curr_x = TopX(*e, top_y); e = e->next_in_ael; @@ -2138,7 +2184,7 @@ namespace Clipper2Lib { if (outrecHasEdges) { OutPt* opA = outrec->pts, * opZ = opA->next; - while (opP != opZ && opP->prev->pt.y == curr_y) + while (opP != opZ && opP->prev->pt.y == curr_y) opP = opP->prev; while (opN != opA && opN->next->pt.y == curr_y) opN = opN->next; @@ -2150,7 +2196,7 @@ namespace Clipper2Lib { while (opN->next != opP && opN->next->pt.y == curr_y) opN = opN->next; } - bool result = + bool result = SetHorzSegHeadingForward(hs, opP, opN) && !hs.left_op->horz; @@ -2160,13 +2206,14 @@ namespace Clipper2Lib { hs.right_op = nullptr; // (for sorting) return result; } - + void ClipperBase::ConvertHorzSegsToJoins() { - auto j = std::count_if(horz_seg_list_.begin(), + auto j = std::count_if(horz_seg_list_.begin(), horz_seg_list_.end(), [](HorzSegment& hs) { return UpdateHorzSegment(hs); }); if (j < 2) return; + std::stable_sort(horz_seg_list_.begin(), horz_seg_list_.end(), HorzSegSorter()); HorzSegmentList::iterator hs1 = horz_seg_list_.begin(), hs2; @@ -2207,8 +2254,8 @@ namespace Clipper2Lib { DuplicateOp(hs1->left_op, false)); horz_join_list_.push_back(join); } - } - } + } + } } void MoveSplits(OutRec* fromOr, OutRec* toOr) @@ -2301,7 +2348,7 @@ namespace Clipper2Lib { void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y) { Point64 ip; - if (!GetIntersectPoint(e1.bot, e1.top, e2.bot, e2.top, ip)) + if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip)) ip = Point64(e1.curr_x, top_y); //parallel edges //rounding errors can occasionally place the calculated intersection @@ -2321,7 +2368,7 @@ namespace Clipper2Lib { ip = GetClosestPointOnSegment(ip, e1.bot, e1.top); else if (abs_dx2 > 100) ip = GetClosestPointOnSegment(ip, e2.bot, e2.top); - else + else { if (ip.y < top_y) ip.y = top_y; else ip.y = bot_y_; @@ -2453,7 +2500,7 @@ namespace Clipper2Lib { horz_seg_list_.push_back(HorzSegment(op)); } - bool ClipperBase::ResetHorzDirection(const Active& horz, + bool ClipperBase::ResetHorzDirection(const Active& horz, const Vertex* max_vertex, int64_t& horz_left, int64_t& horz_right) { if (horz.bot.x == horz.top.x) @@ -2536,8 +2583,8 @@ namespace Clipper2Lib { if (IsHotEdge(horz) && IsJoined(*e)) Split(*e, e->top); - //if (IsHotEdge(horz) != IsHotEdge(*e)) - // DoError(undefined_error_i); + //if (IsHotEdge(horz) != IsHotEdge(*e)) + // DoError(undefined_error_i); if (IsHotEdge(horz)) { @@ -2641,7 +2688,7 @@ namespace Clipper2Lib { ResetHorzDirection(horz, vertex_max, horz_left, horz_right); } - if (IsHotEdge(horz)) + if (IsHotEdge(horz)) { OutPt* op = AddOutPt(horz, horz.top); AddTrialHorzJoin(op); @@ -2754,21 +2801,23 @@ namespace Clipper2Lib { } } - void ClipperBase::CheckJoinLeft(Active& e, + void ClipperBase::CheckJoinLeft(Active& e, const Point64& pt, bool check_curr_x) { Active* prev = e.prev_in_ael; - if (IsOpen(e) || !IsHotEdge(e) || !prev || - IsOpen(*prev) || !IsHotEdge(*prev)) return; + if (!prev || + !IsHotEdge(e) || !IsHotEdge(*prev) || + IsHorizontal(e) || IsHorizontal(*prev) || + IsOpen(e) || IsOpen(*prev) ) return; if ((pt.y < e.top.y + 2 || pt.y < prev->top.y + 2) && - ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins + ((e.bot.y > pt.y) || (prev->bot.y > pt.y))) return; // avoid trivial joins if (check_curr_x) { - if (DistanceFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return; + if (PerpendicDistFromLineSqrd(pt, prev->bot, prev->top) > 0.25) return; } else if (e.curr_x != prev->curr_x) return; - if (CrossProduct(e.top, pt, prev->top)) return; + if (!IsCollinear(e.top, pt, prev->top)) return; if (e.outrec->idx == prev->outrec->idx) AddLocalMaxPoly(*prev, e, pt); @@ -2780,22 +2829,24 @@ namespace Clipper2Lib { e.join_with = JoinWith::Left; } - void ClipperBase::CheckJoinRight(Active& e, + void ClipperBase::CheckJoinRight(Active& e, const Point64& pt, bool check_curr_x) { Active* next = e.next_in_ael; - if (IsOpen(e) || !IsHotEdge(e) || - !next || IsOpen(*next) || !IsHotEdge(*next)) return; + if (!next || + !IsHotEdge(e) || !IsHotEdge(*next) || + IsHorizontal(e) || IsHorizontal(*next) || + IsOpen(e) || IsOpen(*next)) return; if ((pt.y < e.top.y +2 || pt.y < next->top.y +2) && - ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins + ((e.bot.y > pt.y) || (next->bot.y > pt.y))) return; // avoid trivial joins if (check_curr_x) { - if (DistanceFromLineSqrd(pt, next->bot, next->top) > 0.35) return; + if (PerpendicDistFromLineSqrd(pt, next->bot, next->top) > 0.35) return; } else if (e.curr_x != next->curr_x) return; - if (CrossProduct(e.top, pt, next->top)) return; - + if (!IsCollinear(e.top, pt, next->top)) return; + if (e.outrec->idx == next->outrec->idx) AddLocalMaxPoly(e, *next, pt); else if (e.outrec->idx < next->outrec->idx) @@ -2863,7 +2914,7 @@ namespace Clipper2Lib { op2 = op2->next; } - if (path.size() == 3 && IsVerySmallTriangle(*op2)) return false; + if (!isOpen && path.size() == 3 && IsVerySmallTriangle(*op2)) return false; else return true; } @@ -2872,8 +2923,8 @@ namespace Clipper2Lib { if (!outrec->pts) return false; if (!outrec->bounds.IsEmpty()) return true; CleanCollinear(outrec); - if (!outrec->pts || - !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){ + if (!outrec->pts || + !BuildPath64(outrec->pts, reverse_solution_, false, outrec->path)){ return false;} outrec->bounds = GetBounds(outrec->path); return true; @@ -2887,10 +2938,10 @@ namespace Clipper2Lib { if(!split || split == outrec || split->recursive_split == outrec) continue; split->recursive_split = outrec; // prevent infinite loops - if (split->splits && CheckSplitOwner(outrec, split->splits)) + if (split->splits && CheckSplitOwner(outrec, split->splits)) return true; - else if (CheckBounds(split) && - IsValidOwner(outrec, split) && + else if (CheckBounds(split) && + IsValidOwner(outrec, split) && split->bounds.Contains(outrec->bounds) && Path1InsidePath2(outrec->pts, split->pts)) { @@ -2919,7 +2970,7 @@ namespace Clipper2Lib { if (outrec->owner) { - if (!outrec->owner->polypath) + if (!outrec->owner->polypath) RecursiveCheckOwners(outrec->owner, polypath); outrec->polypath = outrec->owner->polypath->AddChild(outrec->path); } @@ -2968,7 +3019,7 @@ namespace Clipper2Lib { open_paths.resize(0); if (has_open_paths_) open_paths.reserve(outrec_list_.size()); - + // outrec_list_.size() is not static here because // CheckBounds below can indirectly add additional // OutRec (via FixOutRecPts & CleanCollinear) @@ -2991,7 +3042,7 @@ namespace Clipper2Lib { bool BuildPathD(OutPt* op, bool reverse, bool isOpen, PathD& path, double inv_scale) { - if (!op || op->next == op || (!isOpen && op->next == op->prev)) + if (!op || op->next == op || (!isOpen && op->next == op->prev)) return false; path.resize(0); @@ -3024,7 +3075,7 @@ namespace Clipper2Lib { #else path.push_back(PointD(lastPt.x * inv_scale, lastPt.y * inv_scale)); #endif - + } if (reverse) op2 = op2->prev; diff --git a/godot/thirdparty/clipper2/src/clipper.offset.cpp b/godot/thirdparty/clipper2/src/clipper.offset.cpp index 0282aa49..508a7f08 100644 --- a/godot/thirdparty/clipper2/src/clipper.offset.cpp +++ b/godot/thirdparty/clipper2/src/clipper.offset.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 28 November 2023 * +* Date : 17 April 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : Path Offset (Inflate/Shrink) * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -20,60 +20,19 @@ const double floating_point_tolerance = 1e-12; // Miscellaneous methods //------------------------------------------------------------------------------ -inline bool ToggleBoolIf(bool val, bool condition) +std::optional GetLowestClosedPathIdx(const Paths64& paths) { - return condition ? !val : val; -} - -void GetMultiBounds(const Paths64& paths, std::vector& recList) -{ - recList.reserve(paths.size()); - for (const Path64& path : paths) - { - if (path.size() < 1) - { - recList.push_back(InvalidRect64); - continue; - } - int64_t x = path[0].x, y = path[0].y; - Rect64 r = Rect64(x, y, x, y); - for (const Point64& pt : path) - { - if (pt.y > r.bottom) r.bottom = pt.y; - else if (pt.y < r.top) r.top = pt.y; - if (pt.x > r.right) r.right = pt.x; - else if (pt.x < r.left) r.left = pt.x; - } - recList.push_back(r); - } -} - -bool ValidateBounds(std::vector& recList, double delta) -{ - int64_t int_delta = static_cast(delta); - int64_t big = MAX_COORD - int_delta; - int64_t small = MIN_COORD + int_delta; - for (const Rect64& r : recList) - { - if (!r.IsValid()) continue; // ignore invalid paths - else if (r.left < small || r.right > big || - r.top < small || r.bottom > big) return false; - } - return true; -} - -int GetLowestClosedPathIdx(std::vector& boundsList) -{ - int i = -1, result = -1; + std::optional result; Point64 botPt = Point64(INT64_MAX, INT64_MIN); - for (const Rect64& r : boundsList) - { - ++i; - if (!r.IsValid()) continue; // ignore invalid paths - else if (r.bottom > botPt.y || (r.bottom == botPt.y && r.left < botPt.x)) + for (size_t i = 0; i < paths.size(); ++i) + { + for (const Point64& pt : paths[i]) { - botPt = Point64(r.left, r.bottom); - result = static_cast(i); + if ((pt.y < botPt.y) || + ((pt.y == botPt.y) && (pt.x >= botPt.x))) continue; + result = i; + botPt.x = pt.x; + botPt.y = pt.y; } } return result; @@ -96,14 +55,14 @@ inline bool AlmostZero(double value, double epsilon = 0.001) return std::fabs(value) < epsilon; } -inline double Hypot(double x, double y) +inline double Hypot(double x, double y) { //see https://stackoverflow.com/a/32436148/359538 return std::sqrt(x * x + y * y); } inline PointD NormalizeVector(const PointD& vec) -{ +{ double h = Hypot(vec.x, vec.y); if (AlmostZero(h)) return PointD(0,0); double inverseHypot = 1 / h; @@ -164,30 +123,21 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType for (Path64& p: paths_in) StripDuplicates(p, is_joined); - // get bounds of each path --> bounds_list - GetMultiBounds(paths_in, bounds_list); - if (end_type == EndType::Polygon) { - is_hole_list.reserve(paths_in.size()); - for (const Path64& path : paths_in) - is_hole_list.push_back(Area(path) < 0); - lowest_path_idx = GetLowestClosedPathIdx(bounds_list); + lowest_path_idx = GetLowestClosedPathIdx(paths_in); // the lowermost path must be an outer path, so if its orientation is negative, // then flag the whole group is 'reversed' (will negate delta etc.) // as this is much more efficient than reversing every path. - is_reversed = (lowest_path_idx >= 0) && is_hole_list[lowest_path_idx]; - if (is_reversed) is_hole_list.flip(); + is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0; } else { - lowest_path_idx = -1; + lowest_path_idx = std::nullopt; is_reversed = false; - is_hole_list.resize(paths_in.size()); } } - //------------------------------------------------------------------------------ // ClipperOffset methods //------------------------------------------------------------------------------ @@ -216,66 +166,29 @@ void ClipperOffset::BuildNormals(const Path64& path) norms.push_back(GetUnitNormal(*path_stop_iter, *(path.cbegin()))); } -inline PointD TranslatePoint(const PointD& pt, double dx, double dy) -{ -#ifdef USINGZ - return PointD(pt.x + dx, pt.y + dy, pt.z); -#else - return PointD(pt.x + dx, pt.y + dy); -#endif -} - -inline PointD ReflectPoint(const PointD& pt, const PointD& pivot) -{ -#ifdef USINGZ - return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y), pt.z); -#else - return PointD(pivot.x + (pivot.x - pt.x), pivot.y + (pivot.y - pt.y)); -#endif -} - -PointD IntersectPoint(const PointD& pt1a, const PointD& pt1b, - const PointD& pt2a, const PointD& pt2b) -{ - if (pt1a.x == pt1b.x) //vertical - { - if (pt2a.x == pt2b.x) return PointD(0, 0); - - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - return PointD(pt1a.x, m2 * pt1a.x + b2); - } - else if (pt2a.x == pt2b.x) //vertical - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - return PointD(pt2a.x, m1 * pt2a.x + b1); - } - else - { - double m1 = (pt1b.y - pt1a.y) / (pt1b.x - pt1a.x); - double b1 = pt1a.y - m1 * pt1a.x; - double m2 = (pt2b.y - pt2a.y) / (pt2b.x - pt2a.x); - double b2 = pt2a.y - m2 * pt2a.x; - if (m1 == m2) return PointD(0, 0); - double x = (b2 - b1) / (m1 - m2); - return PointD(x, m1 * x + b1); - } -} - void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k) { PointD pt1, pt2; if (j == k) { double abs_delta = std::abs(group_delta_); +#ifdef USINGZ + pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y, path[j].z); + pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y, path[j].z); +#else pt1 = PointD(path[j].x - abs_delta * norms[j].x, path[j].y - abs_delta * norms[j].y); pt2 = PointD(path[j].x + abs_delta * norms[j].x, path[j].y + abs_delta * norms[j].y); - } +#endif + } else { +#ifdef USINGZ + pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y, path[j].z); + pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y, path[j].z); +#else pt1 = PointD(path[j].x + group_delta_ * norms[k].x, path[j].y + group_delta_ * norms[k].y); pt2 = PointD(path[j].x + group_delta_ * norms[j].x, path[j].y + group_delta_ * norms[j].y); +#endif } path_out.push_back(Point64(pt1)); path_out.push_back(Point64(pt2)); @@ -284,7 +197,7 @@ void ClipperOffset::DoBevel(const Path64& path, size_t j, size_t k) void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) { PointD vec; - if (j == k) + if (j == k) vec = PointD(norms[j].y, -norms[j].x); else vec = GetAvgUnitVector( @@ -304,10 +217,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) if (j == k) { PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); -#ifdef USINGZ - pt.z = ptQ.z; -#endif + PointD pt = ptQ; + GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); //get the second intersect point through reflecion path_out.push_back(Point64(ReflectPoint(pt, ptQ))); path_out.push_back(Point64(pt)); @@ -315,10 +226,8 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k) else { PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_); - PointD pt = IntersectPoint(pt1, pt2, pt3, pt4); -#ifdef USINGZ - pt.z = ptQ.z; -#endif + PointD pt = ptQ; + GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt); path_out.push_back(Point64(pt)); //get the second intersect point through reflecion path_out.push_back(Point64(ReflectPoint(pt, ptQ))); @@ -343,7 +252,7 @@ void ClipperOffset::DoMiter(const Path64& path, size_t j, size_t k, double cos_a void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle) { if (deltaCallback64_) { - // when deltaCallback64_ is assigned, group_delta_ won't be constant, + // when deltaCallback64_ is assigned, group_delta_ won't be constant, // so we'll need to do the following calculations for *every* vertex. double abs_delta = std::fabs(group_delta_); double arcTol = (arc_tolerance_ > floating_point_tolerance ? @@ -387,7 +296,7 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size // sin(A) < 0: right turning // cos(A) < 0: change in angle is more than 90 degree - if (path[j] == path[k]) { k = j; return; } + if (path[j] == path[k]) return; double sin_a = CrossProduct(norms[j], norms[k]); double cos_a = DotProduct(norms[j], norms[k]); @@ -404,18 +313,29 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size return; } - if (cos_a > -0.99 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) + if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593) { - // is concave + // is concave (so insert 3 points that will create a negative region) +#ifdef USINGZ + path_out.push_back(Point64(GetPerpendic(path[j], norms[k], group_delta_), path[j].z)); +#else path_out.push_back(GetPerpendic(path[j], norms[k], group_delta_)); - // this extra point is the only (simple) way to ensure that - // path reversals are fully cleaned with the trailing clipper - path_out.push_back(path[j]); // (#405) +#endif + + // this extra point is the only simple way to ensure that path reversals + // (ie over-shrunk paths) are fully cleaned out with the trailing union op. + // However it's probably safe to skip this whenever an angle is almost flat. + if (cos_a < 0.99) path_out.push_back(path[j]); // (#405) + +#ifdef USINGZ + path_out.push_back(Point64(GetPerpendic(path[j], norms[j], group_delta_), path[j].z)); +#else path_out.push_back(GetPerpendic(path[j], norms[j], group_delta_)); +#endif } - else if (cos_a > 0.999 && join_type_ != JoinType::Round) + else if (cos_a > 0.999 && join_type_ != JoinType::Round) { - // almost straight - less than 2.5 degree (#424, #482, #526 & #724) + // almost straight - less than 2.5 degree (#424, #482, #526 & #724) DoMiter(path, j, k, cos_a); } else if (join_type_ == JoinType::Miter) @@ -435,9 +355,9 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size void ClipperOffset::OffsetPolygon(Group& group, const Path64& path) { path_out.clear(); - for (Path64::size_type j = 0, k = path.size() -1; j < path.size(); k = j, ++j) - OffsetPoint(group, path, j, k); - solution.push_back(path_out); + for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j) + OffsetPoint(group, path, j, k); + solution->push_back(path_out); } void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path) @@ -445,8 +365,8 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path) OffsetPolygon(group, path); Path64 reverse_path(path); std::reverse(reverse_path.begin(), reverse_path.end()); - - //rebuild normals // BuildNormals(path); + + //rebuild normals std::reverse(norms.begin(), norms.end()); norms.push_back(norms[0]); norms.erase(norms.begin()); @@ -459,7 +379,7 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) { // do the line start cap if (deltaCallback64_) group_delta_ = deltaCallback64_(path, norms, 0, 0); - + if (std::fabs(group_delta_) <= floating_point_tolerance) path_out.push_back(path[0]); else @@ -477,13 +397,13 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) break; } } - + size_t highI = path.size() - 1; // offset the left side going forward for (Path64::size_type j = 1, k = 0; j < highI; k = j, ++j) OffsetPoint(group, path, j, k); - // reverse normals + // reverse normals for (size_t i = highI; i > 0; --i) norms[i] = PointD(-norms[i - 1].x, -norms[i - 1].y); norms[0] = norms[highI]; @@ -510,41 +430,34 @@ void ClipperOffset::OffsetOpenPath(Group& group, const Path64& path) } } - for (size_t j = highI, k = 0; j > 0; k = j, --j) + for (size_t j = highI -1, k = highI; j > 0; k = j, --j) OffsetPoint(group, path, j, k); - solution.push_back(path_out); + solution->push_back(path_out); } void ClipperOffset::DoGroupOffset(Group& group) { if (group.end_type == EndType::Polygon) { - // a straight path (2 points) can now also be 'polygon' offset + // a straight path (2 points) can now also be 'polygon' offset // where the ends will be treated as (180 deg.) joins - if (group.lowest_path_idx < 0) delta_ = std::abs(delta_); + if (!group.lowest_path_idx.has_value()) delta_ = std::abs(delta_); group_delta_ = (group.is_reversed) ? -delta_ : delta_; } else group_delta_ = std::abs(delta_);// *0.5; double abs_delta = std::fabs(group_delta_); - if (!ValidateBounds(group.bounds_list, abs_delta)) - { - DoError(range_error_i); - error_code_ |= range_error_i; - return; - } - join_type_ = group.join_type; end_type_ = group.end_type; if (group.join_type == JoinType::Round || group.end_type == EndType::Round) { - // calculate a sensible number of steps (for 360 deg for the given offset) - // arcTol - when arc_tolerance_ is undefined (0), the amount of - // curve imprecision that's allowed is based on the size of the - // offset (delta). Obviously very large offsets will almost always - // require much less precision. See also offset_triginometry2.svg + // calculate the number of steps required to approximate a circle + // (see http://www.angusj.com/clipper2/Docs/Trigonometry.htm) + // arcTol - when arc_tolerance_ is undefined (0) then curve imprecision + // will be relative to the size of the offset (delta). Obviously very + //large offsets will almost always require much less precision. double arcTol = (arc_tolerance_ > floating_point_tolerance ? std::min(abs_delta, arc_tolerance_) : std::log10(2 + abs_delta) * default_arc_tolerance); @@ -556,24 +469,29 @@ void ClipperOffset::DoGroupOffset(Group& group) steps_per_rad_ = steps_per_360 / (2 * PI); } - std::vector::const_iterator path_rect_it = group.bounds_list.cbegin(); - std::vector::const_iterator is_hole_it = group.is_hole_list.cbegin(); + //double min_area = PI * Sqr(group_delta_); Paths64::const_iterator path_in_it = group.paths_in.cbegin(); - for ( ; path_in_it != group.paths_in.cend(); ++path_in_it, ++path_rect_it, ++is_hole_it) + for ( ; path_in_it != group.paths_in.cend(); ++path_in_it) { - if (!path_rect_it->IsValid()) continue; Path64::size_type pathLen = path_in_it->size(); path_out.clear(); if (pathLen == 1) // single point { + if (deltaCallback64_) + { + group_delta_ = deltaCallback64_(*path_in_it, norms, 0, 0); + if (group.is_reversed) group_delta_ = -group_delta_; + abs_delta = std::fabs(group_delta_); + } + if (group_delta_ < 1) continue; const Point64& pt = (*path_in_it)[0]; //single vertex so build a circle or square ... if (group.join_type == JoinType::Round) { double radius = abs_delta; - int steps = static_cast(std::ceil(steps_per_rad_ * 2 * PI)); //#617 + size_t steps = steps_per_rad_ > 0 ? static_cast(std::ceil(steps_per_rad_ * 2 * PI)) : 0; //#617 path_out = Ellipse(pt, radius, radius, steps); #ifdef USINGZ for (auto& p : path_out) p.z = pt.z; @@ -588,19 +506,14 @@ void ClipperOffset::DoGroupOffset(Group& group) for (auto& p : path_out) p.z = pt.z; #endif } - solution.push_back(path_out); - continue; - } // end of offsetting a single point - // when shrinking outer paths, make sure they can shrink this far (#593) - // also when shrinking holes, make sure they too can shrink this far (#715) - if ((group_delta_ > 0) == ToggleBoolIf(*is_hole_it, group.is_reversed) && - (std::min(path_rect_it->Width(), path_rect_it->Height()) <= -group_delta_ * 2) ) - continue; + solution->push_back(path_out); + continue; + } // end of offsetting a single point if ((pathLen == 2) && (group.end_type == EndType::Joined)) - end_type_ = (group.join_type == JoinType::Round) ? - EndType::Round : + end_type_ = (group.join_type == JoinType::Round) ? + EndType::Round : EndType::Square; BuildNormals(*path_in_it); @@ -610,6 +523,16 @@ void ClipperOffset::DoGroupOffset(Group& group) } } +#ifdef USINGZ +void ClipperOffset::ZCB(const Point64& bot1, const Point64& top1, + const Point64& bot2, const Point64& top2, Point64& ip) +{ + if (bot1.z && ((bot1.z == bot2.z) || (bot1.z == top2.z))) ip.z = bot1.z; + else if (bot2.z && (bot2.z == top1.z)) ip.z = bot2.z; + else if (top1.z && (top1.z == top2.z)) ip.z = top1.z; + else if (zCallback64_) zCallback64_(bot1, top1, bot2, top2, ip); +} +#endif size_t ClipperOffset::CalcSolutionCapacity() { @@ -635,40 +558,35 @@ bool ClipperOffset::CheckReverseOrientation() void ClipperOffset::ExecuteInternal(double delta) { error_code_ = 0; - solution.clear(); if (groups_.size() == 0) return; - solution.reserve(CalcSolutionCapacity()); + solution->reserve(CalcSolutionCapacity()); - if (std::abs(delta) < 0.5) // ie: offset is insignificant + if (std::abs(delta) < 0.5) // ie: offset is insignificant { Paths64::size_type sol_size = 0; for (const Group& group : groups_) sol_size += group.paths_in.size(); - solution.reserve(sol_size); + solution->reserve(sol_size); for (const Group& group : groups_) - copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(solution)); - return; + copy(group.paths_in.begin(), group.paths_in.end(), back_inserter(*solution)); } + else + { - temp_lim_ = (miter_limit_ <= 1) ? - 2.0 : - 2.0 / (miter_limit_ * miter_limit_); + temp_lim_ = (miter_limit_ <= 1) ? + 2.0 : + 2.0 / (miter_limit_ * miter_limit_); - delta_ = delta; - std::vector::iterator git; - for (git = groups_.begin(); git != groups_.end(); ++git) - { - DoGroupOffset(*git); - if (!error_code_) continue; // all OK - solution.clear(); + delta_ = delta; + std::vector::iterator git; + for (git = groups_.begin(); git != groups_.end(); ++git) + { + DoGroupOffset(*git); + if (!error_code_) continue; // all OK + solution->clear(); + } } -} -void ClipperOffset::Execute(double delta, Paths64& paths) -{ - paths.clear(); - - ExecuteInternal(delta); - if (!solution.size()) return; + if (!solution->size()) return; bool paths_reversed = CheckReverseOrientation(); //clean up self-intersections ... @@ -677,41 +595,45 @@ void ClipperOffset::Execute(double delta, Paths64& paths) //the solution should retain the orientation of the input c.ReverseSolution(reverse_solution_ != paths_reversed); #ifdef USINGZ - if (zCallback64_) { c.SetZCallback(zCallback64_); } + auto fp = std::bind(&ClipperOffset::ZCB, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3, + std::placeholders::_4, std::placeholders::_5); + c.SetZCallback(fp); #endif - c.AddSubject(solution); - if (paths_reversed) - c.Execute(ClipType::Union, FillRule::Negative, paths); + c.AddSubject(*solution); + if (solution_tree) + { + if (paths_reversed) + c.Execute(ClipType::Union, FillRule::Negative, *solution_tree); + else + c.Execute(ClipType::Union, FillRule::Positive, *solution_tree); + } else - c.Execute(ClipType::Union, FillRule::Positive, paths); + { + if (paths_reversed) + c.Execute(ClipType::Union, FillRule::Negative, *solution); + else + c.Execute(ClipType::Union, FillRule::Positive, *solution); + } +} + +void ClipperOffset::Execute(double delta, Paths64& paths) +{ + paths.clear(); + solution = &paths; + solution_tree = nullptr; + ExecuteInternal(delta); } void ClipperOffset::Execute(double delta, PolyTree64& polytree) { polytree.Clear(); - + solution_tree = &polytree; + solution = new Paths64(); ExecuteInternal(delta); - if (!solution.size()) return; - - bool paths_reversed = CheckReverseOrientation(); - //clean up self-intersections ... - Clipper64 c; - c.PreserveCollinear(false); - //the solution should retain the orientation of the input - c.ReverseSolution (reverse_solution_ != paths_reversed); -#ifdef USINGZ - if (zCallback64_) { - c.SetZCallback(zCallback64_); - } -#endif - c.AddSubject(solution); - - - if (paths_reversed) - c.Execute(ClipType::Union, FillRule::Negative, polytree); - else - c.Execute(ClipType::Union, FillRule::Positive, polytree); + delete solution; + solution = nullptr; } void ClipperOffset::Execute(DeltaCallback64 delta_cb, Paths64& paths) diff --git a/godot/thirdparty/clipper2/src/clipper.rectclip.cpp b/godot/thirdparty/clipper2/src/clipper.rectclip.cpp index 9aa0fc0f..23809b5e 100644 --- a/godot/thirdparty/clipper2/src/clipper.rectclip.cpp +++ b/godot/thirdparty/clipper2/src/clipper.rectclip.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * Author : Angus Johnson * -* Date : 8 September 2023 * +* Date : 5 July 2024 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2023 * +* Copyright : Angus Johnson 2010-2024 * * Purpose : FAST rectangular clipping * * License : http://www.boost.org/LICENSE_1_0.txt * *******************************************************************************/ @@ -71,7 +71,7 @@ namespace Clipper2Lib { return pt1.y == pt2.y; } - inline bool GetSegmentIntersection(const Point64& p1, + bool GetSegmentIntersection(const Point64& p1, const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip) { double res1 = CrossProduct(p1, p3, p4); @@ -113,7 +113,7 @@ namespace Clipper2Lib { if ((res3 > 0) == (res4 > 0)) return false; // segments must intersect to get here - return GetIntersectPoint(p1, p2, p3, p4, ip); + return GetSegmentIntersectPt(p1, p2, p3, p4, ip); } inline bool GetIntersection(const Path64& rectPath, @@ -125,7 +125,7 @@ namespace Clipper2Lib { { case Location::Left: if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) return true; - else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) + else if ((p.y < rectPath[0].y) && GetSegmentIntersection(p, p2, rectPath[0], rectPath[1], ip)) { loc = Location::Top; return true; @@ -180,7 +180,7 @@ namespace Clipper2Lib { else return false; default: // loc == rInside - if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) + if (GetSegmentIntersection(p, p2, rectPath[0], rectPath[3], ip)) { loc = Location::Left; return true; @@ -320,9 +320,9 @@ namespace Clipper2Lib { // this method is only called by InternalExecute. // Later splitting & rejoining won't create additional op's, // though they will change the (non-storage) results_ count. - int curr_idx = static_cast(results_.size()) - 1; + size_t curr_idx = results_.size(); OutPt2* result; - if (curr_idx < 0 || start_new) + if (curr_idx == 0 || start_new) { result = &op_container_.emplace_back(OutPt2()); result->pt = pt; @@ -332,6 +332,7 @@ namespace Clipper2Lib { } else { + --curr_idx; OutPt2* prevOp = results_[curr_idx]; if (prevOp->pt == pt) return prevOp; result = &op_container_.emplace_back(OutPt2()); @@ -349,27 +350,27 @@ namespace Clipper2Lib { void RectClip64::AddCorner(Location prev, Location curr) { if (HeadingClockwise(prev, curr)) - Add(rect_as_path_[static_cast(prev)]); + Add(rect_as_path_[static_cast(prev)]); else - Add(rect_as_path_[static_cast(curr)]); + Add(rect_as_path_[static_cast(curr)]); } void RectClip64::AddCorner(Location& loc, bool isClockwise) { if (isClockwise) { - Add(rect_as_path_[static_cast(loc)]); + Add(rect_as_path_[static_cast(loc)]); loc = GetAdjacentLocation(loc, true); } else { loc = GetAdjacentLocation(loc, false); - Add(rect_as_path_[static_cast(loc)]); + Add(rect_as_path_[static_cast(loc)]); } } void RectClip64::GetNextLocation(const Path64& path, - Location& loc, int& i, int highI) + Location& loc, size_t& i, size_t highI) { switch (loc) { @@ -420,31 +421,52 @@ namespace Clipper2Lib { break; //inner loop } break; - } //switch + } //switch + } + + bool StartLocsAreClockwise(const std::vector& startlocs) + { + int result = 0; + for (size_t i = 1; i < startlocs.size(); ++i) + { + int d = static_cast(startlocs[i]) - static_cast(startlocs[i - 1]); + switch (d) + { + case -1: result -= 1; break; + case 1: result += 1; break; + case -3: result += 1; break; + case 3: result -= 1; break; + } + } + return result > 0; } void RectClip64::ExecuteInternal(const Path64& path) { - int i = 0, highI = static_cast(path.size()) - 1; + if (path.size() < 1) + return; + + size_t highI = path.size() - 1; Location prev = Location::Inside, loc; Location crossing_loc = Location::Inside; Location first_cross_ = Location::Inside; if (!GetLocation(rect_, path[highI], loc)) { - i = highI - 1; - while (i >= 0 && !GetLocation(rect_, path[i], prev)) --i; - if (i < 0) + size_t i = highI; + while (i > 0 && !GetLocation(rect_, path[i - 1], prev)) + --i; + if (i == 0) { // all of path must be inside fRect for (const auto& pt : path) Add(pt); return; } if (prev == Location::Inside) loc = Location::Inside; - i = 0; } - Location startingLoc = loc; + Location starting_loc = loc; /////////////////////////////////////////////////// + size_t i = 0; while (i <= highI) { prev = loc; @@ -454,12 +476,12 @@ namespace Clipper2Lib { if (i > highI) break; Point64 ip, ip2; - Point64 prev_pt = (i) ? - path[static_cast(i - 1)] : + Point64 prev_pt = (i) ? + path[static_cast(i - 1)] : path[highI]; crossing_loc = loc; - if (!GetIntersection(rect_as_path_, + if (!GetIntersection(rect_as_path_, path[i], prev_pt, crossing_loc, ip)) { // ie remaining outside @@ -470,7 +492,7 @@ namespace Clipper2Lib { start_locs_.push_back(prev); prev = GetAdjacentLocation(prev, isClockw); } while (prev != loc); - crossing_loc = crossing_prev; // still not crossed + crossing_loc = crossing_prev; // still not crossed } else if (prev != Location::Inside && prev != loc) { @@ -504,7 +526,7 @@ namespace Clipper2Lib { } else if (prev != Location::Inside) { - // passing right through rect. 'ip' here will be the second + // passing right through rect. 'ip' here will be the second // intersect pt but we'll also need the first intersect pt (ip2) loc = prev; GetIntersection(rect_as_path_, prev_pt, path[i], loc, ip2); @@ -543,7 +565,7 @@ namespace Clipper2Lib { if (first_cross_ == Location::Inside) { // path never intersects - if (startingLoc != Location::Inside) + if (starting_loc != Location::Inside) { // path is outside rect // but being outside, it still may not contain rect @@ -552,11 +574,13 @@ namespace Clipper2Lib { { // yep, the path does fully contain rect // so add rect to the solution + bool is_clockwise_path = StartLocsAreClockwise(start_locs_); for (size_t j = 0; j < 4; ++j) { - Add(rect_as_path_[j]); + size_t k = is_clockwise_path ? j : 3 - j; // reverses result path + Add(rect_as_path_[k]); // we may well need to do some splitting later, so - AddToEdge(edges_[j * 2], results_[0]); + AddToEdge(edges_[k * 2], results_[0]); } } } @@ -589,8 +613,7 @@ namespace Clipper2Lib { OutPt2* op2 = op; do { - if (!CrossProduct(op2->prev->pt, - op2->pt, op2->next->pt)) + if (IsCollinear(op2->prev->pt, op2->pt, op2->next->pt)) { if (op2 == op) { @@ -640,7 +663,7 @@ namespace Clipper2Lib { } } - void RectClip64::TidyEdges(int idx, OutPt2List& cw, OutPt2List& ccw) + void RectClip64::TidyEdges(size_t idx, OutPt2List& cw, OutPt2List& ccw) { if (ccw.empty()) return; bool isHorz = ((idx == 1) || (idx == 3)); @@ -648,7 +671,7 @@ namespace Clipper2Lib { size_t i = 0, j = 0; OutPt2* p1, * p2, * p1a, * p2a, * op, * op2; - while (i < cw.size()) + while (i < cw.size()) { p1 = cw[i]; if (!p1 || p1->next == p1->prev) @@ -825,8 +848,8 @@ namespace Clipper2Lib { OutPt2* op2 = op->next; while (op2 && op2 != op) { - if (CrossProduct(op2->prev->pt, - op2->pt, op2->next->pt) == 0) + if (IsCollinear(op2->prev->pt, + op2->pt, op2->next->pt)) { op = op2->prev; op2 = UnlinkOp(op2); @@ -854,7 +877,7 @@ namespace Clipper2Lib { if (rect_.IsEmpty()) return result; for (const Path64& path : paths) - { + { if (path.size() < 3) continue; path_bounds_ = GetBounds(path); if (!rect_.Intersects(path_bounds_)) @@ -868,9 +891,9 @@ namespace Clipper2Lib { ExecuteInternal(path); CheckEdges(); - for (int i = 0; i < 4; ++i) + for (size_t i = 0; i < 4; ++i) TidyEdges(i, edges_[i * 2], edges_[i * 2 + 1]); - + for (OutPt2*& op : results_) { Path64 tmp = GetPath(op); @@ -925,14 +948,14 @@ namespace Clipper2Lib { op_container_ = std::deque(); start_locs_.clear(); - int i = 1, highI = static_cast(path.size()) - 1; + size_t i = 1, highI = path.size() - 1; Location prev = Location::Inside, loc; Location crossing_loc; if (!GetLocation(rect_, path[0], loc)) { while (i <= highI && !GetLocation(rect_, path[i], prev)) ++i; - if (i > highI) + if (i > highI) { // all of path must be inside fRect for (const auto& pt : path) Add(pt); @@ -953,7 +976,7 @@ namespace Clipper2Lib { Point64 prev_pt = path[static_cast(i - 1)]; crossing_loc = loc; - if (!GetIntersection(rect_as_path_, + if (!GetIntersection(rect_as_path_, path[i], prev_pt, crossing_loc, ip)) { // ie remaining outside @@ -971,10 +994,10 @@ namespace Clipper2Lib { } else if (prev != Location::Inside) { - // passing right through rect. 'ip' here will be the second + // passing right through rect. 'ip' here will be the second // intersect pt but we'll also need the first intersect pt (ip2) crossing_loc = prev; - GetIntersection(rect_as_path_, + GetIntersection(rect_as_path_, prev_pt, path[i], crossing_loc, ip2); Add(ip2, true); Add(ip); @@ -991,14 +1014,14 @@ namespace Clipper2Lib { { Path64 result; if (!op || op == op->next) return result; - op = op->next; // starting at path beginning + op = op->next; // starting at path beginning result.push_back(op->pt); OutPt2 *op2 = op->next; while (op2 != op) { result.push_back(op2->pt); op2 = op2->next; - } + } return result; } diff --git a/godot/thirdparty/manifold/include/manifold/common.h b/godot/thirdparty/manifold/include/manifold/common.h index 4d669e1e..f1200f46 100644 --- a/godot/thirdparty/manifold/include/manifold/common.h +++ b/godot/thirdparty/manifold/include/manifold/common.h @@ -14,9 +14,12 @@ #pragma once #include -#include #include +#ifdef MANIFOLD_DEBUG +#include +#endif + #include "manifold/linalg.h" namespace manifold { @@ -25,9 +28,7 @@ namespace manifold { * @brief Simple math operations. * */ -/** @addtogroup Scalar - * @ingroup Math - * @brief Simple scalar operations. +/** @addtogroup LinAlg * @{ */ namespace la = linalg; @@ -47,6 +48,13 @@ using ivec2 = la::vec; using ivec3 = la::vec; using ivec4 = la::vec; using quat = la::vec; +/** @} */ + +/** @addtogroup Scalar + * @ingroup Math + * @brief Simple scalar operations. + * @{ + */ constexpr double kPi = 3.14159265358979323846264338327950288; constexpr double kTwoPi = 6.28318530717958647692528676655900576; @@ -556,24 +564,6 @@ class Quality { }; /** @} */ -/** @addtogroup Exceptions - * @ingroup Optional - * @brief Custom Exceptions. Exceptions are only thrown if the - * MANIFOLD_EXCEPTIONS flag is set. - * @{ - */ -struct userErr : public virtual std::runtime_error { - using std::runtime_error::runtime_error; -}; -struct topologyErr : public virtual std::runtime_error { - using std::runtime_error::runtime_error; -}; -struct geometryErr : public virtual std::runtime_error { - using std::runtime_error::runtime_error; -}; -using logicErr = std::logic_error; -/** @} */ - /** @addtogroup Debug * @ingroup Optional * @{ @@ -596,10 +586,65 @@ struct ExecutionParams { /// Suppresses printed errors regarding CW triangles. Has no effect if /// processOverlaps is true. bool suppressErrors = false; - /// Deterministic outputs. Will disable some parallel optimizations. - bool deterministic = false; /// Perform optional but recommended triangle cleanups in SimplifyTopology() bool cleanupTriangles = true; }; /** @} */ + +#ifdef MANIFOLD_DEBUG +inline std::ostream& operator<<(std::ostream& stream, const Box& box) { + return stream << "min: " << box.min << ", " + << "max: " << box.max; +} + +inline std::ostream& operator<<(std::ostream& stream, const Rect& box) { + return stream << "min: " << box.min << ", " + << "max: " << box.max; +} + +/** + * Print the contents of this vector to standard output. Only exists if compiled + * with MANIFOLD_DEBUG flag. + */ +template +void Dump(const std::vector& vec) { + std::cout << "Vec = " << std::endl; + for (size_t i = 0; i < vec.size(); ++i) { + std::cout << i << ", " << vec[i] << ", " << std::endl; + } + std::cout << std::endl; +} + +template +void Diff(const std::vector& a, const std::vector& b) { + std::cout << "Diff = " << std::endl; + if (a.size() != b.size()) { + std::cout << "a and b must have the same length, aborting Diff" + << std::endl; + return; + } + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != b[i]) + std::cout << i << ": " << a[i] << ", " << b[i] << std::endl; + } + std::cout << std::endl; +} + +struct Timer { + std::chrono::high_resolution_clock::time_point start, end; + + void Start() { start = std::chrono::high_resolution_clock::now(); } + + void Stop() { end = std::chrono::high_resolution_clock::now(); } + + float Elapsed() { + return std::chrono::duration_cast(end - start) + .count(); + } + void Print(std::string message) { + std::cout << "----------- " << std::round(Elapsed()) << " ms for " + << message << std::endl; + } +}; +#endif } // namespace manifold diff --git a/godot/thirdparty/manifold/include/manifold/cross_section.h b/godot/thirdparty/manifold/include/manifold/cross_section.h deleted file mode 100644 index 1e0ee1ec..00000000 --- a/godot/thirdparty/manifold/include/manifold/cross_section.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2023 The Manifold Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include - -#include "manifold/common.h" -#include "manifold/vec_view.h" - -namespace manifold { - -/** @addtogroup Optional - * @brief Optional features that can be enabled through build flags and may - * require extra dependencies. - * @{ - */ - -struct PathImpl; - -/** - * @brief Two-dimensional cross sections guaranteed to be without - * self-intersections, or overlaps between polygons (from construction onwards). - * This class makes use of the - * [Clipper2](http://www.angusj.com/clipper2/Docs/Overview.htm) library for - * polygon clipping (boolean) and offsetting operations. - */ -class CrossSection { - public: - /** @name Basics - * Copy / move / assignment - */ - ///@{ - CrossSection(); - ~CrossSection(); - - CrossSection(const CrossSection& other); - CrossSection& operator=(const CrossSection& other); - CrossSection(CrossSection&&) noexcept; - CrossSection& operator=(CrossSection&&) noexcept; - ///@} - - // Adapted from Clipper2 docs: - // http://www.angusj.com/clipper2/Docs/Units/Clipper/Types/FillRule.htm - // (Copyright © 2010-2023 Angus Johnson) - /** - * Filling rules defining which polygon sub-regions are considered to be - * inside a given polygon, and which sub-regions will not (based on winding - * numbers). See the [Clipper2 - * docs](http://www.angusj.com/clipper2/Docs/Units/Clipper/Types/FillRule.htm) - * for a detailed explaination with illusrations. - */ - enum class FillRule { - EvenOdd, ///< Only odd numbered sub-regions are filled. - NonZero, ///< Only non-zero sub-regions are filled. - Positive, ///< Only sub-regions with winding counts > 0 are filled. - Negative ///< Only sub-regions with winding counts < 0 are filled. - }; - - // Adapted from Clipper2 docs: - // http://www.angusj.com/clipper2/Docs/Units/Clipper/Types/JoinType.htm - // (Copyright © 2010-2023 Angus Johnson) - /** - * Specifies the treatment of path/contour joins (corners) when offseting - * CrossSections. See the [Clipper2 - * doc](http://www.angusj.com/clipper2/Docs/Units/Clipper/Types/JoinType.htm) - * for illustrations. - */ - enum class JoinType { - Square, /*!< Squaring is applied uniformly at all joins where the internal - join angle is less that 90 degrees. The squared edge will be at - exactly the offset distance from the join vertex. */ - Round, /*!< Rounding is applied to all joins that have convex external - angles, and it maintains the exact offset distance from the join - vertex. */ - Miter /*!< There's a necessary limit to mitered joins (to avoid narrow - angled joins producing excessively long and narrow - [spikes](http://www.angusj.com/clipper2/Docs/Units/Clipper.Offset/Classes/ClipperOffset/Properties/MiterLimit.htm)). - So where mitered joins would exceed a given maximum miter distance - (relative to the offset distance), these are 'squared' instead. */ - }; - - /** @name Input & Output - */ - ///@{ - CrossSection(const SimplePolygon& contour, - FillRule fillrule = FillRule::Positive); - CrossSection(const Polygons& contours, - FillRule fillrule = FillRule::Positive); - CrossSection(const Rect& rect); - Polygons ToPolygons() const; - ///@} - - /** @name Constructors - * Topological ops and primitives - */ - ///@{ - std::vector Decompose() const; - static CrossSection Compose(std::vector&); - static CrossSection Square(const vec2 dims, bool center = false); - static CrossSection Circle(double radius, int circularSegments = 0); - ///@} - - /** @name Information - * Details of the cross-section - */ - ///@{ - bool IsEmpty() const; - int NumVert() const; - int NumContour() const; - Rect Bounds() const; - double Area() const; - ///@} - - /** @name Transformation - */ - ///@{ - CrossSection Translate(const vec2 v) const; - CrossSection Rotate(double degrees) const; - CrossSection Scale(const vec2 s) const; - CrossSection Mirror(const vec2 ax) const; - CrossSection Transform(const mat2x3& m) const; - CrossSection Warp(std::function warpFunc) const; - CrossSection WarpBatch(std::function)> warpFunc) const; - CrossSection Simplify(double epsilon = 1e-6) const; - CrossSection Offset(double delta, JoinType jt, double miter_limit = 2.0, - int circularSegments = 0) const; - ///@} - - /** @name Boolean - * Combine two manifolds - */ - ///@{ - CrossSection Boolean(const CrossSection& second, OpType op) const; - static CrossSection BatchBoolean( - const std::vector& crossSections, OpType op); - CrossSection operator+(const CrossSection&) const; - CrossSection& operator+=(const CrossSection&); - CrossSection operator-(const CrossSection&) const; - CrossSection& operator-=(const CrossSection&); - CrossSection operator^(const CrossSection&) const; - CrossSection& operator^=(const CrossSection&); - ///@} - - /** @name Convex Hull - */ - ///@{ - CrossSection Hull() const; - static CrossSection Hull(const std::vector& crossSections); - static CrossSection Hull(const SimplePolygon pts); - static CrossSection Hull(const Polygons polys); - ///@} - - private: - mutable std::shared_ptr paths_; - mutable mat2x3 transform_ = la::identity; - CrossSection(std::shared_ptr paths); - std::shared_ptr GetPaths() const; -}; -/** @} */ -} // namespace manifold diff --git a/godot/thirdparty/manifold/include/manifold/linalg.h b/godot/thirdparty/manifold/include/manifold/linalg.h index 50756d66..dfcd305b 100644 --- a/godot/thirdparty/manifold/include/manifold/linalg.h +++ b/godot/thirdparty/manifold/include/manifold/linalg.h @@ -43,6 +43,11 @@ #include // For forward definitions of std::ostream #include // For std::enable_if, std::is_same, std::declval +#ifdef MANIFOLD_DEBUG +#include +#include +#endif + // In Visual Studio 2015, `constexpr` applied to a member function implies // `const`, which causes ambiguous overload resolution #if defined(_MSC_VER) && (_MSC_VER <= 1900) @@ -753,8 +758,97 @@ struct std_copysign { /** @addtogroup vec * @ingroup LinAlg - * @brief Small, fixed-length vector type, consisting of exactly M elements of - * type T, and presumed to be a column-vector unless otherwise noted. + * @brief `linalg::vec` defines a fixed-length vector containing exactly + `M` elements of type `T`. + +This data structure can be used to store a wide variety of types of data, +including geometric vectors, points, homogeneous coordinates, plane equations, +colors, texture coordinates, or any other situation where you need to manipulate +a small sequence of numbers. As such, `vec` is supported by a set of +algebraic and component-wise functions, as well as a set of standard reductions. + +`vec`: +- is + [`DefaultConstructible`](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + ```cpp + float3 v; // v contains 0,0,0 + ``` +- is constructible from `M` elements of type `T`: + ```cpp + float3 v {1,2,3}; // v contains 1,2,3 + ``` +- is + [`CopyConstructible`](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and + [`CopyAssignable`](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + ```cpp + float3 v {1,2,3}; // v contains 1,2,3 + float3 u {v}; // u contains 1,2,3 + float3 w; // w contains 0,0,0 + w = u; // w contains 1,2,3 + ``` +- is + [`EqualityComparable`](https://en.cppreference.com/w/cpp/named_req/EqualityComparable) + and + [`LessThanComparable`](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + ```cpp + if(v == y) cout << "v and u contain equal elements in the same positions" << + endl; if(v < u) cout << "v precedes u lexicographically" << endl; + ``` +- is **explicitly** constructible from a single element of type `T`: + ```cpp + float3 v = float3{4}; // v contains 4,4,4 + ``` +- is **explicitly** constructible from a `vec` of some other type `U`: + ```cpp + float3 v {1.1f,2.3f,3.5f}; // v contains 1.1,2.3,3.5 + int3 u = int3{v}; // u contains 1,2,3 + ``` +- has fields `x,y,z,w`: + ```cpp + float y = point.y; // y contains second element of point + pixel.w = 0.5; // fourth element of pixel set to 0.5 + float s = tc.x; // s contains first element of tc + ``` +- supports indexing: + ```cpp + float x = v[0]; // x contains first element of v + v[2] = 5; // third element of v set to 5 + ``` +- supports unary operators `+`, `-`, `!` and `~` in component-wise fashion: + ```cpp + auto v = -float{2,3}; // v is float2{-2,-3} + ``` +- supports binary operators `+`, `-`, `*`, `/`, `%`, `|`, `&`, `^`, `<<` and + `>>` in component-wise fashion: + ```cpp + auto v = float2{1,1} + float2{2,3}; // v is float2{3,4} + ``` +- supports binary operators with a scalar on the left or the right: + ```cpp + auto v = 2 * float3{1,2,3}; // v is float3{2,4,6} + auto u = float3{1,2,3} + 1; // u is float3{2,3,4} + ``` +- supports operators `+=`, `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=` and + `>>=` with vectors or scalars on the right: + ```cpp + float2 v {1,2}; v *= 3; // v is float2{3,6} + ``` +- supports operations on mixed element types: + ```cpp + auto v = float3{1,2,3} + int3{4,5,6}; // v is float3{5,7,9} + ``` +- supports [range-based + for](https://en.cppreference.com/w/cpp/language/range-for): + ```cpp + for(auto elem : float3{1,2,3}) cout << elem << ' '; // prints "1 2 3 " + ``` +- has a flat memory layout: + ```cpp + float3 v {1,2,3}; + float * p = v.data(); // &v[i] == p+i + p[1] = 4; // v contains 1,4,3 + ``` * @{ */ template @@ -879,8 +973,96 @@ struct vec { /** @addtogroup mat * @ingroup LinAlg - * @brief Small, fixed-size matrix type, consisting of exactly M rows and N - * columns of type T, stored in column-major order. + * @brief `linalg::mat` defines a fixed-size matrix containing exactly + `M` rows and `N` columns of type `T`, in column-major order. + +This data structure is supported by a set of algebraic and component-wise +functions, as well as a set of standard reductions. + +`mat`: +- is + [`DefaultConstructible`](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + ```cpp + float2x2 m; // m contains columns 0,0; 0,0 + ``` +- is constructible from `N` columns of type `vec`: + ```cpp + float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4 + ``` +- is constructible from `linalg::identity`: + ```cpp + float3x3 m = linalg::identity; // m contains columns 1,0,0; 0,1,0; 0,0,1 + ``` +- is + [`CopyConstructible`](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and + [`CopyAssignable`](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + ```cpp + float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4 + float2x2 n {m}; // n contains columns 1,2; 3,4 + float2x2 p; // p contains columns 0,0; 0,0 + p = n; // p contains columns 1,2; 3,4 + ``` +- is + [`EqualityComparable`](https://en.cppreference.com/w/cpp/named_req/EqualityComparable) + and + [`LessThanComparable`](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + ```cpp + if(m == n) cout << "m and n contain equal elements in the same positions" << + endl; if(m < n) cout << "m precedes n lexicographically when compared in + column-major order" << endl; + ``` +- is **explicitly** constructible from a single element of type `T`: + ```cpp + float2x2 m {5}; // m contains columns 5,5; 5,5 + ``` +- is **explicitly** constructible from a `mat` of some other type `U`: + ```cpp + float2x2 m {int2x2{{5,6},{7,8}}}; // m contains columns 5,6; 7,8 + ``` +- supports indexing into *columns*: + ```cpp + float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6 + float2 c = m[0]; // c contains 1,2 + m[1] = {7,8}; // m contains columns 1,2; 7,8; 5,6 + ``` +- supports retrieval (but not assignment) of rows: + ```cpp + float2x3 m {{1,2},{3,4},{5,6}}; // m contains columns 1,2; 3,4; 5,6 + float3 r = m.row(1); // r contains 2,4,6 + ``` + +- supports unary operators `+`, `-`, `!` and `~` in component-wise fashion: + ```cpp + float2x2 m {{1,2},{3,4}}; // m contains columns 1,2; 3,4 + float2x2 n = -m; // n contains columns -1,-2; -3,-4 + ``` +- supports binary operators `+`, `-`, `*`, `/`, `%`, `|`, `&`, `^`, `<<` and + `>>` in component-wise fashion: + ```cpp + float2x2 a {{0,0},{2,2}}; // a contains columns 0,0; 2,2 + float2x2 b {{1,2},{1,2}}; // b contains columns 1,2; 1,2 + float2x2 c = a + b; // c contains columns 1,2; 3,4 + ``` + +- supports binary operators with a scalar on the left or the right: + ```cpp + auto m = 2 * float2x2{{1,2},{3,4}}; // m is float2x2{{2,4},{6,8}} + ``` + +- supports operators `+=`, `-=`, `*=`, `/=`, `%=`, `|=`, `&=`, `^=`, `<<=` and + `>>=` with matrices or scalars on the right: + ```cpp + float2x2 v {{5,4},{3,2}}; + v *= 3; // v is float2x2{{15,12},{9,6}} + ``` + +- supports operations on mixed element types: + +- supports [range-based + for](https://en.cppreference.com/w/cpp/language/range-for) over columns + +- has a flat memory layout * @{ */ template @@ -1122,7 +1304,7 @@ constexpr apply_t zip(const A &a, const B &b, F func) { } /** @} */ -/** @addtogroup comparisons +/** @addtogroup comparison_ops * @ingroup LinAlg * @brief Relational operators are defined to compare the elements of two * vectors or matrices lexicographically, in column-major order. @@ -1236,7 +1418,9 @@ constexpr apply_t operator!(const A &a) { /** @addtogroup binary_ops * @ingroup LinAlg * @brief Binary operators are defined component-wise for linalg types, EXCEPT - * for `operator *`. + * for `operator *`, which does standard matrix multiplication, scalar + * multiplication, and component-wise multiplication for same-size vectors. Use + * `cmul` for the matrix Hadamard product. * @{ */ template @@ -1336,14 +1520,26 @@ constexpr auto operator>>=(A &a, const B &b) -> decltype(a = a >> b) { * @brief Swizzles and subobjects. * @{ */ +/** + * @brief Returns a vector containing the specified ordered indices, e.g. + * linalg::swizzle<1, 2, 0>(vec4(4, 5, 6, 7)) == vec3(5, 6, 4) + */ template constexpr vec swizzle(const vec &a) { return {detail::getter{}(a)...}; } +/** + * @brief Returns a vector containing the specified index range, e.g. + * linalg::subvec<1, 4>(vec4(4, 5, 6, 7)) == vec3(5, 6, 7) + */ template constexpr vec subvec(const vec &a) { return detail::swizzle(a, detail::make_seq{}); } +/** + * @brief Returns a matrix containing the specified row and column range: + * linalg::submat + */ template constexpr mat submat(const mat &a) { return detail::swizzle(a, detail::make_seq{}, @@ -1351,7 +1547,7 @@ constexpr mat submat(const mat &a) { } /** @} */ -/** @addtogroup STL +/** @addtogroup unary_STL * @ingroup LinAlg * @brief Component-wise standard library math functions. * @{ @@ -1432,7 +1628,14 @@ template constexpr apply_t round(const A &a) { return apply(detail::std_round{}, a); } +/** @} */ +/** @addtogroup binary_STL + * @ingroup LinAlg + * @brief Component-wise standard library math functions. Either argument can be + * a vector or a scalar. + * @{ + */ template constexpr apply_t fmod(const A &a, const B &b) { return apply(detail::std_fmod{}, a, b); @@ -1451,9 +1654,10 @@ constexpr apply_t copysign(const A &a, const B &b) { } /** @} */ -/** @addtogroup comparison +/** @addtogroup relational * @ingroup LinAlg - * @brief Component-wise relational functions on vectors. + * @brief Component-wise relational functions on vectors. Either argument can be + * a vector or a scalar. * @{ */ template @@ -1484,7 +1688,8 @@ constexpr apply_t gequal(const A &a, const B &b) { /** @addtogroup selection * @ingroup LinAlg - * @brief Component-wise selection functions on vectors. + * @brief Component-wise selection functions on vectors. Either argument can be + * a vector or a scalar. * @{ */ template @@ -1495,16 +1700,27 @@ template constexpr apply_t max(const A &a, const B &b) { return apply(detail::max{}, a, b); } +/** + * @brief Clamps the components of x between l and h, provided l[i] < h[i]. + */ template constexpr apply_t clamp(const X &x, const L &l, const H &h) { return apply(detail::clamp{}, x, l, h); } +/** + * @brief Returns the component from a if the corresponding component of p is + * true and from b otherwise. + */ template constexpr apply_t select(const P &p, const A &a, const B &b) { return apply(detail::select{}, p, a, b); } +/** + * @brief Linear interpolation from a to b as t goes from 0 -> 1. Values beyond + * [a, b] will result if t is outside [0, 1]. + */ template constexpr apply_t lerp(const A &a, const B &b, const T &t) { @@ -1517,79 +1733,146 @@ constexpr apply_t lerp(const A &a, const B &b, * @brief Support for vector algebra. * @{ */ +/** + * @brief shorthand for `cross({a.x,a.y,0}, {b.x,b.y,0}).z` + */ template constexpr T cross(const vec &a, const vec &b) { return a.x * b.y - a.y * b.x; } +/** + * @brief shorthand for `cross({0,0,a.z}, {b.x,b.y,0}).xy()` + */ template constexpr vec cross(T a, const vec &b) { return {-a * b.y, a * b.x}; } +/** + * @brief shorthand for `cross({a.x,a.y,0}, {0,0,b.z}).xy()` + */ template constexpr vec cross(const vec &a, T b) { return {a.y * b, -a.x * b}; } +/** + * @brief the [cross or vector + * product](https://en.wikipedia.org/wiki/Cross_product) of vectors `a` and `b` + */ template constexpr vec cross(const vec &a, const vec &b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; } +/** + * @brief [dot or inner product](https://en.wikipedia.org/wiki/Dot_product) of + * vectors `a` and `b` + */ template constexpr T dot(const vec &a, const vec &b) { return sum(a * b); } +/** + * @brief *square* of the length or magnitude of vector `a` + */ template constexpr T length2(const vec &a) { return dot(a, a); } +/** + * @brief length or magnitude of a vector `a` + */ template T length(const vec &a) { return std::sqrt(length2(a)); } +/** + * @brief unit length vector in the same direction as `a` (undefined for + zero-length vectors) + + */ template vec normalize(const vec &a) { return a / length(a); } +/** + * @brief *square* of the [Euclidean + * distance](https://en.wikipedia.org/wiki/Euclidean_distance) between points + * `a` and `b` + */ template constexpr T distance2(const vec &a, const vec &b) { return length2(b - a); } +/** + * @brief [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance) + * between points `a` and `b` + */ template T distance(const vec &a, const vec &b) { return length(b - a); } +/** + * @brief Return the angle in radians between two unit vectors. + */ template T uangle(const vec &a, const vec &b) { T d = dot(a, b); return d > 1 ? 0 : std::acos(d < -1 ? -1 : d); } +/** + * @brief Return the angle in radians between two non-unit vectors. + */ template T angle(const vec &a, const vec &b) { return uangle(normalize(a), normalize(b)); } +/** + * @brief vector `v` rotated counter-clockwise by the angle `a` in + * [radians](https://en.wikipedia.org/wiki/Radian) + */ template vec rot(T a, const vec &v) { const T s = std::sin(a), c = std::cos(a); return {v.x * c - v.y * s, v.x * s + v.y * c}; } +/** + * @brief vector `v` rotated counter-clockwise by the angle `a` in + * [radians](https://en.wikipedia.org/wiki/Radian) around the X axis + */ template vec rotx(T a, const vec &v) { const T s = std::sin(a), c = std::cos(a); return {v.x, v.y * c - v.z * s, v.y * s + v.z * c}; } +/** + * @brief vector `v` rotated counter-clockwise by the angle `a` in + * [radians](https://en.wikipedia.org/wiki/Radian) around the Y axis + */ template vec roty(T a, const vec &v) { const T s = std::sin(a), c = std::cos(a); return {v.x * c + v.z * s, v.y, -v.x * s + v.z * c}; } +/** + * @brief vector `v` rotated counter-clockwise by the angle `a` in + * [radians](https://en.wikipedia.org/wiki/Radian) around the Z axis + */ template vec rotz(T a, const vec &v) { const T s = std::sin(a), c = std::cos(a); return {v.x * c - v.y * s, v.x * s + v.y * c, v.z}; } +/** + * @brief shorthand for `normalize(lerp(a,b,t))` + */ template vec nlerp(const vec &a, const vec &b, T t) { return normalize(lerp(a, b, t)); } +/** + * @brief [spherical linear interpolation](https://en.wikipedia.org/wiki/Slerp) + * between unit vectors `a` and `b` (undefined for non-unit vectors) by + * parameter `t` + */ template vec slerp(const vec &a, const vec &b, T t) { T th = uangle(a, b); @@ -1601,18 +1884,33 @@ vec slerp(const vec &a, const vec &b, T t) { /** @addtogroup quaternions * @ingroup LinAlg - * @brief Support for quaternion algebra using 4D vectors, representing xi + yj - * + zk + w. + * @brief Support for quaternion algebra using 4D vectors of arbitrary length, + * representing xi + yj + zk + w. * @{ */ +/** + * @brief + * [conjugate](https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal) + * of quaternion `q` + */ template constexpr vec qconj(const vec &q) { return {-q.x, -q.y, -q.z, q.w}; } +/** + * @brief [inverse or + * reciprocal](https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal) + * of quaternion `q` (undefined for zero-length quaternions) + */ template vec qinv(const vec &q) { return qconj(q) / length2(q); } +/** + * @brief + * [exponential](https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions) + * of quaternion `q` + */ template vec qexp(const vec &q) { const auto v = q.xyz(); @@ -1620,12 +1918,20 @@ vec qexp(const vec &q) { return std::exp(q.w) * vec{v * (vv > 0 ? std::sin(vv) / vv : 0), std::cos(vv)}; } +/** + * @brief + * [logarithm](https://en.wikipedia.org/wiki/Quaternion#Exponential,_logarithm,_and_power_functions) + * of quaternion `q` + */ template vec qlog(const vec &q) { const auto v = q.xyz(); const auto vv = length(v), qq = length(q); return {v * (vv > 0 ? std::acos(q.w / qq) / vv : 0), std::log(qq)}; } +/** + * @brief quaternion `q` raised to the exponent `p` + */ template vec qpow(const vec &q, const T &p) { const auto v = q.xyz(); @@ -1633,6 +1939,11 @@ vec qpow(const vec &q, const T &p) { return std::pow(qq, p) * vec{v * (vv > 0 ? std::sin(p * th) / vv : 0), std::cos(p * th)}; } +/** + * @brief [Hamilton + * product](https://en.wikipedia.org/wiki/Quaternion#Hamilton_product) of + * quaternions `a` and `b` + */ template constexpr vec qmul(const vec &a, const vec &b) { return {a.x * b.w + a.w * b.x + a.y * b.z - a.z * b.y, @@ -1640,6 +1951,9 @@ constexpr vec qmul(const vec &a, const vec &b) { a.z * b.w + a.w * b.z + a.x * b.y - a.y * b.x, a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z}; } +/** + * @brief Multiply as many input quaternions together as desired. + */ template constexpr vec qmul(const vec &a, R... r) { return qmul(a, qmul(r...)); @@ -1648,50 +1962,99 @@ constexpr vec qmul(const vec &a, R... r) { /** @addtogroup quaternion_rotation * @ingroup LinAlg - * @brief Support for 3D spatial rotations using normalized quaternions, via - * qmul(qmul(q, v), qconj(q)). + * @brief Support for 3D spatial rotations using normalized quaternions. * @{ */ +/** + * @brief efficient shorthand for `qrot(q, {1,0,0})` + */ template constexpr vec qxdir(const vec &q) { return {q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z, (q.x * q.y + q.z * q.w) * 2, (q.z * q.x - q.y * q.w) * 2}; } +/** + * @brief efficient shorthand for `qrot(q, {0,1,0})` + */ template constexpr vec qydir(const vec &q) { return {(q.x * q.y - q.z * q.w) * 2, q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z, (q.y * q.z + q.x * q.w) * 2}; } +/** + * @brief efficient shorthand for `qrot(q, {0,0,1})` + */ template constexpr vec qzdir(const vec &q) { return {(q.z * q.x + q.y * q.w) * 2, (q.y * q.z - q.x * q.w) * 2, q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z}; } +/** + * @brief Create an equivalent mat3 rotation matrix from the input quaternion. + */ template constexpr mat qmat(const vec &q) { return {qxdir(q), qydir(q), qzdir(q)}; } +/** + * @brief Rotate a vector by a quaternion. + */ template constexpr vec qrot(const vec &q, const vec &v) { return qxdir(q) * v.x + qydir(q) * v.y + qzdir(q) * v.z; } +/** + * @brief Return the angle in radians of the axis-angle representation of the + * input normalized quaternion. + */ template T qangle(const vec &q) { return std::atan2(length(q.xyz()), q.w) * 2; } +/** + * @brief Return the normalized axis of the axis-angle representation of the + * input normalized quaternion. + */ template vec qaxis(const vec &q) { return normalize(q.xyz()); } +/** + * @brief Linear interpolation that takes the shortest path - this is not + * geometrically sensible, consider qslerp instead. + */ template vec qnlerp(const vec &a, const vec &b, T t) { return nlerp(a, dot(a, b) < 0 ? -b : b, t); } +/** + * @brief Spherical linear interpolation that takes the shortest path. + */ template vec qslerp(const vec &a, const vec &b, T t) { return slerp(a, dot(a, b) < 0 ? -b : b, t); } +/** + * @brief Returns a normalized quaternion representing a rotation by angle in + * radians about the provided axis. + */ +template +vec constexpr rotation_quat(const vec &axis, T angle) { + return {axis * std::sin(angle / 2), std::cos(angle / 2)}; +} +/** + * @brief Returns a normalized quaternion representing the shortest rotation + * from orig vector to dest vector. + */ +template +vec rotation_quat(const vec &orig, const vec &dest); +/** + * @brief Returns a normalized quaternion representing the input rotation + * matrix, which should be orthonormal. + */ +template +vec rotation_quat(const mat &m); /** @} */ /** @addtogroup mat_algebra @@ -1919,30 +2282,6 @@ enum z_range { zero_to_one }; // Should projection matrices map z into the range of [-1,1] or [0,1]? template -vec constexpr rotation_quat(const vec &axis, T angle) { - return {axis * std::sin(angle / 2), std::cos(angle / 2)}; -} -template -vec constexpr rotation_quat(const vec &orig, - const vec &dest) { - T cosTheta = dot(orig, dest); - if (cosTheta >= 1 - std::numeric_limits::epsilon()) { - return {0, 0, 0, 1}; - } - if (cosTheta < -1 + std::numeric_limits::epsilon()) { - vec axis = cross(vec(0, 0, 1), orig); - if (length2(axis) < std::numeric_limits::epsilon()) - axis = cross(vec(1, 0, 0), orig); - return rotation_quat(normalize(axis), - 3.14159265358979323846264338327950288); - } - vec axis = cross(orig, dest); - T s = std::sqrt((1 + cosTheta) * 2); - return {axis * (1 / s), s * 0.5}; -} -template -vec rotation_quat(const mat &m); -template mat translation_matrix(const vec &translation) { return {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {translation, 1}}; } @@ -1978,7 +2317,7 @@ mat perspective_matrix(T fovy, T aspect, T n, T f, fwd_axis a = neg_z, } /** @} */ -/** @addtogroup std::array +/** @addtogroup array * @ingroup LinAlg * @brief Provide implicit conversion between linalg::vec and * std::array. @@ -2026,10 +2365,50 @@ struct converter, vec> { } }; /** @} */ + +#ifdef MANIFOLD_DEBUG +template +std::ostream &operator<<(std::ostream &out, const vec &v) { + return out << '{' << v[0] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const vec &v) { + return out << '{' << v[0] << ',' << v[1] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const vec &v) { + return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const vec &v) { + return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; +} + +template +std::ostream &operator<<(std::ostream &out, const mat &m) { + return out << '{' << m[0] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const mat &m) { + return out << '{' << m[0] << ',' << m[1] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const mat &m) { + return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; +} +template +std::ostream &operator<<(std::ostream &out, const mat &m) { + return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; +} +#endif } // namespace linalg namespace std { -// Provide specializations for std::hash<...> with linalg types +/** @addtogroup hash + * @ingroup LinAlg + * @brief Provide specializations for std::hash<...> with linalg types. + * @{ + */ template struct hash> { std::size_t operator()(const linalg::vec &v) const { @@ -2087,6 +2466,7 @@ struct hash> { return h(v.x) ^ (h(v.y) << M) ^ (h(v.z) << (M * 2)) ^ (h(v.w) << (M * 3)); } }; +/** @} */ } // namespace std // Definitions of functions too long to be defined inline @@ -2168,6 +2548,25 @@ constexpr T linalg::determinant(const mat &a) { a.w.x * a.y.y * a.z.z - a.z.x * a.w.y * a.y.z); } +template +linalg::vec linalg::rotation_quat(const vec &orig, + const vec &dest) { + T cosTheta = dot(orig, dest); + if (cosTheta >= 1 - std::numeric_limits::epsilon()) { + return {0, 0, 0, 1}; + } + if (cosTheta < -1 + std::numeric_limits::epsilon()) { + vec axis = cross(vec(0, 0, 1), orig); + if (length2(axis) < std::numeric_limits::epsilon()) + axis = cross(vec(1, 0, 0), orig); + return rotation_quat(normalize(axis), + 3.14159265358979323846264338327950288); + } + vec axis = cross(orig, dest); + T s = std::sqrt((1 + cosTheta) * 2); + return {axis * (1 / s), s * 0.5}; +} + template linalg::vec linalg::rotation_quat(const mat &m) { const vec q{m.x.x - m.y.y - m.z.z, m.y.y - m.x.x - m.z.z, diff --git a/godot/thirdparty/manifold/include/manifold/manifold.h b/godot/thirdparty/manifold/include/manifold/manifold.h index e6754dbc..a4f65e9c 100644 --- a/godot/thirdparty/manifold/include/manifold/manifold.h +++ b/godot/thirdparty/manifold/include/manifold/manifold.h @@ -166,7 +166,7 @@ using MeshGL = MeshGLP; /** * @brief Double-precision, 64-bit indices - best for huge meshes. */ -using MeshGL64 = MeshGLP; +using MeshGL64 = MeshGLP; /** * @brief This library's internal representation of an oriented, 2-manifold, diff --git a/godot/thirdparty/manifold/include/manifold/meshIO.h b/godot/thirdparty/manifold/include/manifold/meshIO.h deleted file mode 100644 index 3898b4a1..00000000 --- a/godot/thirdparty/manifold/include/manifold/meshIO.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2021 The Manifold Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once -#include - -#include "manifold/manifold.h" - -namespace manifold { - -/** @addtogroup MeshIO - * @ingroup Optional - * @brief 3D model file I/O based on Assimp - * @{ - */ - -/** - * PBR material properties for GLB/glTF files. - */ -struct Material { - /// Roughness value between 0 (shiny) and 1 (matte). - double roughness = 0.2; - /// Metalness value, generally either 0 (dielectric) or 1 (metal). - double metalness = 1; - /// Color (RGB) multiplier to apply to the whole mesh (each value between 0 - /// and 1). - vec3 color = vec3(1.0); - /// Alpha multiplier to apply to the whole mesh (each value between 0 - /// and 1). - int alpha = 1.0; - /// Gives the property index where the first normal channel - /// can be found. 0 indicates the first three property channels following - /// position. A negative value does not save normals. - int normalIdx = -1; - /// Gives the property index where the first color channel - /// can be found. 0 indicates the first three property channels following - /// position. A negative value does not save vertex colors. - int colorIdx = -1; - /// Gives the property index where the alpha channel - /// can be found. 0 indicates the first property channel following - /// position. A negative value does not save vertex alpha. - int alphaIdx = -1; -}; - -/** - * These options only currently affect .glb and .gltf files. - */ -struct ExportOptions { - /// When false, vertex normals are exported, causing the mesh to appear smooth - /// through normal interpolation. - bool faceted = true; - /// PBR material properties. - Material mat = {}; -}; - -MeshGL ImportMesh(const std::string& filename, bool forceCleanup = false); - -void ExportMesh(const std::string& filename, const MeshGL& mesh, - const ExportOptions& options); -/** @} */ -} // namespace manifold diff --git a/godot/thirdparty/manifold/include/manifold/optional_assert.h b/godot/thirdparty/manifold/include/manifold/optional_assert.h index 6997b17d..905746cc 100644 --- a/godot/thirdparty/manifold/include/manifold/optional_assert.h +++ b/godot/thirdparty/manifold/include/manifold/optional_assert.h @@ -14,34 +14,53 @@ #pragma once -#ifdef MANIFOLD_EXCEPTIONS -#include -#endif - #ifdef MANIFOLD_DEBUG #include #include +#include #include +/** @addtogroup Debug + * @{ + */ +struct userErr : public virtual std::runtime_error { + using std::runtime_error::runtime_error; +}; +struct topologyErr : public virtual std::runtime_error { + using std::runtime_error::runtime_error; +}; +struct geometryErr : public virtual std::runtime_error { + using std::runtime_error::runtime_error; +}; +using logicErr = std::logic_error; + template -void Assert(bool condition, const char* file, int line, const std::string& cond, - const std::string& msg) { - if (!condition) { - std::ostringstream output; - output << "Error in file: " << file << " (" << line << "): \'" << cond - << "\' is false: " << msg; - throw Ex(output.str()); - } +void AssertFail(const char* file, int line, const char* cond, const char* msg) { + std::ostringstream output; + output << "Error in file: " << file << " (" << line << "): \'" << cond + << "\' is false: " << msg; + throw Ex(output.str()); } -#define DEBUG_ASSERT(condition, EX, msg) \ - Assert(condition, __FILE__, __LINE__, #condition, msg); -#else -#define DEBUG_ASSERT(condition, EX, msg) -#endif -#ifdef MANIFOLD_EXCEPTIONS +template +void AssertFail(const char* file, int line, const std::string& cond, + const std::string& msg) { + std::ostringstream output; + output << "Error in file: " << file << " (" << line << "): \'" << cond + << "\' is false: " << msg; + throw Ex(output.str()); +} + +// DEBUG_ASSERT is slightly slower due to the function call, but gives more +// detailed info. +#define DEBUG_ASSERT(condition, EX, msg) \ + if (!(condition)) AssertFail(__FILE__, __LINE__, #condition, msg); +// ASSERT has almost no overhead, so better to use for frequent calls like +// vector bounds checking. #define ASSERT(condition, EX) \ if (!(condition)) throw(EX); #else +#define DEBUG_ASSERT(condition, EX, msg) #define ASSERT(condition, EX) #endif +/** @} */ diff --git a/godot/thirdparty/manifold/src/boolean3.cpp b/godot/thirdparty/manifold/src/boolean3.cpp index 4b37eee8..1fca99db 100644 --- a/godot/thirdparty/manifold/src/boolean3.cpp +++ b/godot/thirdparty/manifold/src/boolean3.cpp @@ -16,7 +16,7 @@ #include -#include "manifold/parallel.h" +#include "./parallel.h" using namespace manifold; @@ -573,6 +573,11 @@ Boolean3::Boolean3(const Manifold::Impl &inP, const Manifold::Impl &inQ, Intersect12(inQ, inP, s20, p2q0, s11, p1q1, z20, xyzz11, p2q1_, false); PRINT("x21 size = " << x21_.size()); + s11.clear(); + xyzz11.clear(); + z02.clear(); + z20.clear(); + Vec p0 = p0q2.Copy(false); p0q2.Resize(0); Vec q0 = p2q0.Copy(true); diff --git a/godot/thirdparty/manifold/src/boolean_result.cpp b/godot/thirdparty/manifold/src/boolean_result.cpp index 86cb546a..44faf73e 100644 --- a/godot/thirdparty/manifold/src/boolean_result.cpp +++ b/godot/thirdparty/manifold/src/boolean_result.cpp @@ -17,8 +17,8 @@ #include #include "./boolean3.h" +#include "./parallel.h" #include "./utils.h" -#include "manifold/parallel.h" #if (MANIFOLD_PAR == 1) && __has_include() #define TBB_PREVIEW_CONCURRENT_ORDERED_CONTAINERS 1 @@ -253,7 +253,7 @@ void AddNewEdgeVerts( #if (MANIFOLD_PAR == 1) && __has_include() // parallelize operations, requires concurrent_map so we can only enable this // with tbb - if (!ManifoldParams().deterministic && p1q2.size() > kParallelThreshold) { + if (p1q2.size() > kParallelThreshold) { // ideally we should have 1 mutex per key, but kParallelThreshold is enough // to avoid contention for most of the cases std::array mutexes; @@ -486,9 +486,7 @@ void AppendWholeEdges(Manifold::Impl &outR, Vec &facePtrR, bool forward) { ZoneScoped; for_each_n( - ManifoldParams().deterministic ? ExecutionPolicy::Seq - : autoPolicy(inP.halfedge_.size()), - countAt(0), inP.halfedge_.size(), + autoPolicy(inP.halfedge_.size()), countAt(0), inP.halfedge_.size(), DuplicateHalfedges({outR.halfedge_, halfedgeRef, facePtrR, wholeHalfedgeP, inP.halfedge_, i03, vP2R, faceP2R, forward})); } @@ -780,12 +778,18 @@ Manifold::Impl Boolean3::Result(OpType op) const { AddNewEdgeVerts(edgesP, edgesNew, p1q2_, i12, v12R, inP_.halfedge_, true); AddNewEdgeVerts(edgesQ, edgesNew, p2q1_, i21, v21R, inQ_.halfedge_, false); + v12R.clear(); + v21R.clear(); + // Level 4 Vec faceEdge; Vec facePQ2R; std::tie(faceEdge, facePQ2R) = SizeOutput(outR, inP_, inQ_, i03, i30, i12, i21, p1q2_, p2q1_, invertQ); + i12.clear(); + i21.clear(); + // This gets incremented for each halfedge that's added to a face so that the // next one knows where to slot in. Vec facePtrR = faceEdge; @@ -801,14 +805,28 @@ Manifold::Impl Boolean3::Result(OpType op) const { AppendPartialEdges(outR, wholeHalfedgeQ, facePtrR, edgesQ, halfedgeRef, inQ_, i30, vQ2R, facePQ2R.begin() + inP_.NumTri(), false); + edgesP.clear(); + edgesQ.clear(); + AppendNewEdges(outR, facePtrR, edgesNew, halfedgeRef, facePQ2R, inP_.NumTri()); + edgesNew.clear(); + AppendWholeEdges(outR, facePtrR, halfedgeRef, inP_, wholeHalfedgeP, i03, vP2R, facePQ2R.cview(0, inP_.NumTri()), true); AppendWholeEdges(outR, facePtrR, halfedgeRef, inQ_, wholeHalfedgeQ, i30, vQ2R, facePQ2R.cview(inP_.NumTri(), inQ_.NumTri()), false); + wholeHalfedgeP.clear(); + wholeHalfedgeQ.clear(); + facePtrR.clear(); + facePQ2R.clear(); + i03.clear(); + i30.clear(); + vP2R.clear(); + vQ2R.clear(); + #ifdef MANIFOLD_DEBUG assemble.Stop(); Timer triangulate; @@ -821,6 +839,8 @@ Manifold::Impl Boolean3::Result(OpType op) const { DEBUG_ASSERT(outR.IsManifold(), logicErr, "polygon mesh is not manifold!"); outR.Face2Tri(faceEdge, halfedgeRef); + halfedgeRef.clear(); + faceEdge.clear(); #ifdef MANIFOLD_DEBUG triangulate.Stop(); diff --git a/godot/thirdparty/manifold/src/collider.h b/godot/thirdparty/manifold/src/collider.h index 6c303fb5..80de94b7 100644 --- a/godot/thirdparty/manifold/src/collider.h +++ b/godot/thirdparty/manifold/src/collider.h @@ -13,11 +13,11 @@ // limitations under the License. #pragma once +#include "./parallel.h" #include "./sparse.h" #include "./utils.h" #include "./vec.h" #include "manifold/common.h" -#include "manifold/parallel.h" #ifdef _MSC_VER #include diff --git a/godot/thirdparty/manifold/src/constructors.cpp b/godot/thirdparty/manifold/src/constructors.cpp index 92e0af97..17df744e 100644 --- a/godot/thirdparty/manifold/src/constructors.cpp +++ b/godot/thirdparty/manifold/src/constructors.cpp @@ -14,7 +14,7 @@ #include "./csg_tree.h" #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" #include "manifold/polygon.h" namespace manifold { diff --git a/godot/thirdparty/manifold/src/csg_tree.cpp b/godot/thirdparty/manifold/src/csg_tree.cpp index 2cbc1b80..8f98fe29 100644 --- a/godot/thirdparty/manifold/src/csg_tree.cpp +++ b/godot/thirdparty/manifold/src/csg_tree.cpp @@ -25,7 +25,7 @@ #include "./csg_tree.h" #include "./impl.h" #include "./mesh_fixes.h" -#include "manifold/parallel.h" +#include "./parallel.h" constexpr int kParallelThreshold = 4096; @@ -468,35 +468,32 @@ std::shared_ptr CsgOpNode::BatchBoolean( return std::make_shared(boolean.Result(operation)); } #if (MANIFOLD_PAR == 1) && __has_include() - if (!ManifoldParams().deterministic) { - tbb::task_group group; - tbb::concurrent_priority_queue queue( - results.size()); - for (auto result : results) { - queue.emplace(result); - } - results.clear(); - std::function process = [&]() { - while (queue.size() > 1) { - SharedImpl a, b; - if (!queue.try_pop(a)) continue; - if (!queue.try_pop(b)) { - queue.push(a); - continue; - } - group.run([&, a, b]() { - Boolean3 boolean(*getImplPtr(a), *getImplPtr(b), operation); - queue.emplace( - std::make_shared(boolean.Result(operation))); - return group.run(process); - }); - } - }; - group.run_and_wait(process); - SharedImpl r; - queue.try_pop(r); - return *std::get_if>(&r); + tbb::task_group group; + tbb::concurrent_priority_queue queue(results.size()); + for (auto result : results) { + queue.emplace(result); } + results.clear(); + std::function process = [&]() { + while (queue.size() > 1) { + SharedImpl a, b; + if (!queue.try_pop(a)) continue; + if (!queue.try_pop(b)) { + queue.push(a); + continue; + } + group.run([&, a, b]() { + Boolean3 boolean(*getImplPtr(a), *getImplPtr(b), operation); + queue.emplace( + std::make_shared(boolean.Result(operation))); + return group.run(process); + }); + } + }; + group.run_and_wait(process); + SharedImpl r; + queue.try_pop(r); + return *std::get_if>(&r); #endif // apply boolean operations starting from smaller meshes // the assumption is that boolean operations on smaller meshes is faster, @@ -604,10 +601,8 @@ std::vector> &CsgOpNode::GetChildren( if (forceToLeafNodes && !impl->forcedToLeafNodes_) { impl->forcedToLeafNodes_ = true; - for_each(impl->children_.size() > 1 && !ManifoldParams().deterministic - ? ExecutionPolicy::Par - : ExecutionPolicy::Seq, - impl->children_.begin(), impl->children_.end(), [](auto &child) { + for_each(ExecutionPolicy::Par, impl->children_.begin(), + impl->children_.end(), [](auto &child) { if (child->GetNodeType() != CsgNodeType::Leaf) { child = child->ToLeafNode(); } diff --git a/godot/thirdparty/manifold/src/edge_op.cpp b/godot/thirdparty/manifold/src/edge_op.cpp index 20f2fd0f..d22577a2 100644 --- a/godot/thirdparty/manifold/src/edge_op.cpp +++ b/godot/thirdparty/manifold/src/edge_op.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" namespace { using namespace manifold; diff --git a/godot/thirdparty/manifold/src/face_op.cpp b/godot/thirdparty/manifold/src/face_op.cpp index 15cab42a..b1a6b16c 100644 --- a/godot/thirdparty/manifold/src/face_op.cpp +++ b/godot/thirdparty/manifold/src/face_op.cpp @@ -20,7 +20,7 @@ #include #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" #include "manifold/polygon.h" namespace manifold { diff --git a/godot/thirdparty/manifold/src/impl.cpp b/godot/thirdparty/manifold/src/impl.cpp index 1718bcb1..9582b2c1 100644 --- a/godot/thirdparty/manifold/src/impl.cpp +++ b/godot/thirdparty/manifold/src/impl.cpp @@ -20,8 +20,8 @@ #include "./hashtable.h" #include "./mesh_fixes.h" +#include "./parallel.h" #include "./svd.h" -#include "manifold/parallel.h" namespace { using namespace manifold; diff --git a/godot/thirdparty/manifold/src/manifold.cpp b/godot/thirdparty/manifold/src/manifold.cpp index 3e280a1d..a36dd460 100644 --- a/godot/thirdparty/manifold/src/manifold.cpp +++ b/godot/thirdparty/manifold/src/manifold.cpp @@ -19,7 +19,7 @@ #include "./boolean3.h" #include "./csg_tree.h" #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" namespace { using namespace manifold; @@ -323,7 +323,7 @@ MeshGL Manifold::GetMeshGL(int normalIdx) const { */ MeshGL64 Manifold::GetMeshGL64(int normalIdx) const { const Impl& impl = *GetCsgLeafNode().GetImpl(); - return GetMeshGLImpl(impl, normalIdx); + return GetMeshGLImpl(impl, normalIdx); } /** diff --git a/godot/thirdparty/manifold/include/manifold/parallel.h b/godot/thirdparty/manifold/src/parallel.h similarity index 100% rename from godot/thirdparty/manifold/include/manifold/parallel.h rename to godot/thirdparty/manifold/src/parallel.h diff --git a/godot/thirdparty/manifold/src/polygon.cpp b/godot/thirdparty/manifold/src/polygon.cpp index 38d209ae..60f510a9 100644 --- a/godot/thirdparty/manifold/src/polygon.cpp +++ b/godot/thirdparty/manifold/src/polygon.cpp @@ -19,9 +19,9 @@ #include #include "./collider.h" +#include "./parallel.h" #include "./utils.h" #include "manifold/optional_assert.h" -#include "manifold/parallel.h" namespace { using namespace manifold; @@ -950,7 +950,7 @@ namespace manifold { std::vector TriangulateIdx(const PolygonsIdx &polys, double epsilon) { std::vector triangles; double updatedEpsilon = epsilon; -#ifdef MANIFOLD_EXCEPTIONS +#ifdef MANIFOLD_DEBUG try { #endif if (IsConvex(polys, epsilon)) { // fast path @@ -960,7 +960,6 @@ std::vector TriangulateIdx(const PolygonsIdx &polys, double epsilon) { triangles = triangulator.Triangulate(); updatedEpsilon = triangulator.GetPrecision(); } -#ifdef MANIFOLD_EXCEPTIONS #ifdef MANIFOLD_DEBUG if (params.intermediateChecks) { CheckTopology(triangles, polys); @@ -976,9 +975,6 @@ std::vector TriangulateIdx(const PolygonsIdx &polys, double epsilon) { } catch (const std::exception &e) { PrintFailure(e, polys, triangles, updatedEpsilon); throw; -#else - } catch (const std::exception &e) { -#endif } #endif return triangles; diff --git a/godot/thirdparty/manifold/src/properties.cpp b/godot/thirdparty/manifold/src/properties.cpp index 73fb84bc..32f79e5b 100644 --- a/godot/thirdparty/manifold/src/properties.cpp +++ b/godot/thirdparty/manifold/src/properties.cpp @@ -15,8 +15,8 @@ #include #include "./impl.h" +#include "./parallel.h" #include "./tri_dist.h" -#include "manifold/parallel.h" namespace { using namespace manifold; @@ -89,7 +89,7 @@ struct UpdateProperties { auto old = std::atomic_exchange( reinterpret_cast*>(&counters[propVert]), static_cast(1)); - if (old == 1) return; + if (old == 1) continue; for (int p = 0; p < oldNumProp; ++p) { properties[numProp * propVert + p] = @@ -192,7 +192,7 @@ bool Manifold::Impl::Is2Manifold() const { stable_sort(halfedge.begin(), halfedge.end()); return all_of( - countAt(0_uz), countAt(2 * NumEdge() - 1), [halfedge](size_t edge) { + countAt(0_uz), countAt(2 * NumEdge() - 1), [&halfedge](size_t edge) { const Halfedge h = halfedge[edge]; if (h.startVert == -1 && h.endVert == -1 && h.pairedHalfedge == -1) return true; diff --git a/godot/thirdparty/manifold/src/quickhull.cpp b/godot/thirdparty/manifold/src/quickhull.cpp index 6e20d8c9..be2b89e1 100644 --- a/godot/thirdparty/manifold/src/quickhull.cpp +++ b/godot/thirdparty/manifold/src/quickhull.cpp @@ -60,9 +60,9 @@ size_t MeshBuilder::addFace() { if (disabledFaces.size()) { size_t index = disabledFaces.back(); auto& f = faces[index]; - ASSERT(f.isDisabled(), logicErr("f should be disabled")); - ASSERT(!f.pointsOnPositiveSide, - logicErr("f should not be on the positive side")); + DEBUG_ASSERT(f.isDisabled(), logicErr, "f should be disabled"); + DEBUG_ASSERT(!f.pointsOnPositiveSide, logicErr, + "f should not be on the positive side"); f.mostDistantPointDist = 0; disabledFaces.pop_back(); return index; @@ -204,8 +204,8 @@ HalfEdgeMesh::HalfEdgeMesh(const MeshBuilder& builderObject, } for (auto& halfEdgeIndexFace : halfEdgeIndexFaces) { - ASSERT(halfEdgeMapping.count(halfEdgeIndexFace) == 1, - logicErr("invalid halfedge mapping")); + DEBUG_ASSERT(halfEdgeMapping.count(halfEdgeIndexFace) == 1, logicErr, + "invalid halfedge mapping"); halfEdgeIndexFace = halfEdgeMapping[halfEdgeIndexFace]; } @@ -324,7 +324,7 @@ void QuickHull::createConvexHalfedgeMesh() { // Compute base tetrahedron setupInitialTetrahedron(); - ASSERT(mesh.faces.size() == 4, logicErr("not a tetrahedron")); + DEBUG_ASSERT(mesh.faces.size() == 4, logicErr, "not a tetrahedron"); // Init face stack with those faces that have points assigned to them faceList.clear(); @@ -354,8 +354,9 @@ void QuickHull::createConvexHalfedgeMesh() { auto& tf = mesh.faces[topFaceIndex]; tf.inFaceStack = 0; - ASSERT(!tf.pointsOnPositiveSide || tf.pointsOnPositiveSide->size() > 0, - logicErr("there should be points on the positive side")); + DEBUG_ASSERT( + !tf.pointsOnPositiveSide || tf.pointsOnPositiveSide->size() > 0, + logicErr, "there should be points on the positive side"); if (!tf.pointsOnPositiveSide || tf.isDisabled()) { continue; } @@ -376,7 +377,7 @@ void QuickHull::createConvexHalfedgeMesh() { const auto faceData = possiblyVisibleFaces.back(); possiblyVisibleFaces.pop_back(); auto& pvf = mesh.faces[faceData.faceIndex]; - ASSERT(!pvf.isDisabled(), logicErr("pvf should not be disabled")); + DEBUG_ASSERT(!pvf.isDisabled(), logicErr, "pvf should not be disabled"); if (pvf.visibilityCheckedOnIteration == iter) { if (pvf.isVisibleFaceOnCurrentIteration) { @@ -400,8 +401,8 @@ void QuickHull::createConvexHalfedgeMesh() { } continue; } - ASSERT(faceData.faceIndex != topFaceIndex, - logicErr("face index invalid")); + DEBUG_ASSERT(faceData.faceIndex != topFaceIndex, logicErr, + "face index invalid"); } // The face is not visible. Therefore, the halfedge we came from is part @@ -475,7 +476,7 @@ void QuickHull::createConvexHalfedgeMesh() { auto t = mesh.disableFace(faceIndex); if (t) { // Because we should not assign point vectors to faces unless needed... - ASSERT(t->size(), logicErr("t should not be empty")); + DEBUG_ASSERT(t->size(), logicErr, "t should not be empty"); disabledFacePointVectors.push_back(std::move(t)); } } @@ -530,7 +531,8 @@ void QuickHull::createConvexHalfedgeMesh() { // Assign points that were on the positive side of the disabled faces to the // new faces. for (auto& disabledPoints : disabledFacePointVectors) { - ASSERT(disabledPoints, logicErr("disabledPoints should not be null")); + DEBUG_ASSERT(disabledPoints != nullptr, logicErr, + "disabledPoints should not be null"); for (const auto& point : *(disabledPoints)) { if (point == activePointIndex) { continue; @@ -550,8 +552,8 @@ void QuickHull::createConvexHalfedgeMesh() { for (const auto newFaceIndex : newFaceIndices) { auto& newFace = mesh.faces[newFaceIndex]; if (newFace.pointsOnPositiveSide) { - ASSERT(newFace.pointsOnPositiveSide->size() > 0, - logicErr("there should be points on the positive side")); + DEBUG_ASSERT(newFace.pointsOnPositiveSide->size() > 0, logicErr, + "there should be points on the positive side"); if (!newFace.inFaceStack) { faceList.push_back(newFaceIndex); newFace.inFaceStack = 1; @@ -620,10 +622,11 @@ bool QuickHull::reorderHorizonEdges(VecView& horizonEdges) { return false; } } - ASSERT(mesh.halfedges[horizonEdges[horizonEdges.size() - 1]].endVert == - mesh.halfedges[mesh.halfedges[horizonEdges[0]].pairedHalfedge] - .endVert, - logicErr("invalid halfedge")); + DEBUG_ASSERT( + mesh.halfedges[horizonEdges[horizonEdges.size() - 1]].endVert == + mesh.halfedges[mesh.halfedges[horizonEdges[0]].pairedHalfedge] + .endVert, + logicErr, "invalid halfedge"); return true; } @@ -680,8 +683,8 @@ void QuickHull::setupInitialTetrahedron() { std::min((size_t)2, vertexCount - 1), std::min((size_t)3, vertexCount - 1)); } - ASSERT(selectedPoints.first != selectedPoints.second, - logicErr("degenerate selectedPoints")); + DEBUG_ASSERT(selectedPoints.first != selectedPoints.second, logicErr, + "degenerate selectedPoints"); // Find the most distant point to the line between the two chosen extreme // points. @@ -730,8 +733,8 @@ void QuickHull::setupInitialTetrahedron() { } // These three points form the base triangle for our tetrahedron. - ASSERT(selectedPoints.first != maxI && selectedPoints.second != maxI, - logicErr("degenerate selectedPoints")); + DEBUG_ASSERT(selectedPoints.first != maxI && selectedPoints.second != maxI, + logicErr, "degenerate selectedPoints"); std::array baseTriangle{selectedPoints.first, selectedPoints.second, maxI}; const vec3 baseTriangleVertices[] = {originalVertexData[baseTriangle[0]], diff --git a/godot/thirdparty/manifold/src/sdf.cpp b/godot/thirdparty/manifold/src/sdf.cpp index 0e644fb1..64dd7094 100644 --- a/godot/thirdparty/manifold/src/sdf.cpp +++ b/godot/thirdparty/manifold/src/sdf.cpp @@ -14,10 +14,10 @@ #include "./hashtable.h" #include "./impl.h" +#include "./parallel.h" #include "./utils.h" #include "./vec.h" #include "manifold/manifold.h" -#include "manifold/parallel.h" namespace { using namespace manifold; @@ -117,13 +117,15 @@ vec3 Position(ivec4 gridIndex, vec3 origin, vec3 spacing) { return origin + spacing * (vec3(gridIndex) + (gridIndex.w == 1 ? 0.0 : -0.5)); } +vec3 Bound(vec3 pos, vec3 origin, vec3 spacing, ivec3 gridSize) { + return min(max(pos, origin), origin + spacing * (vec3(gridSize) - 1)); +} + double BoundedSDF(ivec4 gridIndex, vec3 origin, vec3 spacing, ivec3 gridSize, double level, std::function sdf) { - auto Min = [](ivec3 p) { return std::min(p.x, std::min(p.y, p.z)); }; - const ivec3 xyz(gridIndex); - const int lowerBoundDist = Min(xyz); - const int upperBoundDist = Min(gridSize - xyz); + const int lowerBoundDist = minelem(xyz); + const int upperBoundDist = minelem(gridSize - xyz); const int boundDist = std::min(lowerBoundDist, upperBoundDist - gridIndex.w); if (boundDist < 0) { @@ -264,7 +266,7 @@ struct NearSurface { // Bound the delta of each vert to ensure the tetrahedron cannot invert. if (la::all(la::less(la::abs(pos - gridPos), kS * spacing))) { const int idx = AtomicAdd(vertIndex[0], 1); - vertPos[idx] = pos; + vertPos[idx] = Bound(pos, origin, spacing, gridSize); gridVert.movedVert = idx; for (int j = 0; j < 7; ++j) { if (gridVert.edgeVerts[j] == kCrossing) gridVert.edgeVerts[j] = idx; @@ -323,9 +325,10 @@ struct ComputeVerts { } const int idx = AtomicAdd(vertIndex[0], 1); - vertPos[idx] = FindSurface(position, gridVert.distance, - Position(neighborIndex, origin, spacing), val, - tol, level, sdf); + const vec3 pos = FindSurface(position, gridVert.distance, + Position(neighborIndex, origin, spacing), + val, tol, level, sdf); + vertPos[idx] = Bound(pos, origin, spacing, gridSize); gridVert.edgeVerts[i] = idx; } } @@ -423,23 +426,23 @@ namespace manifold { /** * Constructs a level-set manifold from the input Signed-Distance Function - * (SDF). This uses a form of Marching Tetrahedra (akin to Marching Cubes, but - * better for manifoldness). Instead of using a cubic grid, it uses a - * body-centered cubic grid (two shifted cubic grids). This means if your - * function's interior exceeds the given bounds, you will see a kind of - * egg-crate shape closing off the manifold, which is due to the underlying - * grid. + * (SDF). This uses a form of Marching Tetrahedra (akin to Marching + * Cubes, but better for manifoldness). Instead of using a cubic grid, it uses a + * body-centered cubic grid (two shifted cubic grids). These grid points are + * snapped to the surface where possible to keep short edges from forming. * * @param sdf The signed-distance functor, containing this function signature: * `double operator()(vec3 point)`, which returns the * signed distance of a given point in R^3. Positive values are inside, - * negative outside. + * negative outside. There is no requirement that the function be a true + * distance, or even continuous. * @param bounds An axis-aligned box that defines the extent of the grid. * @param edgeLength Approximate maximum edge length of the triangles in the * final result. This affects grid spacing, and hence has a strong effect on * performance. - * @param level You can inset your Mesh by using a positive value, or outset - * it with a negative value. + * @param level Extract the surface at this value of your sdf; defaults to + * zero. You can inset your mesh by using a positive value, or outset it with a + * negative value. * @param tolerance Ensure each vertex is within this distance of the true * surface. Defaults to -1, which will return the interpolated * crossing-point based on the two nearest grid points. Small positive values diff --git a/godot/thirdparty/manifold/src/shared.h b/godot/thirdparty/manifold/src/shared.h index b1e80737..3f333614 100644 --- a/godot/thirdparty/manifold/src/shared.h +++ b/godot/thirdparty/manifold/src/shared.h @@ -14,10 +14,10 @@ #pragma once +#include "./parallel.h" #include "./sparse.h" #include "./utils.h" #include "./vec.h" -#include "manifold/parallel.h" namespace manifold { diff --git a/godot/thirdparty/manifold/src/smoothing.cpp b/godot/thirdparty/manifold/src/smoothing.cpp index 5c1d7e81..57d81b46 100644 --- a/godot/thirdparty/manifold/src/smoothing.cpp +++ b/godot/thirdparty/manifold/src/smoothing.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" namespace { using namespace manifold; diff --git a/godot/thirdparty/manifold/src/sort.cpp b/godot/thirdparty/manifold/src/sort.cpp index a1c78e08..f394aa52 100644 --- a/godot/thirdparty/manifold/src/sort.cpp +++ b/godot/thirdparty/manifold/src/sort.cpp @@ -16,7 +16,7 @@ #include #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" namespace { using namespace manifold; diff --git a/godot/thirdparty/manifold/src/sparse.h b/godot/thirdparty/manifold/src/sparse.h index 9eedb16a..a25ea611 100644 --- a/godot/thirdparty/manifold/src/sparse.h +++ b/godot/thirdparty/manifold/src/sparse.h @@ -13,11 +13,11 @@ // limitations under the License. #pragma once +#include "./parallel.h" #include "./utils.h" #include "./vec.h" #include "manifold/common.h" #include "manifold/optional_assert.h" -#include "manifold/parallel.h" namespace { template diff --git a/godot/thirdparty/manifold/src/subdivision.cpp b/godot/thirdparty/manifold/src/subdivision.cpp index 83b66d44..c8631d79 100644 --- a/godot/thirdparty/manifold/src/subdivision.cpp +++ b/godot/thirdparty/manifold/src/subdivision.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include "./impl.h" -#include "manifold/parallel.h" +#include "./parallel.h" template <> struct std::hash { diff --git a/godot/thirdparty/manifold/src/utils.h b/godot/thirdparty/manifold/src/utils.h index 29589f53..12d6a584 100644 --- a/godot/thirdparty/manifold/src/utils.h +++ b/godot/thirdparty/manifold/src/utils.h @@ -19,12 +19,6 @@ #include #include -#ifdef MANIFOLD_DEBUG -#include -#include -#include -#endif - #include "./vec.h" #include "manifold/common.h" @@ -39,7 +33,7 @@ #endif #endif -#include "manifold/parallel.h" +#include "./parallel.h" #if __has_include() #include @@ -230,95 +224,4 @@ inline mat4 Mat4(mat3x4 a) { } inline mat3 Mat3(mat2x3 a) { return mat3({a[0], 0}, {a[1], 0}, {a[2], 1}); } -#ifdef MANIFOLD_DEBUG - -template -std::ostream& operator<<(std::ostream& out, const la::vec& v) { - return out << '{' << v[0] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::vec& v) { - return out << '{' << v[0] << ',' << v[1] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::vec& v) { - return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::vec& v) { - return out << '{' << v[0] << ',' << v[1] << ',' << v[2] << ',' << v[3] << '}'; -} - -template -std::ostream& operator<<(std::ostream& out, const la::mat& m) { - return out << '{' << m[0] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::mat& m) { - return out << '{' << m[0] << ',' << m[1] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::mat& m) { - return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << '}'; -} -template -std::ostream& operator<<(std::ostream& out, const la::mat& m) { - return out << '{' << m[0] << ',' << m[1] << ',' << m[2] << ',' << m[3] << '}'; -} - -inline std::ostream& operator<<(std::ostream& stream, const Box& box) { - return stream << "min: " << box.min << ", " - << "max: " << box.max; -} - -inline std::ostream& operator<<(std::ostream& stream, const Rect& box) { - return stream << "min: " << box.min << ", " - << "max: " << box.max; -} - -/** - * Print the contents of this vector to standard output. Only exists if compiled - * with MANIFOLD_DEBUG flag. - */ -template -void Dump(const std::vector& vec) { - std::cout << "Vec = " << std::endl; - for (size_t i = 0; i < vec.size(); ++i) { - std::cout << i << ", " << vec[i] << ", " << std::endl; - } - std::cout << std::endl; -} - -template -void Diff(const std::vector& a, const std::vector& b) { - std::cout << "Diff = " << std::endl; - if (a.size() != b.size()) { - std::cout << "a and b must have the same length, aborting Diff" - << std::endl; - return; - } - for (size_t i = 0; i < a.size(); ++i) { - if (a[i] != b[i]) - std::cout << i << ": " << a[i] << ", " << b[i] << std::endl; - } - std::cout << std::endl; -} - -struct Timer { - std::chrono::high_resolution_clock::time_point start, end; - - void Start() { start = std::chrono::high_resolution_clock::now(); } - - void Stop() { end = std::chrono::high_resolution_clock::now(); } - - float Elapsed() { - return std::chrono::duration_cast(end - start) - .count(); - } - void Print(std::string message) { - std::cout << "----------- " << std::round(Elapsed()) << " ms for " - << message << std::endl; - } -}; -#endif } // namespace manifold diff --git a/godot/thirdparty/manifold/src/vec.h b/godot/thirdparty/manifold/src/vec.h index 48c6d4e1..b59b4b6a 100644 --- a/godot/thirdparty/manifold/src/vec.h +++ b/godot/thirdparty/manifold/src/vec.h @@ -21,7 +21,7 @@ #endif #include -#include "manifold/parallel.h" +#include "./parallel.h" #include "manifold/vec_view.h" namespace manifold {