diff --git a/.clang-format b/.clang-format index a6822dc2cdd..46923aae03d 100644 --- a/.clang-format +++ b/.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/COPYRIGHT.txt b/COPYRIGHT.txt index d8c2ffa5ccc..29e78f50632 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -195,7 +195,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/ diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 7ae73552190..82ca2438eea 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -702,6 +702,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)); @@ -1627,6 +1712,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/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 2cb9e6458ef..413775e0dd1 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -200,6 +200,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); @@ -1385,6 +1386,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/core/input/input_map.cpp b/core/input/input_map.cpp index 757468b8225..4448594e25d 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -521,12 +521,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 @@ -536,7 +539,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/core/math/projection.cpp b/core/math/projection.cpp index da286823938..4d71ef31f65 100644 --- a/core/math/projection.cpp +++ b/core/math/projection.cpp @@ -914,14 +914,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/core/os/os.cpp b/core/os/os.cpp index 4b599c70ee5..fda9cc55123 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -538,9 +538,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/core/register_core_types.cpp b/core/register_core_types.cpp index 0f400e5db8a..92ddd7959a4 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -315,17 +315,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); @@ -333,23 +344,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/core/register_core_types.h b/core/register_core_types.h index 977b8447bfd..29b2f932f56 100644 --- a/core/register_core_types.h +++ b/core/register_core_types.h @@ -36,6 +36,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/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 0fd909c6054..c6a1663da56 100644 --- a/doc/classes/@GlobalScope.xml +++ b/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/doc/classes/AnimationLibrary.xml b/doc/classes/AnimationLibrary.xml index 7f87ea4616e..51588a60526 100644 --- a/doc/classes/AnimationLibrary.xml +++ b/doc/classes/AnimationLibrary.xml @@ -31,6 +31,12 @@ Returns the keys for the [Animation]s stored in the library. + + + + Returns the key count for the [Animation]s stored in the library. + + diff --git a/doc/classes/CollisionShape3D.xml b/doc/classes/CollisionShape3D.xml index a4e0ed0b282..69a7dd2b366 100644 --- a/doc/classes/CollisionShape3D.xml +++ b/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/doc/classes/Control.xml b/doc/classes/Control.xml index 9a26d765aad..a3e999501cf 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -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/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index fb4f9018298..7d954e2aa0b 100644 --- a/doc/classes/DisplayServer.xml +++ b/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/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index f3ef010c599..0d471b579dd 100644 --- a/doc/classes/LightmapGI.xml +++ b/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/doc/classes/LookAtModifier3D.xml b/doc/classes/LookAtModifier3D.xml index b6d106c4c5c..2475de18688 100644 --- a/doc/classes/LookAtModifier3D.xml +++ b/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/doc/classes/MeshLibrary.xml b/doc/classes/MeshLibrary.xml index f65e29af8e5..5f51d18e4e0 100644 --- a/doc/classes/MeshLibrary.xml +++ b/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/doc/classes/Object.xml b/doc/classes/Object.xml index 2f01f8acf23..4a81943fbbc 100644 --- a/doc/classes/Object.xml +++ b/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/doc/classes/RDTextureFormat.xml b/doc/classes/RDTextureFormat.xml index ac875ab7c10..617ed954347 100644 --- a/doc/classes/RDTextureFormat.xml +++ b/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/doc/classes/RDVertexAttribute.xml b/doc/classes/RDVertexAttribute.xml index 31605f5471f..364b82526b6 100644 --- a/doc/classes/RDVertexAttribute.xml +++ b/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/doc/classes/RenderSceneBuffersRD.xml b/doc/classes/RenderSceneBuffersRD.xml index 6a5aba1dbc2..6a9445a25ed 100644 --- a/doc/classes/RenderSceneBuffersRD.xml +++ b/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/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index a0e6e6cf551..f2b65eae653 100644 --- a/doc/classes/RenderingDevice.xml +++ b/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 Redot is built in dev or debug mode; when the GPU crashes Redot 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/doc/classes/RetargetModifier3D.xml b/doc/classes/RetargetModifier3D.xml new file mode 100644 index 00000000000..522b954aba3 --- /dev/null +++ b/doc/classes/RetargetModifier3D.xml @@ -0,0 +1,34 @@ + + + + A modifier to transfer parent skeleton poses (or global poses) to child skeletons in model space with different rests. + + + Retrieves the pose (or global pose) relative to the parent Skeleton's rest in model space and transfers it to the child Skeleton. + This modifier rewrites the pose of the child skeleton directly in the parent skeleton's update process. This means that it overwrites the mapped bone pose set in the normal process on the target skeleton. If you want to set the target skeleton bone pose after retargeting, you will need to add a [SkeletonModifier3D] child to the target skeleton and thereby modify the pose. + [b]Note:[/b] When the [member use_global_pose] is enabled, even if it is an unmapped bone, it can cause visual problems because the global pose is applied ignoring the parent bone's pose [b]if it has mapped bone children[/b]. See also [member use_global_pose]. + + + + + + If [code]true[/code], allows to retarget the position. + + + [SkeletonProfile] for retargeting bones with names matching the bone list. + + + If [code]true[/code], allows to retarget the rotation. + + + If [code]true[/code], allows to retarget the scale. + + + If [code]false[/code], in case the target skeleton has fewer bones than the source skeleton, the source bone parent's transform will be ignored. + Instead, it is possible to retarget between models with different body shapes, and position, rotation, and scale can be retargeted separately. + If [code]true[/code], retargeting is performed taking into account global pose. + In case the target skeleton has fewer bones than the source skeleton, the source bone parent's transform is taken into account. However, bone length between skeletons must match exactly, if not, the bones will be forced to expand or shrink. + This is useful for using dummy bone with length [code]0[/code] to match postures when retargeting between models with different number of bones. + + + diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index f5b808be8e2..aa751de5f24 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -393,6 +393,11 @@ [b]Note:[/b] During the update process, this signal is not fired, so modification by [SkeletonModifier3D] is not detected. + + + Emitted when the rest is updated. + + Emitted when the value of [member show_rest_only] changes. diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index 678b7690d97..3e772c4e884 100644 --- a/doc/classes/Viewport.xml +++ b/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/doc/classes/Window.xml b/doc/classes/Window.xml index 424941b8422..6e833ef25a5 100644 --- a/doc/classes/Window.xml +++ b/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/drivers/d3d12/rendering_context_driver_d3d12.cpp b/drivers/d3d12/rendering_context_driver_d3d12.cpp index d8e89159410..a7bf29d44e8 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_context_driver_d3d12.cpp @@ -98,6 +98,9 @@ RenderingContextDriverD3D12::~RenderingContextDriverD3D12() { if (lib_dxgi) { FreeLibrary(lib_dxgi); } + if (lib_dcomp) { + FreeLibrary(lib_dcomp); + } } Error RenderingContextDriverD3D12::_init_device_factory() { @@ -110,6 +113,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/drivers/d3d12/rendering_context_driver_d3d12.h b/drivers/d3d12/rendering_context_driver_d3d12.h index 2a3b3c5b1dc..e49da526ff8 100644 --- a/drivers/d3d12/rendering_context_driver_d3d12.h +++ b/drivers/d3d12/rendering_context_driver_d3d12.h @@ -61,6 +61,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 @@ -116,10 +130,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/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 053f98b3953..cc711977c58 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -2471,7 +2471,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; @@ -2480,7 +2480,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; @@ -2488,14 +2488,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); @@ -2505,6 +2502,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/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 8b16da8d475..504924b36d4 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3316,10 +3316,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. @@ -3357,7 +3353,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); @@ -3414,11 +3410,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/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index d3e79dbe04c..5210092a3fc 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1402,12 +1402,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(); } } @@ -6630,6 +6644,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()) { @@ -7877,10 +7892,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/editor/create_dialog.cpp b/editor/create_dialog.cpp index 9acf2012b14..7d62ce32a27 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -766,6 +766,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); @@ -781,6 +782,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/editor/debugger/editor_performance_profiler.cpp b/editor/debugger/editor_performance_profiler.cpp index 0f07ead61c4..d74850128c6 100644 --- a/editor/debugger/editor_performance_profiler.cpp +++ b/editor/debugger/editor_performance_profiler.cpp @@ -406,6 +406,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/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 590be573ea3..e61c3d8e048 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -714,6 +714,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/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 6cbb8ee723c..2205a66ef3a 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -809,6 +809,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/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index a551500511e..3d3cd548ad2 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -1064,6 +1064,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(); @@ -1923,6 +1924,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); @@ -1958,6 +1960,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/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 5aa45072119..ccc228ceae4 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -529,6 +529,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(); @@ -545,21 +559,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(); } @@ -668,15 +685,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/editor/dependency_editor.h b/editor/dependency_editor.h index 8d07911a693..2476dac3935 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -33,6 +33,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" @@ -100,6 +101,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; @@ -124,6 +127,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/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index bd4dd040932..401b4a281cf 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -737,6 +737,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/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index fb3778ae42a..ec36d04e702 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -961,6 +961,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/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index 6b7cd476cd4..b0df84da5b7 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -991,6 +991,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/editor/editor_interface.cpp b/editor/editor_interface.cpp index 8873bb2e766..7fd694aa7fd 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -33,6 +33,7 @@ #include "editor_interface.h" #include "editor_interface.compat.inc" +#include "core/config/project_settings.h" #include "editor/editor_command_palette.h" #include "editor/editor_feature_profile.h" #include "editor/editor_main_screen.h" @@ -52,6 +53,10 @@ #include "editor/property_selector.h" #include "editor/themes/editor_scale.h" #include "main/main.h" +#include "plugins/editor_preview_plugins.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/world_environment.h" #include "scene/gui/box_container.h" #include "scene/gui/control.h" #include "scene/main/window.h" @@ -100,6 +105,27 @@ EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const { return EditorUndoRedoManager::get_singleton(); } +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; + Node3D *base = mesh_node; + while (base) { + accum_xform = base->get_transform() * accum_xform; + base = Object::cast_to(base->get_parent()); + } + + AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb()); + p_scene_aabb.merge_with(aabb); + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + p_scene_aabb = _calculate_aabb_for_scene(p_node->get_child(i), p_scene_aabb); + } + + return p_scene_aabb; +} + TypedArray EditorInterface::_make_mesh_previews(const TypedArray &p_meshes, int p_preview_size) { Vector> meshes; @@ -205,6 +231,136 @@ 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."); + + SubViewport *sub_viewport_node = memnew(SubViewport); + AABB scene_aabb; + scene_aabb = _calculate_aabb_for_scene(p_scene, scene_aabb); + + sub_viewport_node->set_update_mode(SubViewport::UPDATE_ALWAYS); + sub_viewport_node->set_size(Vector2i(p_preview_size, p_preview_size)); + sub_viewport_node->set_transparent_background(false); + Ref world; + world.instantiate(); + sub_viewport_node->set_world_3d(world); + + EditorNode::get_singleton()->add_child(sub_viewport_node); + Ref env; + env.instantiate(); + env->set_background(Environment::BG_CLEAR_COLOR); + + Ref camera_attributes; + camera_attributes.instantiate(); + + Node3D *root = memnew(Node3D); + root->set_name("Root"); + sub_viewport_node->add_child(root); + + Camera3D *camera = memnew(Camera3D); + camera->set_environment(env); + camera->set_attributes(camera_attributes); + camera->set_name("Camera3D"); + root->add_child(camera); + camera->set_current(true); + + camera->set_position(Vector3(0.0, 0.0, 3.0)); + + DirectionalLight3D *light = memnew(DirectionalLight3D); + light->set_name("Light"); + DirectionalLight3D *light2 = memnew(DirectionalLight3D); + light2->set_name("Light2"); + light2->set_color(Color(0.7, 0.7, 0.7, 1.0)); + + root->add_child(light); + root->add_child(light2); + + sub_viewport_node->add_child(p_scene); + + // Calculate the camera and lighting position based on the size of the scene. + Vector3 center = scene_aabb.get_center(); + float camera_size = scene_aabb.get_longest_axis_size(); + + const float cam_rot_x = -Math_PI / 4; + const float cam_rot_y = -Math_PI / 4; + + camera->set_orthogonal(camera_size * 2.0, 0.0001, camera_size * 2.0); + + Transform3D xf; + xf.basis = Basis(Vector3(0, 1, 0), cam_rot_y) * Basis(Vector3(1, 0, 0), cam_rot_x); + xf.origin = center; + xf.translate_local(0, 0, camera_size); + + camera->set_transform(xf); + + Transform3D xform; + xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI / 6); + xform.basis = Basis().rotated(Vector3(1, 0, 0), Math_PI / 6) * xform.basis; + + light->set_transform(xform * Transform3D().looking_at(Vector3(-2, -1, -1), Vector3(0, 1, 0))); + light2->set_transform(xform * Transform3D().looking_at(Vector3(+1, -1, -2), Vector3(0, 1, 0))); + + // Update the renderer to get the screenshot. + DisplayServer::get_singleton()->process_events(); + Main::iteration(); + Main::iteration(); + + // Get the texture. + Ref texture = sub_viewport_node->get_texture(); + ERR_FAIL_COND_MSG(texture.is_null(), "Failed to get texture from sub_viewport_node."); + + // Remove the initial scene node. + sub_viewport_node->remove_child(p_scene); + + // Cleanup the viewport. + if (sub_viewport_node) { + 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. + Ref img = texture->get_image(); + if (img.is_valid() && img->get_width() > 0 && img->get_height() > 0) { + img = img->duplicate(); + + int preview_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size"); + preview_size *= EDSCALE; + + int vp_size = MIN(img->get_width(), img->get_height()); + int x = (img->get_width() - vp_size) / 2; + int y = (img->get_height() - vp_size) / 2; + + if (vp_size < preview_size) { + img->crop_from_point(x, y, vp_size, vp_size); + } else { + int ratio = vp_size / preview_size; + int size = preview_size * MAX(1, ratio / 2); + + x = (img->get_width() - size) / 2; + y = (img->get_height() - size) / 2; + + img->crop_from_point(x, y, size, size); + img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS); + } + img->convert(Image::FORMAT_RGB8); + + String temp_path = EditorPaths::get_singleton()->get_cache_dir(); + String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text(); + cache_base = temp_path.path_join("resthumb-" + cache_base); + + post_process_preview(img); + img->save_png(cache_base + ".png"); + } + + EditorResourcePreview::get_singleton()->check_for_invalidation(p_path); + EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed")); +} + void EditorInterface::set_plugin_enabled(const String &p_plugin, bool p_enabled) { EditorNode::get_singleton()->set_addon_plugin_enabled(p_plugin, p_enabled, true); } diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 62e2a06cc21..ae85fb68caf 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -81,6 +81,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 &p_scene_aabb); protected: static void _bind_methods(); @@ -109,6 +110,7 @@ class EditorInterface : public Object { EditorUndoRedoManager *get_editor_undo_redo() const; Vector> make_mesh_previews(const Vector> &p_meshes, Vector *p_transforms, int p_preview_size); + void make_scene_preview(const String &p_path, Node *p_scene, int p_preview_size); void set_plugin_enabled(const String &p_plugin, bool p_enabled); bool is_plugin_enabled(const String &p_plugin) const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f987d94aeb3..87a60567f99 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -5192,7 +5192,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); } } @@ -5202,7 +5203,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); } } @@ -5211,7 +5213,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/editor/editor_properties.cpp b/editor/editor_properties.cpp index 9e91c3f3e98..bb309a1e3c1 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -54,6 +54,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" @@ -2647,7 +2648,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/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index ba79f287293..41b872bf5dc 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -149,6 +149,10 @@ void EditorResourcePreview::_preview_ready(const String &p_path, int p_hash, con if (!p_path.begins_with("ID:")) { modified_time = FileAccess::get_modified_time(p_path); + String import_path = p_path + ".import"; + if (FileAccess::exists(import_path)) { + modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path)); + } } Item item; @@ -239,7 +243,14 @@ void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref< } Ref f = FileAccess::open(cache_base + ".txt", FileAccess::WRITE); ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file '" + cache_base + ".txt'. Check user write permissions."); - _write_preview_cache(f, thumbnail_size, has_small_texture, FileAccess::get_modified_time(p_item.path), FileAccess::get_md5(p_item.path), p_metadata); + + uint64_t modtime = FileAccess::get_modified_time(p_item.path); + String import_path = p_item.path + ".import"; + if (FileAccess::exists(import_path)) { + modtime = MAX(modtime, FileAccess::get_modified_time(import_path)); + } + + _write_preview_cache(f, thumbnail_size, has_small_texture, modtime, FileAccess::get_md5(p_item.path), p_metadata); } } @@ -300,6 +311,11 @@ void EditorResourcePreview::_iterate() { _generate_preview(texture, small_texture, item, cache_base, preview_metadata); } else { uint64_t modtime = FileAccess::get_modified_time(item.path); + String import_path = item.path + ".import"; + if (FileAccess::exists(import_path)) { + modtime = MAX(modtime, FileAccess::get_modified_time(import_path)); + } + int tsize; bool has_small_texture; uint64_t last_modtime; @@ -515,6 +531,11 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) { if (cache.has(p_path)) { uint64_t modified_time = FileAccess::get_modified_time(p_path); + String import_path = p_path + ".import"; + if (FileAccess::exists(import_path)) { + modified_time = MAX(modified_time, FileAccess::get_modified_time(import_path)); + } + if (modified_time != cache[p_path].modified_time) { cache.erase(p_path); call_invalidated = true; diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 0aa9ed7ac5a..88e49dc3255 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -355,6 +355,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/editor/export/project_export.cpp b/editor/export/project_export.cpp index e1a9a5a3311..b7cd277ce8c 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -1418,6 +1418,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/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 4d62ba99db4..0cd70fcbbec 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -4148,6 +4148,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/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 6235dc0ad94..c74ca13b833 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -2319,6 +2319,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); @@ -2328,6 +2329,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/editor/icons/RetargetModifier3D.svg b/editor/icons/RetargetModifier3D.svg new file mode 100644 index 00000000000..2ca7af6c6e4 --- /dev/null +++ b/editor/icons/RetargetModifier3D.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp index 003b1b0c153..f25672faa0d 100644 --- a/editor/import/3d/post_import_plugin_skeleton_renamer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp @@ -41,7 +41,7 @@ void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List *r_options) { if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { - r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true)); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique"), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name"), "GeneralSkeleton")); } diff --git a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp index 4066524f48c..d9bd9ff6e20 100644 --- a/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp @@ -35,6 +35,7 @@ #include "editor/import/3d/scene_import_settings.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/retarget_modifier_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/bone_map.h" @@ -44,8 +45,18 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImpo r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true)); - r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); + + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/rest_fixer/retarget_method", PROPERTY_HINT_ENUM, "None,Overwrite Axis,Use Retarget Modifier", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/keep_global_rest_on_leftovers"), true)); + String skeleton_bones_must_be_renamed_warning = String( + "The skeleton modifier option uses SkeletonProfile as a list of bone names and retargets by name matching. Without renaming, retargeting by modifier will not work and the track path of the animation will be broken and it will be not playbacked correctly."); // TODO: translate. + r_options->push_back(ResourceImporter::ImportOption( + PropertyInfo( + Variant::STRING, U"retarget/rest_fixer/\u26A0_validation_warning/skeleton_bones_must_be_renamed", + PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY), + Variant(skeleton_bones_must_be_renamed_warning))); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/use_global_pose"), true)); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/rest_fixer/original_skeleton_name"), "OriginalSkeleton")); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); // TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options). // get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values. @@ -65,7 +76,11 @@ Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(Intern } } } else if (p_option == "retarget/rest_fixer/keep_global_rest_on_leftovers") { - return bool(p_options["retarget/rest_fixer/overwrite_axis"]); + return int(p_options["retarget/rest_fixer/retarget_method"]) == 1; + } else if (p_option == "retarget/rest_fixer/original_skeleton_name" || p_option == "retarget/rest_fixer/use_global_pose") { + return int(p_options["retarget/rest_fixer/retarget_method"]) == 2; + } else if (p_option.begins_with("retarget/") && p_option.ends_with("skeleton_bones_must_be_renamed")) { + return int(p_options["retarget/rest_fixer/retarget_method"]) == 2 && bool(p_options["retarget/bone_renamer/rename_bones"]) == false; } } return true; @@ -149,7 +164,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory src_skeleton->set_bone_pose_position(src_idx, src_skeleton->get_bone_pose_position(src_idx) * scl); } - // Fix animation. + // Fix animation by changing node transform. bones_to_process = src_skeleton->get_parentless_bones(); { TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); @@ -226,6 +241,10 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory List anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { + if (String(name).contains("/")) { + continue; // Avoid animation library which may be created by importer dynamically. + } + Ref anim = ap->get_animation(name); int track_len = anim->get_track_count(); @@ -456,8 +475,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } - // Overwrite axis. - if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) { + bool is_using_modifier = int(p_options["retarget/rest_fixer/retarget_method"]) == 2; + bool is_using_global_pose = bool(p_options["retarget/rest_fixer/use_global_pose"]); + Skeleton3D *orig_skeleton = nullptr; + Skeleton3D *profile_skeleton = nullptr; + + // Retarget in some way. + if (int(p_options["retarget/rest_fixer/retarget_method"]) > 0) { LocalVector old_skeleton_rest; LocalVector old_skeleton_global_rest; for (int i = 0; i < src_skeleton->get_bone_count(); i++) { @@ -465,11 +489,151 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); } + // Build structure for modifier. + if (is_using_modifier) { + orig_skeleton = src_skeleton; + + // Duplicate src_skeleton to modify animation tracks, it will memdelele after that animation track modification. + src_skeleton = memnew(Skeleton3D); + for (int i = 0; i < orig_skeleton->get_bone_count(); i++) { + src_skeleton->add_bone(orig_skeleton->get_bone_name(i)); + src_skeleton->set_bone_rest(i, orig_skeleton->get_bone_rest(i)); + src_skeleton->set_bone_pose(i, orig_skeleton->get_bone_pose(i)); + } + for (int i = 0; i < orig_skeleton->get_bone_count(); i++) { + src_skeleton->set_bone_parent(i, orig_skeleton->get_bone_parent(i)); + } + src_skeleton->set_motion_scale(orig_skeleton->get_motion_scale()); + + // Rename orig_skeleton (previous src_skeleton), since it is not animated by animation track with GeneralSkeleton. + String original_skeleton_name = String(p_options["retarget/rest_fixer/original_skeleton_name"]); + String skel_name = orig_skeleton->get_name(); + ERR_FAIL_COND_MSG(original_skeleton_name.is_empty(), "Original skeleton name cannot be empty."); + ERR_FAIL_COND_MSG(original_skeleton_name == skel_name, "Original skeleton name must be different from unique skeleton name."); + + // Rename profile skeleton to be general skeleton. + profile_skeleton = memnew(Skeleton3D); + bool is_unique = orig_skeleton->is_unique_name_in_owner(); + if (is_unique) { + orig_skeleton->set_unique_name_in_owner(false); + } + orig_skeleton->set_name(original_skeleton_name); + profile_skeleton->set_name(skel_name); + if (is_unique) { + profile_skeleton->set_unique_name_in_owner(true); + } + // Build profile skeleton bones. + int len = profile->get_bone_size(); + for (int i = 0; i < len; i++) { + profile_skeleton->add_bone(profile->get_bone_name(i)); + profile_skeleton->set_bone_rest(i, profile->get_reference_pose(i)); + } + for (int i = 0; i < len; i++) { + int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i)); + if (target_parent >= 0) { + profile_skeleton->set_bone_parent(i, target_parent); + } + } + for (int i = 0; i < len; i++) { + Vector3 origin; + int found = orig_skeleton->find_bone(profile->get_bone_name(i)); + String parent_name = profile->get_bone_parent(i); + if (found >= 0) { + origin = orig_skeleton->get_bone_global_rest(found).origin; + if (profile->get_bone_name(i) != profile->get_root_bone()) { + int src_parent = -1; + while (src_parent < 0 && !parent_name.is_empty()) { + src_parent = orig_skeleton->find_bone(parent_name); + parent_name = profile->get_bone_parent(profile->find_bone(parent_name)); + } + if (src_parent >= 0) { + Transform3D parent_grest = orig_skeleton->get_bone_global_rest(src_parent); + origin = origin - parent_grest.origin; + } + } + } + int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i)); + if (target_parent >= 0) { + origin = profile_skeleton->get_bone_global_rest(target_parent).basis.get_rotation_quaternion().xform_inv(origin); + } + profile_skeleton->set_bone_rest(i, Transform3D(profile_skeleton->get_bone_rest(i).basis, origin)); + } + profile_skeleton->set_motion_scale(orig_skeleton->get_motion_scale()); + profile_skeleton->reset_bone_poses(); + // Make structure with modifier. + Node *owner = p_node->get_owner(); + + Node *pr = orig_skeleton->get_parent(); + pr->add_child(profile_skeleton); + profile_skeleton->set_owner(owner); + + RetargetModifier3D *mod = memnew(RetargetModifier3D); + profile_skeleton->add_child(mod); + mod->set_owner(owner); + mod->set_name("RetargetModifier3D"); + + orig_skeleton->set_owner(nullptr); + orig_skeleton->reparent(mod, false); + orig_skeleton->set_owner(owner); + orig_skeleton->set_unique_name_in_owner(true); + + mod->set_use_global_pose(is_using_global_pose); + mod->set_profile(profile); + + // Fix skeleton name in animation. + // Mapped skeleton is animated by %GenerarSkeleton:RenamedBoneName. + // Unmapped skeleton is animated by %OriginalSkeleton:OriginalBoneName. + if (is_using_modifier) { + TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); + String general_skeleton_pathname = UNIQUE_NODE_PREFIX + profile_skeleton->get_name(); + while (nodes.size()) { + AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); + List anims; + ap->get_animation_list(&anims); + for (const StringName &name : anims) { + Ref anim = ap->get_animation(name); + int track_len = anim->get_track_count(); + for (int i = 0; i < track_len; i++) { + if (anim->track_get_path(i).get_name_count() == 0) { + return; + } + if (anim->track_get_path(i).get_name(0) == general_skeleton_pathname) { + bool replace = false; + if (anim->track_get_path(i).get_subname_count() > 0) { + int found = profile_skeleton->find_bone(anim->track_get_path(i).get_concatenated_subnames()); + if (found < 0) { + replace = true; + } + } else { + replace = true; + } + if (replace) { + String path_string = UNIQUE_NODE_PREFIX + original_skeleton_name; + if (anim->track_get_path(i).get_name_count() > 1) { + Vector names = anim->track_get_path(i).get_names(); + names.remove_at(0); + for (int j = 0; j < names.size(); j++) { + path_string += "/" + names[i].operator String(); + } + } + if (anim->track_get_path(i).get_subname_count() > 0) { + path_string = path_string + String(":") + anim->track_get_path(i).get_concatenated_subnames(); + } + anim->track_set_path(i, path_string); + } + } + } + } + } + } + } + bool keep_global_rest_leftovers = bool(p_options["retarget/rest_fixer/keep_global_rest_on_leftovers"]); // Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants. + // When both is_using_modifier and is_using_global_pose are enabled, this array is used for detecting warning. Vector keep_bone_rest; - if (keep_global_rest_leftovers) { + if (is_using_modifier || keep_global_rest_leftovers) { Vector bones_to_process = src_skeleton->get_parentless_bones(); while (bones_to_process.size() > 0) { int src_idx = bones_to_process[0]; @@ -528,12 +692,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory if (src_parent_idx >= 0) { src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis; } - int prof_idx = profile->find_bone(src_bone_name); if (prof_idx >= 0) { - tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose. + // Mapped bone uses reference pose. + // It is fine to change rest here even though is_using_modifier is enabled, since next process is aborted with unmapped bones. + tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; } else if (keep_global_rest_leftovers && keep_bone_rest.has(src_idx)) { - tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone without mapped children keeps global rest. + // Non-Mapped bones without mapped children keeps global rest. + tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; } } @@ -550,7 +716,8 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin))); } - // Fix animation. + // Fix animation by changing rest. + bool warning_detected = false; { TypedArray nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { @@ -575,7 +742,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory ERR_CONTINUE(!node); Skeleton3D *track_skeleton = Object::cast_to(node); - if (!track_skeleton || track_skeleton != src_skeleton) { + if (!track_skeleton || + (is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) || + (!is_using_modifier && track_skeleton != src_skeleton)) { continue; } @@ -586,6 +755,16 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory int bone_idx = src_skeleton->find_bone(bn); + if (is_using_modifier) { + int prof_idx = profile->find_bone(bn); + if (prof_idx < 0) { + if (keep_bone_rest.has(bone_idx)) { + warning_detected = true; + } + continue; // If is_using_modifier, the original skeleton rest is not changed. + } + } + Transform3D old_rest = old_skeleton_rest[bone_idx]; Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx); Transform3D old_pg; @@ -631,6 +810,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } } + if (is_using_global_pose && warning_detected) { + // TODO: + // Theoretically, if A and its conversion are calculated correctly taking into account the difference in the number of bones, + // there is no need to disable use_global_pose, but this is probably a fairly niche case. + WARN_PRINT_ED("Animated extra bone between mapped bones detected, consider disabling Use Global Pose option to prevent that the pose origin be overridden by the RetargetModifier3D."); + } + if (p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") && !bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) { // If Reset All Bone Poses After Import is disabled, preserve the original bone pose, adjusted for the new bone rolls. for (int bone_idx = 0; bone_idx < src_skeleton->get_bone_count(); bone_idx++) { @@ -656,6 +842,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } + if (is_using_modifier) { + memdelete(src_skeleton); + src_skeleton = profile_skeleton; + } + is_rest_changed = true; } @@ -683,7 +874,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory ERR_CONTINUE(!node); Skeleton3D *track_skeleton = Object::cast_to(node); - if (!track_skeleton || track_skeleton != src_skeleton) { + if (!track_skeleton || + (is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) || + (!is_using_modifier && track_skeleton != src_skeleton)) { continue; } @@ -698,7 +891,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } - if (is_rest_changed) { + if (!is_using_modifier && is_rest_changed) { // Fix skin. { HashSet> mutated_skins; @@ -768,6 +961,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion()); src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale()); } + if (orig_skeleton) { + for (int i = 0; i < orig_skeleton->get_bone_count(); i++) { + Transform3D fixed_rest = orig_skeleton->get_bone_rest(i); + orig_skeleton->set_bone_pose_position(i, fixed_rest.origin); + orig_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion()); + orig_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale()); + } + } } } diff --git a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp index 6fd2da303d6..7063bd81896 100644 --- a/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp @@ -41,7 +41,7 @@ void PostImportPluginSkeletonTrackOrganizer::get_internal_import_options(Interna if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/except_bone_transform"), false)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unimportant_positions"), true)); - r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unmapped_bones"), false)); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/remove_tracks/unmapped_bones", PROPERTY_HINT_ENUM, "None,Remove,Separate Library"), 0)); } } @@ -63,9 +63,9 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate } bool remove_except_bone = bool(p_options["retarget/remove_tracks/except_bone_transform"]); bool remove_positions = bool(p_options["retarget/remove_tracks/unimportant_positions"]); - bool remove_unmapped_bones = bool(p_options["retarget/remove_tracks/unmapped_bones"]); + int separate_unmapped_bones = int(p_options["retarget/remove_tracks/unmapped_bones"]); - if (!remove_positions && !remove_unmapped_bones) { + if (!remove_positions && separate_unmapped_bones == 0) { return; } @@ -74,10 +74,16 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate AnimationPlayer *ap = Object::cast_to(nodes.pop_back()); List anims; ap->get_animation_list(&anims); + + Ref unmapped_al; + unmapped_al.instantiate(); + for (const StringName &name : anims) { Ref anim = ap->get_animation(name); int track_len = anim->get_track_count(); Vector remove_indices; + Vector mapped_bone_indices; + Vector unmapped_bone_indices; for (int i = 0; i < track_len; i++) { String track_path = String(anim->track_get_path(i).get_concatenated_names()); Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path)); @@ -98,16 +104,19 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate StringName bn = anim->track_get_path(i).get_subname(0); if (bn) { int prof_idx = profile->find_bone(bone_map->find_profile_bone_name(bn)); - if (remove_unmapped_bones && prof_idx < 0) { - remove_indices.push_back(i); + if (prof_idx < 0) { + unmapped_bone_indices.push_back(i); continue; } if (remove_positions && anim->track_get_type(i) == Animation::TYPE_POSITION_3D && prof_idx >= 0) { StringName prof_bn = profile->get_bone_name(prof_idx); if (prof_bn == profile->get_root_bone() || prof_bn == profile->get_scale_base_bone()) { + mapped_bone_indices.push_back(i); continue; } remove_indices.push_back(i); + } else { + mapped_bone_indices.push_back(i); } } } @@ -116,11 +125,34 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate } } + if (separate_unmapped_bones == 2 && !unmapped_bone_indices.is_empty()) { + Ref unmapped_anim = anim->duplicate(); + Vector to_delete; + to_delete.append_array(mapped_bone_indices); + to_delete.append_array(remove_indices); + to_delete.sort(); + to_delete.reverse(); + for (int E : to_delete) { + unmapped_anim->remove_track(E); + } + unmapped_al->add_animation(name, unmapped_anim); + } + + if (separate_unmapped_bones >= 1) { + remove_indices.append_array(unmapped_bone_indices); + remove_indices.sort(); + } remove_indices.reverse(); for (int i = 0; i < remove_indices.size(); i++) { anim->remove_track(remove_indices[i]); } } + + if (unmapped_al->get_animation_list_size() == 0) { + unmapped_al.unref(); + } else if (separate_unmapped_bones == 2) { + ap->add_animation_library("unmapped_bones", unmapped_al); + } } } } diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 554d6bc7480..3477b696aa9 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -36,6 +36,7 @@ #include "core/io/dir_access.h" #include "core/io/resource_saver.h" #include "core/object/script_language.h" +#include "editor/editor_interface.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/import/3d/scene_import_settings.h" @@ -2314,6 +2315,7 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor } } + // TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken. for (int i = 0; i < post_importer_plugins.size(); i++) { Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options); if (ret.get_type() == Variant::BOOL) { @@ -2816,6 +2818,15 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani } } +void ResourceImporterScene::_generate_editor_preview_for_scene(const String &p_path, Node *p_scene) { + if (!Engine::get_singleton()->is_editor_hint()) { + return; + } + ERR_FAIL_COND_MSG(p_path.is_empty(), "Path is empty, 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); +} + Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap &p_options) { Ref importer; String ext = p_source_file.get_extension().to_lower(); @@ -3166,6 +3177,7 @@ Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p print_verbose("Saving scene to: " + p_save_path + ".scn"); err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'."); + _generate_editor_preview_for_scene(p_source_file, scene); } else { ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type); } diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index f2b57417e1a..42795a29f1f 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -238,6 +238,7 @@ class ResourceImporterScene : public ResourceImporter { }; void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions); + void _generate_editor_preview_for_scene(const String &p_path, Node *p_scene); String _scene_import_type = "PackedScene"; diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 19f423cd419..688f99cde75 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -903,7 +903,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/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp index 7bdeb151ea6..71a3d4fad67 100644 --- a/editor/plugins/camera_3d_editor_plugin.cpp +++ b/editor/plugins/camera_3d_editor_plugin.cpp @@ -32,8 +32,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) { @@ -78,9 +81,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 { @@ -88,27 +117,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/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h index b3ec6927daf..d361bf5a886 100644 --- a/editor/plugins/camera_3d_editor_plugin.h +++ b/editor/plugins/camera_3d_editor_plugin.h @@ -34,7 +34,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); @@ -53,11 +56,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/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp index 13fc56921d4..8e16fc68d8c 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp @@ -48,24 +48,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; } @@ -168,7 +150,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/editor/plugins/gizmos/camera_3d_gizmo_plugin.h b/editor/plugins/gizmos/camera_3d_gizmo_plugin.h index 63e573907b9..9a591d4d065 100644 --- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.h @@ -39,7 +39,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/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 2c852c718b0..bfe31d78115 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -51,17 +51,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; } @@ -313,9 +343,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; @@ -353,7 +394,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)); @@ -376,7 +417,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); } @@ -414,7 +455,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; @@ -478,7 +519,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; @@ -533,7 +574,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); } @@ -551,7 +592,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); } } @@ -560,7 +601,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()); } @@ -571,7 +612,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())); @@ -581,7 +622,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/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h index 0b6cd9164a8..46af8f14fe6 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.h @@ -40,6 +40,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/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index c12775dd3d8..318faee7197 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -158,6 +158,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/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 56de4d98358..783c922ab76 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -294,14 +294,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/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 785bcb70ed7..056800ad30b 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -94,6 +94,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" @@ -8824,7 +8825,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`. @@ -9313,6 +9319,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/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index fe5537785a8..5d029a1fcad 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -883,6 +883,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/editor/plugins/plugin_config_dialog.cpp b/editor/plugins/plugin_config_dialog.cpp index 78986cf6caf..f5104dcb9ae 100644 --- a/editor/plugins/plugin_config_dialog.cpp +++ b/editor/plugins/plugin_config_dialog.cpp @@ -307,7 +307,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/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index d2098de99a3..562a98c44a6 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -66,6 +66,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" @@ -4156,6 +4158,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); @@ -4199,6 +4202,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); @@ -4208,6 +4212,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/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 92a0fbbebb5..0314782f0e6 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -42,6 +42,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/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 2f9276bb46b..a4d6cff4188 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -823,6 +823,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/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index 4d3d2dd84a7..c994ced9739 100644 --- a/editor/plugins/shader_file_editor_plugin.cpp +++ b/editor/plugins/shader_file_editor_plugin.cpp @@ -259,6 +259,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/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index c4546394f31..2dae5809d20 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -1105,6 +1105,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/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index b8f3ba764c4..16dc13eafee 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -1958,6 +1958,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/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index dd2401b9071..2fc20178a66 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -50,6 +50,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" @@ -940,6 +942,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); @@ -1939,6 +1942,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/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index d0590dae183..820a6dc3d5c 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -43,9 +43,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/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index 7f6806347a8..046a1d108f9 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -320,6 +320,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/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index 43800d63c87..8fdebe1325a 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -2417,6 +2417,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)); @@ -3533,6 +3534,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/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 72ee8047c24..d66fcbd54c8 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -863,6 +863,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)); @@ -948,6 +949,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/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index dcf66cab9b1..7d1bc5fcd6a 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -1296,6 +1296,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/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index f7595769e71..67dc9aadda3 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -61,6 +61,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" @@ -6678,6 +6679,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/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 106bb4df5e2..e18fe13ae01 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -2143,6 +2143,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/main/main.cpp b/main/main.cpp index 046c5a2086f..72856dd3e26 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -735,6 +735,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(); @@ -1871,12 +1872,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 @@ -1901,20 +1945,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 @@ -1986,8 +2016,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 = ""; @@ -2546,9 +2574,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. @@ -2556,15 +2581,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 @@ -2580,15 +2596,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); @@ -3015,10 +3022,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/misc/extension_api_validation/4.3-stable.expected b/misc/extension_api_validation/4.3-stable.expected index 506844e6d63..0c988b89130 100644 --- a/misc/extension_api_validation/4.3-stable.expected +++ b/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/misc/utility/.clang-format-glsl b/misc/utility/.clang-format-glsl index c588df0236c..59efa8fa353 100644 --- a/misc/utility/.clang-format-glsl +++ b/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/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index 229d0b2b393..6eb37290ecc 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -291,6 +291,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/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp index d61c97eabfe..5732a3372bb 100644 --- a/modules/gdscript/language_server/gdscript_extend_parser.cpp +++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp @@ -59,6 +59,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; @@ -240,9 +246,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/modules/gdscript/tests/scripts/lsp/first_line_comment.gd b/modules/gdscript/tests/scripts/lsp/first_line_comment.gd new file mode 100644 index 00000000000..34ead5fabb3 --- /dev/null +++ b/modules/gdscript/tests/scripts/lsp/first_line_comment.gd @@ -0,0 +1,2 @@ +# Some comment +extends Node diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd index 463d207e591..69aecec6a82 100644 --- a/modules/gdscript/tests/scripts/runtime/features/stringify.gd +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd @@ -22,6 +22,7 @@ func test(): 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/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out index 9983366db03..b044f8105dc 100644 --- a/modules/gdscript/tests/scripts/runtime/features/stringify.out +++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out @@ -17,6 +17,7 @@ hello world [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 diff --git a/modules/gdscript/tests/test_lsp.h b/modules/gdscript/tests/test_lsp.h index bd9af6fc363..b5a3b83462e 100644 --- a/modules/gdscript/tests/test_lsp.h +++ b/modules/gdscript/tests/test_lsp.h @@ -377,6 +377,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); @@ -470,6 +482,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/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 328d79d9792..39eec218f74 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -1235,6 +1235,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/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index 58bf2f4bde6..4ea3d945123 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -716,6 +716,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/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index ee17835b3ea..99ba1134831 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -724,7 +724,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); @@ -1421,6 +1421,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()); } @@ -2051,8 +2052,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); @@ -2066,7 +2065,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/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 38bb70568f3..5745bfe2c03 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -358,16 +358,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) { @@ -390,11 +384,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(); @@ -426,7 +420,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(); @@ -441,7 +435,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; @@ -478,7 +472,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()) { @@ -494,7 +488,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; @@ -551,7 +545,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; } } @@ -689,17 +683,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/modules/navigation/nav_map.h b/modules/navigation/nav_map.h index 2594ace8fcc..b7cce05852d 100644 --- a/modules/navigation/nav_map.h +++ b/modules/navigation/nav_map.h @@ -118,15 +118,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; @@ -222,15 +214,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/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h index 525c786ec56..407128eac30 100644 --- a/modules/navigation/nav_utils.h +++ b/modules/navigation/nav_utils.h @@ -300,6 +300,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/modules/text_server_adv/thorvg_bounds_iterator.cpp b/modules/text_server_adv/thorvg_bounds_iterator.cpp deleted file mode 100644 index 4d521b2e8d0..00000000000 --- a/modules/text_server_adv/thorvg_bounds_iterator.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* REDOT ENGINE */ -/* https://redotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2024-present Redot Engine contributors */ -/* (see REDOT_AUTHORS.md) */ -/* 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/modules/text_server_adv/thorvg_bounds_iterator.h b/modules/text_server_adv/thorvg_bounds_iterator.h deleted file mode 100644 index 0cd9acc3b4f..00000000000 --- a/modules/text_server_adv/thorvg_bounds_iterator.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.h */ -/**************************************************************************/ -/* This file is part of: */ -/* REDOT ENGINE */ -/* https://redotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2024-present Redot Engine contributors */ -/* (see REDOT_AUTHORS.md) */ -/* 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/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 99523e7b6c8..84d6bc12b42 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -59,8 +59,6 @@ using namespace godot; #include "thorvg_svg_in_ot.h" -#include "thorvg_bounds_iterator.h" - #include #include @@ -94,8 +92,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"); @@ -113,15 +112,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()) { @@ -135,91 +145,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); @@ -252,6 +249,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/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h index 5d002981e35..41fee572cd0 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -62,8 +62,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/modules/text_server_fb/thorvg_bounds_iterator.cpp b/modules/text_server_fb/thorvg_bounds_iterator.cpp deleted file mode 100644 index 4d521b2e8d0..00000000000 --- a/modules/text_server_fb/thorvg_bounds_iterator.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.cpp */ -/**************************************************************************/ -/* This file is part of: */ -/* REDOT ENGINE */ -/* https://redotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2024-present Redot Engine contributors */ -/* (see REDOT_AUTHORS.md) */ -/* 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/modules/text_server_fb/thorvg_bounds_iterator.h b/modules/text_server_fb/thorvg_bounds_iterator.h deleted file mode 100644 index 0cd9acc3b4f..00000000000 --- a/modules/text_server_fb/thorvg_bounds_iterator.h +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************/ -/* thorvg_bounds_iterator.h */ -/**************************************************************************/ -/* This file is part of: */ -/* REDOT ENGINE */ -/* https://redotengine.org */ -/**************************************************************************/ -/* Copyright (c) 2024-present Redot Engine contributors */ -/* (see REDOT_AUTHORS.md) */ -/* 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/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index 10e690593e0..84d6bc12b42 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -51,7 +51,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 @@ -59,8 +59,6 @@ using namespace godot; #include "thorvg_svg_in_ot.h" -#include "thorvg_bounds_iterator.h" - #include #include @@ -94,8 +92,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"); @@ -113,15 +112,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()) { @@ -135,91 +145,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); @@ -252,6 +249,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/modules/text_server_fb/thorvg_svg_in_ot.h b/modules/text_server_fb/thorvg_svg_in_ot.h index 5d002981e35..41fee572cd0 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.h +++ b/modules/text_server_fb/thorvg_svg_in_ot.h @@ -62,8 +62,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/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 0a424a7c6a9..89d029eaedb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -1125,7 +1125,7 @@ class Godot(private val context: Context) { @Keep private fun createNewGodotInstance(args: Array): Int { - return primaryHost?.onNewGodotInstanceRequested(args) ?: 0 + return primaryHost?.onNewGodotInstanceRequested(args) ?: -1 } @Keep diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index 80812f6e411..f82061b9eeb 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -476,7 +476,7 @@ public int onNewGodotInstanceRequested(String[] args) { if (parentHost != null) { return parentHost.onNewGodotInstanceRequested(args); } - return 0; + return -1; } @Override diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java index 97fbe203bab..7c75e9ffbfc 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotHost.java @@ -94,7 +94,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/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt b/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt index 2befe0583bd..19fb4528926 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/io/FilePicker.kt +++ b/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/platform/android/os_android.cpp b/platform/android/os_android.cpp index aa22f970ffa..da66442736c 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -865,6 +865,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/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py index aca5d4ecba1..6a3855da84d 100644 --- a/platform/web/emscripten_helpers.py +++ b/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/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 8f278893820..8a0402a7eb0 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2169,6 +2169,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/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 020b0a76364..df8ed3412a8 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -358,6 +358,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/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index dbd68e56ec0..43625b57263 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1530,7 +1530,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(); } diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index a30e1576bf5..86b807cbcfe 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -67,6 +67,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/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 77665317a84..fb64bead13d 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -531,9 +531,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/scene/3d/look_at_modifier_3d.cpp b/scene/3d/look_at_modifier_3d.cpp index a251f5a78c2..e1ed24273b0 100644 --- a/scene/3d/look_at_modifier_3d.cpp +++ b/scene/3d/look_at_modifier_3d.cpp @@ -35,7 +35,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; @@ -51,11 +51,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; } } @@ -77,8 +77,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 { @@ -134,8 +155,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 { @@ -332,6 +374,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); @@ -345,6 +389,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); @@ -399,14 +445,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"); @@ -476,7 +524,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)); @@ -488,7 +536,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); @@ -499,6 +547,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) || @@ -506,16 +556,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(); } } @@ -530,7 +584,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/scene/3d/look_at_modifier_3d.h b/scene/3d/look_at_modifier_3d.h index f4bdc381ad0..b793152ec97 100644 --- a/scene/3d/look_at_modifier_3d.h +++ b/scene/3d/look_at_modifier_3d.h @@ -56,7 +56,8 @@ class LookAtModifier3D : public SkeletonModifier3D { }; private: - int bone = 0; + String bone_name; + int bone = -1; Vector3 forward_vector; Vector3 forward_vector_nrm; @@ -66,6 +67,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; @@ -125,6 +127,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; @@ -137,6 +141,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/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 91edd491776..9e27b49c72f 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -843,10 +843,10 @@ void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { ERR_THREAD_GUARD; if (p_keep_global_transform) { Transform3D temp = get_global_transform(); - Node::reparent(p_parent); + Node::reparent(p_parent, p_keep_global_transform); set_global_transform(temp); } else { - Node::reparent(p_parent); + Node::reparent(p_parent, p_keep_global_transform); } } diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp index c1690457018..84dd03d3539 100644 --- a/scene/3d/physics/collision_shape_3d.cpp +++ b/scene/3d/physics/collision_shape_3d.cpp @@ -83,12 +83,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; @@ -168,11 +175,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) { @@ -180,11 +202,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) { @@ -217,6 +255,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/scene/3d/physics/collision_shape_3d.h b/scene/3d/physics/collision_shape_3d.h index 74f7c16d338..56544806da7 100644 --- a/scene/3d/physics/collision_shape_3d.h +++ b/scene/3d/physics/collision_shape_3d.h @@ -45,6 +45,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 @@ -57,6 +64,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(); @@ -66,6 +80,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/scene/3d/retarget_modifier_3d.cpp b/scene/3d/retarget_modifier_3d.cpp new file mode 100644 index 00000000000..7e2462faf8e --- /dev/null +++ b/scene/3d/retarget_modifier_3d.cpp @@ -0,0 +1,443 @@ +/**************************************************************************/ +/* retarget_modifier_3d.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* 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 "retarget_modifier_3d.h" + +PackedStringArray RetargetModifier3D::get_configuration_warnings() const { + PackedStringArray warnings = SkeletonModifier3D::get_configuration_warnings(); + if (child_skeletons.is_empty()) { + warnings.push_back(RTR("There is no child Skeleton3D!")); + } + return warnings; +} + +/// Caching + +void RetargetModifier3D::_profile_changed(Ref p_old, Ref p_new) { + if (p_old.is_valid() && p_old->is_connected(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset))) { + p_old->disconnect(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset)); + } + profile = p_new; + if (p_new.is_valid() && !p_new->is_connected(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset))) { + p_new->connect(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset)); + } + cache_rests_with_reset(); +} + +void RetargetModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + if (p_old && p_old->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests))) { + p_old->disconnect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests)); + } + if (p_new && !p_new->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests))) { + p_new->connect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests)); + } + cache_rests(); +} + +void RetargetModifier3D::cache_rests_with_reset() { + _reset_child_skeleton_poses(); + cache_rests(); +} + +void RetargetModifier3D::cache_rests() { + source_bone_ids.clear(); + + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + source_bone_ids.push_back(source_skeleton->find_bone(E)); + } + + for (int i = 0; i < child_skeletons.size(); i++) { + _update_child_skeleton_rests(i); + } +} + +Vector RetargetModifier3D::cache_bone_global_rests(Skeleton3D *p_skeleton) { + // Retarget global pose in model space: + // tgt_global_pose.basis = src_global_pose.basis * src_rest.basis.inv * src_parent_global_rest.basis.inv * tgt_parent_global_rest.basis * tgt_rest.basis + // tgt_global_pose.origin = src_global_pose.origin + Skeleton3D *source_skeleton = get_skeleton(); + Vector bone_rests; + if (profile.is_null() || !source_skeleton) { + return bone_rests; + } + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + RetargetBoneInfo rbi; + int source_bone_id = source_skeleton->find_bone(E); + if (source_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = source_skeleton->get_bone_parent(source_bone_id); + if (bone_parent >= 0) { + parent_global_rest = source_skeleton->get_bone_global_rest(bone_parent); + } + rbi.post_basis = source_skeleton->get_bone_rest(source_bone_id).basis.inverse() * parent_global_rest.basis.inverse(); + } + int target_bone_id = p_skeleton->find_bone(E); + rbi.bone_id = target_bone_id; + if (target_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = p_skeleton->get_bone_parent(target_bone_id); + if (bone_parent >= 0) { + parent_global_rest = p_skeleton->get_bone_global_rest(bone_parent); + } + rbi.post_basis = rbi.post_basis * parent_global_rest.basis * p_skeleton->get_bone_rest(target_bone_id).basis; + } + bone_rests.push_back(rbi); + } + return bone_rests; +} + +Vector RetargetModifier3D::cache_bone_rests(Skeleton3D *p_skeleton) { + // Retarget pose in model space: + // tgt_pose.basis = tgt_parent_global_rest.basis.inv * src_parent_global_rest.basis * src_pose.basis * src_rest.basis.inv * src_parent_global_rest.basis.inv * tgt_parent_global_rest.basis * tgt_rest.basis + // tgt_pose.origin = tgt_parent_global_rest.basis.inv.xform(src_parent_global_rest.basis.xform(src_pose.origin - src_rest.origin)) + tgt_rest.origin + Skeleton3D *source_skeleton = get_skeleton(); + Vector bone_rests; + if (profile.is_null() || !source_skeleton) { + return bone_rests; + } + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + RetargetBoneInfo rbi; + int source_bone_id = source_skeleton->find_bone(E); + if (source_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = source_skeleton->get_bone_parent(source_bone_id); + if (bone_parent >= 0) { + parent_global_rest = source_skeleton->get_bone_global_rest(bone_parent); + } + rbi.pre_basis = parent_global_rest.basis; + rbi.post_basis = source_skeleton->get_bone_rest(source_bone_id).basis.inverse() * parent_global_rest.basis.inverse(); + } + + int target_bone_id = p_skeleton->find_bone(E); + rbi.bone_id = target_bone_id; + if (target_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = p_skeleton->get_bone_parent(target_bone_id); + if (bone_parent >= 0) { + parent_global_rest = p_skeleton->get_bone_global_rest(bone_parent); + } + rbi.pre_basis = parent_global_rest.basis.inverse() * rbi.pre_basis; + rbi.post_basis = rbi.post_basis * parent_global_rest.basis * p_skeleton->get_bone_rest(target_bone_id).basis; + } + bone_rests.push_back(rbi); + } + return bone_rests; +} + +void RetargetModifier3D::_update_child_skeleton_rests(int p_child_skeleton_idx) { + ERR_FAIL_INDEX(p_child_skeleton_idx, child_skeletons.size()); + Skeleton3D *c = Object::cast_to(ObjectDB::get_instance(child_skeletons[p_child_skeleton_idx].skeleton_id)); + if (!c) { + return; + } + if (use_global_pose) { + child_skeletons.write[p_child_skeleton_idx].humanoid_bone_rests = cache_bone_global_rests(c); + } else { + child_skeletons.write[p_child_skeleton_idx].humanoid_bone_rests = cache_bone_rests(c); + } +} + +void RetargetModifier3D::_update_child_skeletons() { + _reset_child_skeletons(); + + for (int i = 0; i < get_child_count(); i++) { + RetargetInfo ri; + Skeleton3D *c = Object::cast_to(get_child(i)); + if (c) { + int id = child_skeletons.size(); + ri.skeleton_id = c->get_instance_id(); + child_skeletons.push_back(ri); + c->connect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests).bind(id)); + } + } + + cache_rests(); + update_configuration_warnings(); +} + +void RetargetModifier3D::_reset_child_skeleton_poses() { + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *c = Object::cast_to(ObjectDB::get_instance(E.skeleton_id)); + if (!c) { + continue; + } + if (c->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests))) { + c->disconnect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests)); + } + for (const RetargetBoneInfo &F : E.humanoid_bone_rests) { + if (F.bone_id < 0) { + continue; + } + c->reset_bone_pose(F.bone_id); + } + } +} + +void RetargetModifier3D::_reset_child_skeletons() { + _reset_child_skeleton_poses(); + child_skeletons.clear(); +} + +/// General functions + +void RetargetModifier3D::add_child_notify(Node *p_child) { + if (Object::cast_to(p_child)) { + _update_child_skeletons(); + } +} + +void RetargetModifier3D::move_child_notify(Node *p_child) { + if (Object::cast_to(p_child)) { + _update_child_skeletons(); + } +} + +void RetargetModifier3D::remove_child_notify(Node *p_child) { + if (Object::cast_to(p_child)) { + // Reset after process. + callable_mp(this, &RetargetModifier3D::_update_child_skeletons).call_deferred(); + } +} + +void RetargetModifier3D::_validate_property(PropertyInfo &p_property) const { + if (use_global_pose) { + if (p_property.name == "position_enabled" || p_property.name == "rotation_enabled" || p_property.name == "scale_enabled") { + p_property.usage = PROPERTY_USAGE_NONE; + } + } +} + +void RetargetModifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_profile", "profile"), &RetargetModifier3D::set_profile); + ClassDB::bind_method(D_METHOD("get_profile"), &RetargetModifier3D::get_profile); + ClassDB::bind_method(D_METHOD("set_use_global_pose", "use_global_pose"), &RetargetModifier3D::set_use_global_pose); + ClassDB::bind_method(D_METHOD("is_using_global_pose"), &RetargetModifier3D::is_using_global_pose); + ClassDB::bind_method(D_METHOD("set_position_enabled", "enabled"), &RetargetModifier3D::set_position_enabled); + ClassDB::bind_method(D_METHOD("is_position_enabled"), &RetargetModifier3D::is_position_enabled); + ClassDB::bind_method(D_METHOD("set_rotation_enabled", "enabled"), &RetargetModifier3D::set_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_rotation_enabled"), &RetargetModifier3D::is_rotation_enabled); + ClassDB::bind_method(D_METHOD("set_scale_enabled", "enabled"), &RetargetModifier3D::set_scale_enabled); + ClassDB::bind_method(D_METHOD("is_scale_enabled"), &RetargetModifier3D::is_scale_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_pose"), "set_use_global_pose", "is_using_global_pose"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "position_enabled"), "set_position_enabled", "is_position_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotation_enabled"), "set_rotation_enabled", "is_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scale_enabled"), "set_scale_enabled", "is_scale_enabled"); +} + +void RetargetModifier3D::_set_active(bool p_active) { + if (!p_active) { + _reset_child_skeleton_poses(); + } +} + +void RetargetModifier3D::_retarget_global_pose() { + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + LocalVector source_poses; + if (influence < 1.0) { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_global_rest(source_bone_id).interpolate_with(source_skeleton->get_bone_global_pose(source_bone_id), influence)); + } + } else { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_global_pose(source_bone_id)); + } + } + + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *target_skeleton = Object::cast_to(ObjectDB::get_instance(E.skeleton_id)); + if (!target_skeleton) { + continue; + } + for (int i = 0; i < source_bone_ids.size(); i++) { + int target_bone_id = E.humanoid_bone_rests[i].bone_id; + if (target_bone_id < 0) { + continue; + } + Transform3D retarget_pose = source_poses[i]; + retarget_pose.basis = retarget_pose.basis * E.humanoid_bone_rests[i].post_basis; + target_skeleton->set_bone_global_pose(target_bone_id, retarget_pose); + } + } +} + +void RetargetModifier3D::_retarget_pose() { + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + LocalVector source_poses; + if (influence < 1.0) { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_rest(source_bone_id).interpolate_with(source_skeleton->get_bone_pose(source_bone_id), influence)); + } + } else { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_pose(source_bone_id)); + } + } + + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *target_skeleton = Object::cast_to(ObjectDB::get_instance(E.skeleton_id)); + if (!target_skeleton) { + continue; + } + float motion_scale_ratio = target_skeleton->get_motion_scale() / source_skeleton->get_motion_scale(); + for (int i = 0; i < source_bone_ids.size(); i++) { + int target_bone_id = E.humanoid_bone_rests[i].bone_id; + if (target_bone_id < 0) { + continue; + } + int source_bone_id = source_bone_ids[i]; + if (source_bone_id < 0) { + continue; + } + + Transform3D extracted_transform = source_poses[i]; + extracted_transform.basis = E.humanoid_bone_rests[i].pre_basis * extracted_transform.basis * E.humanoid_bone_rests[i].post_basis; + extracted_transform.origin = E.humanoid_bone_rests[i].pre_basis.xform((extracted_transform.origin - source_skeleton->get_bone_rest(source_bone_id).origin) * motion_scale_ratio) + target_skeleton->get_bone_rest(target_bone_id).origin; + + Transform3D retarget_pose = target_skeleton->get_bone_pose(target_bone_id); + if (enable_position) { + retarget_pose.origin = extracted_transform.origin; + } + if (enable_rotation) { + retarget_pose.basis = extracted_transform.basis.get_rotation_quaternion(); + } + if (enable_scale) { + retarget_pose.basis.scale_local(extracted_transform.basis.get_scale()); + } + target_skeleton->set_bone_pose(target_bone_id, retarget_pose); + } + } +} + +void RetargetModifier3D::_process_modification() { + if (use_global_pose) { + _retarget_global_pose(); + } else { + _retarget_pose(); + } +} + +void RetargetModifier3D::set_profile(Ref p_profile) { + if (profile == p_profile) { + return; + } + _profile_changed(profile, p_profile); +} + +Ref RetargetModifier3D::get_profile() const { + return profile; +} + +void RetargetModifier3D::set_use_global_pose(bool p_use_global_pose) { + if (use_global_pose == p_use_global_pose) { + return; + } + + use_global_pose = p_use_global_pose; + cache_rests_with_reset(); + + notify_property_list_changed(); +} + +bool RetargetModifier3D::is_using_global_pose() const { + return use_global_pose; +} + +void RetargetModifier3D::set_position_enabled(bool p_enabled) { + if (enable_position != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_position = p_enabled; +} + +bool RetargetModifier3D::is_position_enabled() const { + return enable_position; +} + +void RetargetModifier3D::set_rotation_enabled(bool p_enabled) { + if (enable_rotation != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_rotation = p_enabled; +} + +bool RetargetModifier3D::is_rotation_enabled() const { + return enable_rotation; +} + +void RetargetModifier3D::set_scale_enabled(bool p_enabled) { + if (enable_scale != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_scale = p_enabled; +} + +bool RetargetModifier3D::is_scale_enabled() const { + return enable_scale; +} + +void RetargetModifier3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _update_child_skeletons(); + } break; + case NOTIFICATION_EDITOR_PRE_SAVE: { + _reset_child_skeleton_poses(); + } break; + case NOTIFICATION_EXIT_TREE: { + _reset_child_skeletons(); + } break; + } +} + +RetargetModifier3D::RetargetModifier3D() { +} + +RetargetModifier3D::~RetargetModifier3D() { +} diff --git a/scene/3d/retarget_modifier_3d.h b/scene/3d/retarget_modifier_3d.h new file mode 100644 index 00000000000..b6b5735bc2d --- /dev/null +++ b/scene/3d/retarget_modifier_3d.h @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* retarget_modifier_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* 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 RETARGET_MODIFIER_3D_H +#define RETARGET_MODIFIER_3D_H + +#include "scene/3d/skeleton_modifier_3d.h" +#include "scene/resources/skeleton_profile.h" + +class RetargetModifier3D : public SkeletonModifier3D { + GDCLASS(RetargetModifier3D, SkeletonModifier3D); + + Ref profile; + + bool use_global_pose = false; + bool enable_position = true; + bool enable_rotation = true; + bool enable_scale = true; + + struct RetargetBoneInfo { + int bone_id = -1; + Basis pre_basis; + Basis post_basis; + }; + + struct RetargetInfo { + ObjectID skeleton_id; + Vector humanoid_bone_rests; + }; + + Vector child_skeletons; + Vector source_bone_ids; + + void _update_child_skeleton_rests(int p_child_skeleton_idx); + void _update_child_skeletons(); + void _reset_child_skeleton_poses(); + void _reset_child_skeletons(); + + void cache_rests_with_reset(); + void cache_rests(); + Vector cache_bone_global_rests(Skeleton3D *p_skeleton); + Vector cache_bone_rests(Skeleton3D *p_skeleton); + Vector get_humanoid_bone_rests(Skeleton3D *p_skeleton); + + void _retarget_global_pose(); + void _retarget_pose(); + +protected: + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override; + void _profile_changed(Ref p_old, Ref p_new); + + void _validate_property(PropertyInfo &p_property) const; + + static void _bind_methods(); + virtual void _notification(int p_what); + + virtual void add_child_notify(Node *p_child) override; + virtual void move_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; + + virtual void _set_active(bool p_active) override; + virtual void _process_modification() override; + +public: + virtual PackedStringArray get_configuration_warnings() const override; + + void set_use_global_pose(bool p_use_global_pose); + bool is_using_global_pose() const; + void set_position_enabled(bool p_enabled); + bool is_position_enabled() const; + void set_rotation_enabled(bool p_enabled); + bool is_rotation_enabled() const; + void set_scale_enabled(bool p_enabled); + bool is_scale_enabled() const; + + void set_profile(Ref p_profile); + Ref get_profile() const; + + RetargetModifier3D(); + virtual ~RetargetModifier3D(); +}; + +#endif // RETARGET_MODIFIER_3D_H diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 89212503caa..273b68d2773 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -1050,7 +1050,12 @@ void Skeleton3D::force_update_all_bone_transforms() { for (int i = 0; i < parentless_bones.size(); i++) { force_update_bone_children_transforms(parentless_bones[i]); } - rest_dirty = false; + if (rest_dirty) { + rest_dirty = false; + emit_signal(SNAME("rest_updated")); + } else { + rest_dirty = false; + } dirty = false; if (updating) { return; @@ -1260,6 +1265,7 @@ void Skeleton3D::_bind_methods() { ADD_GROUP("Modifier", "modifier_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process"); + ADD_SIGNAL(MethodInfo("rest_updated")); ADD_SIGNAL(MethodInfo("pose_updated")); ADD_SIGNAL(MethodInfo("skeleton_updated")); ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 2abf989560f..7df102a301c 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -224,6 +224,7 @@ class Skeleton3D : public Node3D { // Skeleton creation API uint64_t get_version() const; int add_bone(const String &p_name); + void remove_bone(int p_bone); int find_bone(const String &p_name) const; String get_bone_name(int p_bone) const; void set_bone_name(int p_bone, const String &p_name); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 0b720d67107..81fea2d0ece 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -166,8 +166,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; @@ -391,7 +391,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) { @@ -497,8 +497,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(); @@ -662,8 +662,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); @@ -727,7 +727,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() { @@ -737,18 +737,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 { @@ -779,7 +773,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/scene/animation/animation_player.h b/scene/animation/animation_player.h index dd8ffd3286f..c20978f13b1 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -72,6 +72,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/scene/gui/color_mode.h b/scene/gui/color_mode.h index 10c1d7ab48b..684e3ee9d5d 100644 --- a/scene/gui/color_mode.h +++ b/scene/gui/color_mode.h @@ -35,8 +35,6 @@ #include "scene/gui/color_picker.h" -struct Color; - class ColorMode { public: ColorPicker *color_picker = nullptr; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index e6edb5e0bfd..96234e5db8d 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -32,21 +32,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: { @@ -161,10 +165,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/scene/gui/color_picker.h b/scene/gui/color_picker.h index 95aa94603b7..ec7b22a64f7 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -33,28 +33,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); @@ -112,11 +108,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/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 819d467da70..8d4e06c7c12 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -552,7 +552,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/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2ceba9276f0..35c33760254 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -95,7 +95,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) { @@ -153,7 +153,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(); @@ -250,8 +250,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; @@ -279,7 +277,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; @@ -294,11 +296,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/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d885b07d14b..d0b749302f9 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1607,7 +1607,9 @@ void TextEdit::_notification(int p_what) { draw_caret = true; } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -2010,7 +2012,9 @@ void TextEdit::gui_input(const Ref &p_gui_input) { } } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f66d39dbade..1d5afab526c 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1261,6 +1261,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; @@ -4643,6 +4647,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/scene/main/viewport.h b/scene/main/viewport.h index 31fbefe8419..644e55e9660 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -518,6 +518,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/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8767a593560..ae272d9a9f7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -279,6 +279,7 @@ #include "scene/3d/physics/vehicle_body_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/remote_transform_3d.h" +#include "scene/3d/retarget_modifier_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_ik_3d.h" #include "scene/3d/skeleton_modifier_3d.h" @@ -596,6 +597,7 @@ void register_scene_types() { GDREGISTER_CLASS(Marker3D); GDREGISTER_CLASS(RootMotionView); GDREGISTER_VIRTUAL_CLASS(SkeletonModifier3D); + GDREGISTER_CLASS(RetargetModifier3D); OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/3d/box_shape_3d.cpp b/scene/resources/3d/box_shape_3d.cpp index ef604f67b90..e4c02894b26 100644 --- a/scene/resources/3d/box_shape_3d.cpp +++ b/scene/resources/3d/box_shape_3d.cpp @@ -31,6 +31,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 { @@ -49,6 +51,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/scene/resources/3d/box_shape_3d.h b/scene/resources/3d/box_shape_3d.h index 96064b61a03..1649a1c38d6 100644 --- a/scene/resources/3d/box_shape_3d.h +++ b/scene/resources/3d/box_shape_3d.h @@ -53,6 +53,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/scene/resources/3d/capsule_shape_3d.cpp b/scene/resources/3d/capsule_shape_3d.cpp index 6ad16121e79..4a37e88bd04 100644 --- a/scene/resources/3d/capsule_shape_3d.cpp +++ b/scene/resources/3d/capsule_shape_3d.cpp @@ -32,6 +32,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 { @@ -69,6 +70,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/scene/resources/3d/capsule_shape_3d.h b/scene/resources/3d/capsule_shape_3d.h index 3ed834b01b9..0b29292a1bf 100644 --- a/scene/resources/3d/capsule_shape_3d.h +++ b/scene/resources/3d/capsule_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CapsuleShape3D : public Shape3D { GDCLASS(CapsuleShape3D, Shape3D); float radius = 0.5; @@ -52,6 +54,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/scene/resources/3d/concave_polygon_shape_3d.cpp b/scene/resources/3d/concave_polygon_shape_3d.cpp index 2a553390e19..45cb99f698b 100644 --- a/scene/resources/3d/concave_polygon_shape_3d.cpp +++ b/scene/resources/3d/concave_polygon_shape_3d.cpp @@ -32,6 +32,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 { @@ -61,6 +62,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/scene/resources/3d/concave_polygon_shape_3d.h b/scene/resources/3d/concave_polygon_shape_3d.h index 2bb69be43c5..56654dc4c0f 100644 --- a/scene/resources/3d/concave_polygon_shape_3d.h +++ b/scene/resources/3d/concave_polygon_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConcavePolygonShape3D : public Shape3D { GDCLASS(ConcavePolygonShape3D, Shape3D); @@ -74,6 +76,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/scene/resources/3d/convex_polygon_shape_3d.cpp b/scene/resources/3d/convex_polygon_shape_3d.cpp index 3e6db206ffb..a9fb8dd015b 100644 --- a/scene/resources/3d/convex_polygon_shape_3d.cpp +++ b/scene/resources/3d/convex_polygon_shape_3d.cpp @@ -32,6 +32,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 { @@ -55,6 +56,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/scene/resources/3d/convex_polygon_shape_3d.h b/scene/resources/3d/convex_polygon_shape_3d.h index 708f8652a56..15dd3b860b0 100644 --- a/scene/resources/3d/convex_polygon_shape_3d.h +++ b/scene/resources/3d/convex_polygon_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConvexPolygonShape3D : public Shape3D { GDCLASS(ConvexPolygonShape3D, Shape3D); Vector points; @@ -49,6 +51,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/scene/resources/3d/cylinder_shape_3d.cpp b/scene/resources/3d/cylinder_shape_3d.cpp index 164ab49c77d..0f8747e3269 100644 --- a/scene/resources/3d/cylinder_shape_3d.cpp +++ b/scene/resources/3d/cylinder_shape_3d.cpp @@ -32,6 +32,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 { @@ -62,6 +63,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/scene/resources/3d/cylinder_shape_3d.h b/scene/resources/3d/cylinder_shape_3d.h index d4cf04a61ba..08f31db4969 100644 --- a/scene/resources/3d/cylinder_shape_3d.h +++ b/scene/resources/3d/cylinder_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CylinderShape3D : public Shape3D { GDCLASS(CylinderShape3D, Shape3D); float radius = 0.5; @@ -51,6 +53,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/scene/resources/3d/height_map_shape_3d.cpp b/scene/resources/3d/height_map_shape_3d.cpp index 7854f416907..205f54bbafd 100644 --- a/scene/resources/3d/height_map_shape_3d.cpp +++ b/scene/resources/3d/height_map_shape_3d.cpp @@ -33,6 +33,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 { @@ -84,6 +85,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/scene/resources/3d/height_map_shape_3d.h b/scene/resources/3d/height_map_shape_3d.h index 8ac9cd29bc0..71933eac9ee 100644 --- a/scene/resources/3d/height_map_shape_3d.h +++ b/scene/resources/3d/height_map_shape_3d.h @@ -35,6 +35,7 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; class Image; class HeightMapShape3D : public Shape3D { @@ -64,6 +65,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/scene/resources/3d/mesh_library.cpp b/scene/resources/3d/mesh_library.cpp index f0823bedc60..0b490e6d555 100644 --- a/scene/resources/3d/mesh_library.cpp +++ b/scene/resources/3d/mesh_library.cpp @@ -49,6 +49,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; @@ -93,6 +111,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") { @@ -122,6 +142,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")); @@ -156,6 +177,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; @@ -202,6 +229,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; @@ -330,6 +362,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); @@ -338,6 +371,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/scene/resources/3d/mesh_library.h b/scene/resources/3d/mesh_library.h index e52fbb281f3..726ed98a7a8 100644 --- a/scene/resources/3d/mesh_library.h +++ b/scene/resources/3d/mesh_library.h @@ -37,6 +37,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 { @@ -52,6 +53,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; @@ -77,6 +79,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); @@ -85,6 +88,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/scene/resources/3d/separation_ray_shape_3d.cpp b/scene/resources/3d/separation_ray_shape_3d.cpp index d9aa636289e..1f97d06b67e 100644 --- a/scene/resources/3d/separation_ray_shape_3d.cpp +++ b/scene/resources/3d/separation_ray_shape_3d.cpp @@ -32,6 +32,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 { @@ -43,6 +44,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/scene/resources/3d/separation_ray_shape_3d.h b/scene/resources/3d/separation_ray_shape_3d.h index 5c7681fcbed..1aad61bdc45 100644 --- a/scene/resources/3d/separation_ray_shape_3d.h +++ b/scene/resources/3d/separation_ray_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SeparationRayShape3D : public Shape3D { GDCLASS(SeparationRayShape3D, Shape3D); float length = 1.0; @@ -52,6 +54,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/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index 04f1653d2e5..78a9d668275 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -68,6 +68,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; @@ -81,29 +109,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/scene/resources/3d/shape_3d.h b/scene/resources/3d/shape_3d.h index 2adfd152f97..7b005c1fb91 100644 --- a/scene/resources/3d/shape_3d.h +++ b/scene/resources/3d/shape_3d.h @@ -36,6 +36,7 @@ #include "core/io/resource.h" class ArrayMesh; +class Material; class Shape3D : public Resource { GDCLASS(Shape3D, Resource); @@ -46,6 +47,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(); @@ -53,6 +58,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: @@ -60,6 +67,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; @@ -71,6 +79,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/scene/resources/3d/sphere_shape_3d.cpp b/scene/resources/3d/sphere_shape_3d.cpp index 59ed80fd3b7..15806dd66b7 100644 --- a/scene/resources/3d/sphere_shape_3d.cpp +++ b/scene/resources/3d/sphere_shape_3d.cpp @@ -32,6 +32,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 { @@ -56,6 +58,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/scene/resources/3d/sphere_shape_3d.h b/scene/resources/3d/sphere_shape_3d.h index 068b7f9acef..14b29f74cf2 100644 --- a/scene/resources/3d/sphere_shape_3d.h +++ b/scene/resources/3d/sphere_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SphereShape3D : public Shape3D { GDCLASS(SphereShape3D, Shape3D); float radius = 0.5f; @@ -49,6 +51,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/scene/resources/3d/world_boundary_shape_3d.cpp b/scene/resources/3d/world_boundary_shape_3d.cpp index 7cff587a206..1957b2c99e5 100644 --- a/scene/resources/3d/world_boundary_shape_3d.cpp +++ b/scene/resources/3d/world_boundary_shape_3d.cpp @@ -32,6 +32,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 { @@ -63,6 +64,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/scene/resources/3d/world_boundary_shape_3d.h b/scene/resources/3d/world_boundary_shape_3d.h index cd7bc72a5e2..ea70ba4dc40 100644 --- a/scene/resources/3d/world_boundary_shape_3d.h +++ b/scene/resources/3d/world_boundary_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class WorldBoundaryShape3D : public Shape3D { GDCLASS(WorldBoundaryShape3D, Shape3D); Plane plane; @@ -48,6 +50,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/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index fd9df49b4f6..0fba9db429a 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -127,6 +127,10 @@ void AnimationLibrary::get_animation_list(List *p_animations) const } } +int AnimationLibrary::get_animation_list_size() const { + return animations.size(); +} + void AnimationLibrary::_set_data(const Dictionary &p_data) { for (KeyValue> &K : animations) { K.value->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); @@ -168,6 +172,7 @@ void AnimationLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation); ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation); ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list); + ClassDB::bind_method(D_METHOD("get_animation_list_size"), &AnimationLibrary::get_animation_list_size); ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data); diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index bafc355ce49..032f25167da 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -63,6 +63,7 @@ class AnimationLibrary : public Resource { bool has_animation(const StringName &p_name) const; Ref get_animation(const StringName &p_name) const; void get_animation_list(List *p_animations) const; + int get_animation_list_size() const; #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List *r_options) const override; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b7f5c4c3389..2b15a123b9b 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -2739,13 +2739,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/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp index 369d72f8013..bc1c55c614c 100644 --- a/scene/resources/skeleton_profile.cpp +++ b/scene/resources/skeleton_profile.cpp @@ -271,6 +271,14 @@ int SkeletonProfile::find_bone(const StringName &p_bone_name) const { return -1; } +PackedStringArray SkeletonProfile::get_bone_names() { + PackedStringArray s; + for (const SkeletonProfileBone &bone : bones) { + s.push_back(bone.bone_name); + } + return s; +} + StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); return bones[p_bone_idx].bone_name; diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h index 6db0afaac51..8de514c7c12 100644 --- a/scene/resources/skeleton_profile.h +++ b/scene/resources/skeleton_profile.h @@ -99,6 +99,7 @@ class SkeletonProfile : public Resource { int find_bone(const StringName &p_bone_name) const; + PackedStringArray get_bone_names(); StringName get_bone_name(int p_bone_idx) const; void set_bone_name(int p_bone_idx, const StringName &p_bone_name); diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 3e43817a077..2b9e745de1e 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -454,7 +454,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/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index cc0dc0c251d..f8b83f2dd3e 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -358,7 +358,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); @@ -390,7 +390,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); @@ -414,7 +414,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); @@ -432,7 +432,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); @@ -465,7 +465,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); @@ -483,7 +483,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/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index f20591c2247..8331d9b8737 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -593,7 +593,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()) { @@ -660,7 +660,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)); @@ -733,7 +733,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); @@ -835,7 +835,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) { @@ -855,7 +855,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); @@ -925,7 +925,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)); @@ -990,7 +990,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)); @@ -1027,7 +1027,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()); @@ -1092,7 +1092,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); @@ -1171,7 +1171,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); @@ -1249,7 +1249,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); @@ -1269,7 +1269,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/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index d2b481b626d..cb7a2ca2638 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -284,7 +284,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); @@ -328,7 +328,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); @@ -353,7 +353,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/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp index 36f2c6ec92d..3a361d8f81b 100644 --- a/servers/rendering/renderer_rd/effects/fsr2.cpp +++ b/servers/rendering/renderer_rd/effects/fsr2.cpp @@ -237,6 +237,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/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp index 93b2c9a9b0d..36aae002457 100644 --- a/servers/rendering/renderer_rd/effects/luminance.cpp +++ b/servers/rendering/renderer_rd/effects/luminance.cpp @@ -186,7 +186,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/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 5fab713e772..aa2bebea1b2 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -168,7 +168,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/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 8d51bade889..8778fbcb29d 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -96,7 +96,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/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index cc8501e0e70..d0421e72f77 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -1723,7 +1723,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/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index 0ecc0f39f52..e4ab1d1659a 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1317,7 +1317,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(); } @@ -1338,7 +1338,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(); } @@ -1355,7 +1355,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(); } @@ -1479,7 +1479,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(); } @@ -1498,7 +1498,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/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 0eb99feaac6..ab386375b2a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -645,11 +645,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(); } @@ -1478,7 +1478,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(); } } @@ -2012,7 +2012,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); @@ -2024,7 +2024,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(); @@ -2086,7 +2086,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. @@ -2103,7 +2103,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(); @@ -2111,7 +2111,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(); } @@ -2123,7 +2123,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(); } @@ -2150,7 +2150,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); @@ -2173,7 +2173,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); @@ -2238,7 +2238,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(); } @@ -2302,7 +2302,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(); @@ -2701,7 +2701,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); @@ -2725,7 +2725,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(); @@ -2772,7 +2772,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(); } @@ -2826,7 +2826,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(); } @@ -2876,7 +2876,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] = { @@ -2985,7 +2985,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/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 669531dd282..b57866d35f7 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -352,7 +352,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; @@ -360,8 +359,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; @@ -387,7 +387,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/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 18a673e15ae..167a6b2fb61 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -691,7 +691,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(); } } @@ -1053,7 +1053,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. @@ -1070,7 +1070,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) { @@ -1167,7 +1167,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(); @@ -1447,7 +1447,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); @@ -1471,7 +1471,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(); @@ -1523,7 +1523,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(); } @@ -1569,7 +1569,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] = { @@ -1644,7 +1644,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(); } @@ -2096,11 +2096,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/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 77389fb4b80..a78eec66721 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -259,8 +259,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; @@ -334,7 +334,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/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 8dcc6b1e1e0..6804abb8a57 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -977,6 +977,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); @@ -1028,7 +1029,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++) { @@ -1099,7 +1100,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); @@ -1169,7 +1170,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; @@ -2176,7 +2177,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_ALL : 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/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 0c7fced8033..36b43d94e27 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -648,7 +648,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/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl b/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl index fb35d3cde69..553637dc4fe 100644 --- a/servers/rendering/renderer_rd/shaders/effects/subsurface_scattering.glsl +++ b/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/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc index ad8e72c5fb3..709a7d850c9 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.compat.inc @@ -56,6 +56,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); @@ -63,6 +67,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/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 19636463e20..d8ea30aa0d0 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -50,7 +50,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); @@ -181,8 +181,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) @@ -244,7 +244,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; @@ -264,6 +264,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/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index 842de4c9487..5b763f842c2 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -197,7 +197,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; @@ -425,6 +425,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/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 3db3173518c..2d6a37fb326 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -2896,7 +2896,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); @@ -3577,7 +3577,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; } @@ -3694,7 +3694,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/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc index a234938b674..1b14f52f435 100644 --- a/servers/rendering/rendering_device.compat.inc +++ b/servers/rendering/rendering_device.compat.inc @@ -88,11 +88,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) { @@ -148,6 +166,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/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index cd0c23564c4..96f72b53d00 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -848,6 +848,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; @@ -951,6 +952,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; @@ -1131,6 +1133,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; @@ -1891,6 +1894,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; } @@ -2031,6 +2035,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); @@ -2084,31 +2114,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 @@ -2118,7 +2124,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"); @@ -2159,17 +2165,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++) { @@ -2351,6 +2368,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_ @@ -2366,14 +2384,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; @@ -2440,15 +2465,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; } @@ -2489,6 +2523,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; @@ -2497,6 +2533,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"); @@ -2518,6 +2555,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; } @@ -2533,11 +2575,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())); @@ -2549,6 +2599,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; } @@ -3835,7 +3887,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); @@ -3843,150 +3895,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."); @@ -4010,43 +3919,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); @@ -5445,6 +5399,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()) { @@ -5855,13 +5810,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(); } @@ -6213,7 +6162,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++) { @@ -6711,6 +6660,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); @@ -6772,7 +6724,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 @@ -7296,22 +7248,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 @@ -7393,6 +7343,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/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 6e81291cb1c..8f117f320e1 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -253,6 +253,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; @@ -289,6 +290,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; } }; @@ -351,33 +353,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 ****/ @@ -525,7 +502,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. @@ -540,47 +518,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; }; @@ -828,6 +772,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)); @@ -856,7 +820,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: @@ -1160,8 +1126,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 @@ -1169,16 +1133,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); @@ -1571,15 +1562,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/servers/rendering/rendering_device_binds.h b/servers/rendering/rendering_device_binds.h index c553dad2de8..6d512b0cfdc 100644 --- a/servers/rendering/rendering_device_binds.h +++ b/servers/rendering/rendering_device_binds.h @@ -71,6 +71,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); } @@ -86,6 +88,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/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index 6463c8c5b8b..dd486e8197c 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -375,6 +375,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) { @@ -397,6 +398,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/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp index 4a16f8335a0..eb51814a6c7 100644 --- a/servers/rendering/rendering_device_graph.cpp +++ b/servers/rendering/rendering_device_graph.cpp @@ -250,6 +250,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; @@ -504,6 +538,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); @@ -530,6 +566,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); } } @@ -700,6 +737,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) { @@ -807,6 +876,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()); @@ -827,7 +927,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) { @@ -885,9 +985,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); @@ -1340,9 +1451,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++) { @@ -1568,28 +1684,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) { @@ -1770,51 +1870,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); @@ -2184,20 +2295,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; @@ -2206,9 +2317,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/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h index 3cb43b39316..20ecdfda9f4 100644 --- a/servers/rendering/rendering_device_graph.h +++ b/servers/rendering/rendering_device_graph.h @@ -178,6 +178,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) { @@ -195,6 +196,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; @@ -209,6 +226,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; @@ -232,13 +258,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 { @@ -322,15 +351,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]); @@ -340,12 +372,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]); } }; @@ -618,6 +674,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; @@ -662,13 +719,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); @@ -682,7 +742,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); @@ -699,7 +759,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); @@ -731,7 +792,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/tests/core/io/test_udp_server.h b/tests/core/io/test_udp_server.h new file mode 100644 index 00000000000..e2f7607b71a --- /dev/null +++ b/tests/core/io/test_udp_server.h @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* test_udp_server.h */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* 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/tests/scene/test_camera_3d.h b/tests/scene/test_camera_3d.h index 549383b17f6..040bc1072bb 100644 --- a/tests/scene/test_camera_3d.h +++ b/tests/scene/test_camera_3d.h @@ -233,10 +233,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") { @@ -244,12 +247,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/tests/test_main.cpp b/tests/test_main.cpp index e86f17cef56..9e643905bd8 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -57,6 +57,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/thirdparty/README.md b/thirdparty/README.md index 7a95e3c7240..291ab1164d6 100644 --- a/thirdparty/README.md +++ b/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: diff --git a/thirdparty/clipper2/include/clipper2/clipper.core.h b/thirdparty/clipper2/include/clipper2/clipper.core.h index 0de7c3720e0..0f69bf2d9ff 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.core.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.engine.h b/thirdparty/clipper2/include/clipper2/clipper.engine.h index 13c7f069aa9..f6108832cd5 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.engine.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.export.h b/thirdparty/clipper2/include/clipper2/clipper.export.h index d7286132a49..53a445368e4 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.export.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.h b/thirdparty/clipper2/include/clipper2/clipper.h index 0f516b60e83..a2fe5c3cc28 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.minkowski.h b/thirdparty/clipper2/include/clipper2/clipper.minkowski.h index ebddd08a595..a3ddcf86f3a 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.minkowski.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.offset.h b/thirdparty/clipper2/include/clipper2/clipper.offset.h index 30992bfa55a..bb075a6d492 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.offset.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.rectclip.h b/thirdparty/clipper2/include/clipper2/clipper.rectclip.h index ff043f25f09..bfcfacf2e7c 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.rectclip.h +++ b/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/thirdparty/clipper2/include/clipper2/clipper.version.h b/thirdparty/clipper2/include/clipper2/clipper.version.h index d7644067e2a..61464095f6b 100644 --- a/thirdparty/clipper2/include/clipper2/clipper.version.h +++ b/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/thirdparty/clipper2/patches/clipper2-exceptions.patch b/thirdparty/clipper2/patches/clipper2-exceptions.patch index 0e1c6585fef..44c2b0287a1 100644 --- a/thirdparty/clipper2/patches/clipper2-exceptions.patch +++ b/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/thirdparty/clipper2/patches/gcc14-warning.patch b/thirdparty/clipper2/patches/gcc14-warning.patch deleted file mode 100644 index a4f06ef37ea..00000000000 --- a/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/thirdparty/clipper2/patches/llvm-error.patch b/thirdparty/clipper2/patches/llvm-error.patch new file mode 100644 index 00000000000..e326d73e831 --- /dev/null +++ b/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/thirdparty/clipper2/src/clipper.engine.cpp b/thirdparty/clipper2/src/clipper.engine.cpp index 9358b74b706..8f120267c3c 100644 --- a/thirdparty/clipper2/src/clipper.engine.cpp +++ b/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/thirdparty/clipper2/src/clipper.offset.cpp b/thirdparty/clipper2/src/clipper.offset.cpp index 0282aa49bb5..508a7f0831d 100644 --- a/thirdparty/clipper2/src/clipper.offset.cpp +++ b/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/thirdparty/clipper2/src/clipper.rectclip.cpp b/thirdparty/clipper2/src/clipper.rectclip.cpp index 9aa0fc0f764..23809b5ef6b 100644 --- a/thirdparty/clipper2/src/clipper.rectclip.cpp +++ b/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; }