WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 354 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
68 changed files with 2941 additions and 874 deletions
Showing only changes of commit e41ecb28db - Show all commits

View File

@ -123,12 +123,24 @@ if(CMAKE_COMPILER_IS_GNUCC)
The minimum supported version of GCC is 11.0.0, found ${CMAKE_C_COMPILER_VERSION}"
)
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
if(CMAKE_COMPILER_IS_GNUCC AND ("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "8.0"))
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "11.0.0")
message(FATAL_ERROR "\
The minimum supported version of CLANG is 8.0, found ${CMAKE_C_COMPILER_VERSION}"
The minimum supported version of GCC is 11.0.0, found ${CMAKE_CXX_COMPILER_VERSION}"
)
endif()
elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
if(CMAKE_COMPILER_IS_GNUCC)
if("${CMAKE_C_COMPILER_VERSION}" VERSION_LESS "8.0")
message(FATAL_ERROR "\
The minimum supported version of CLANG is 8.0, found ${CMAKE_C_COMPILER_VERSION}"
)
endif()
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "8.0")
message(FATAL_ERROR "\
The minimum supported version of CLANG is 8.0, found ${CMAKE_CXX_COMPILER_VERSION}"
)
endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(MSVC_VERSION VERSION_LESS "1928")
# MSVC_VERSION is an internal version number, it doesn't map to something
@ -2010,8 +2022,292 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
CXX_WARN_ERROR_UNGUARDED_AVAILABILITY_NEW -Werror=unguarded-availability-new
)
if(MSVC_CLANG)
# clang-cl produces an unhealthy ammount of warnings in its default
# configuration as it for reasons unknown decided to enable all
# warnings known to mankind. Resulting in a 5.5GB build log containing
# well over 11 million warnings. The code below disables every single
# one of them indiscriminately. Someone with time on their hands,
# could/should go over these and either fix them or describe why we
# would want to disable the warning. The list below contains both C
# and C++ warnings for all warnings since clang has seemingly no
# easy way to tell if something is a C or C++ specific warning and
# manually auditing every single one of them just isn't in the cards
# right now.
# /W3 is being removed, then added back again, this is because order
# matters for clang and these flags are being placed before the
# CMAKE_[LANGUAGE]_FLAGS which normally contain /W3, so we would
# disable certain warings here only for them to be re-enabled by /W3
# later on.
remove_cc_flag("/W3")
add_check_c_compiler_flags(
C_WARNINGS
C_WARN_CLANG_CL_W3 /W3
# The number behind each warn is the number of unique warning were
# generated on 2024-04-24 (d2be9cecc28a03ff1f799e8c63f1f9f8eda7cce3)
# especially the ones in the single and low double digits are likely
# genuine problems that can be investigated.
C_WARN_CLANG_CL_C++98_COMPAT -Wno-c++98-compat # 352692
C_WARN_CLANG_CL_OLD_STYLE_CAST -Wno-old-style-cast # 178608
C_WARN_CLANG_CL_UNSAFE_BUFFER_USAGE -Wno-unsafe-buffer-usage # 89032
C_WARN_CLANG_CL_MISSING_PROTOTYPES -Wno-missing-prototypes # 25587
C_WARN_CLANG_CL_SIGN_CONVERSION -Wno-sign-conversion # 20109
C_WARN_CLANG_CL_MISSING_FIELD_INITIALIZERS -Wno-missing-field-initializers # 20060
C_WARN_CLANG_CL_EXTRA_SEMI -Wno-extra-semi # 12513
C_WARN_CLANG_CL_LANGUAGE_EXTENSION_TOKEN -Wno-language-extension-token # 11032
C_WARN_CLANG_CL_IMPLICIT_FLOAT_CONVERSION -Wno-implicit-float-conversion # 11003
C_WARN_CLANG_CL_C++98_COMPAT_PEDANTIC -Wno-c++98-compat-pedantic # 10336
C_WARN_CLANG_CL_IMPLICIT_INT_FLOAT_CONVERSION -Wno-implicit-int-float-conversion # 7354
C_WARN_CLANG_CL_DOUBLE_PROMOTION -Wno-double-promotion # 7350
C_WARN_CLANG_CL_PRE_C++17_COMPAT -Wno-pre-c++17-compat # 7303
C_WARN_CLANG_CL_SHORTEN_64_TO_32 -Wno-shorten-64-to-32 # 7085
C_WARN_CLANG_CL_C++98_COMPAT_LOCAL_TYPE_TEMPLATE_ARGS -Wno-c++98-compat-local-type-template-args # 6906
C_WARN_CLANG_CL_RESERVED_IDENTIFIER -Wno-reserved-identifier # 5886
C_WARN_CLANG_CL_CAST_ALIGN -Wno-cast-align # 5513
C_WARN_CLANG_CL_DOCUMENTATION -Wno-documentation # 5107
C_WARN_CLANG_CL_DISABLED_MACRO_EXPANSION -Wno-disabled-macro-expansion # 4449
C_WARN_CLANG_CL_EXTRA_SEMI_STMT -Wno-extra-semi-stmt # 4349
C_WARN_CLANG_CL_ZERO_AS_NULL_POINTER_CONSTANT -Wno-zero-as-null-pointer-constant # 3209
C_WARN_CLANG_CL_FLOAT_CONVERSION -Wno-float-conversion # 2869
C_WARN_CLANG_CL_RESERVED_MACRO_IDENTIFIER -Wno-reserved-macro-identifier # 2862
C_WARN_CLANG_CL_CAST_FUNCTION_TYPE_STRICT -Wno-cast-function-type-strict # 2663
C_WARN_CLANG_CL_FLOAT_EQUAL -Wno-float-equal # 2153
C_WARN_CLANG_CL_IMPLICIT_INT_CONVERSION -Wno-implicit-int-conversion # 2117
C_WARN_CLANG_CL_SHADOW -Wno-shadow # 2068
C_WARN_CLANG_CL_SHADOW_FIELD_IN_CONSTRUCTOR -Wno-shadow-field-in-constructor # 1829
C_WARN_CLANG_CL_CAST_QUAL -Wno-cast-qual # 1742
C_WARN_CLANG_CL_PRE_C++14_COMPAT -Wno-pre-c++14-compat # 1569
C_WARN_CLANG_CL_GLOBAL_CONSTRUCTORS -Wno-global-constructors # 1402
C_WARN_CLANG_CL_SWITCH_ENUM -Wno-switch-enum # 973
C_WARN_CLANG_CL_EXIT_TIME_DESTRUCTORS -Wno-exit-time-destructors # 940
C_WARN_CLANG_CL_CTAD_MAYBE_UNSUPPORTED -Wno-ctad-maybe-unsupported # 891
C_WARN_CLANG_CL_UNDEFINED_FUNC_TEMPLATE -Wno-undefined-func-template # 863
C_WARN_CLANG_CL_C++98_COMPAT_EXTRA_SEMI -Wno-c++98-compat-extra-semi # 848
C_WARN_CLANG_CL_CAST_FUNCTION_TYPE -Wno-cast-function-type # 807
C_WARN_CLANG_CL_NULLABILITY_EXTENSION -Wno-nullability-extension # 602
C_WARN_CLANG_CL_SHADOW_FIELD -Wno-shadow-field # 585
C_WARN_CLANG_CL_CONDITIONAL_UNINITIALIZED -Wno-conditional-uninitialized # 555
C_WARN_CLANG_CL_UNUSED_PARAMETER -Wno-unused-parameter # 539
C_WARN_CLANG_CL_SUGGEST_DESTRUCTOR_OVERRIDE -Wno-suggest-destructor-override # 356
C_WARN_CLANG_CL_SHADOW_UNCAPTURED_LOCAL -Wno-shadow-uncaptured-local # 355
C_WARN_CLANG_CL_UNUSED_MACROS -Wno-unused-macros # 289
C_WARN_CLANG_CL_COVERED_SWITCH_DEFAULT -Wno-covered-switch-default # 233
C_WARN_CLANG_CL_SIGNED_ENUM_BITFIELD -Wno-signed-enum-bitfield # 229
C_WARN_CLANG_CL_DECLARATION_AFTER_STATEMENT -Wno-declaration-after-statement # 228
C_WARN_CLANG_CL_IMPLICIT_FALLTHROUGH -Wno-implicit-fallthrough # 164
C_WARN_CLANG_CL_NON_VIRTUAL_DTOR -Wno-non-virtual-dtor # 161
C_WARN_CLANG_CL_NESTED_ANON_TYPES -Wno-nested-anon-types # 140
C_WARN_CLANG_CL_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS -Wno-gnu-zero-variadic-macro-arguments # 132
C_WARN_CLANG_CL_UNREACHABLE_CODE_BREAK -Wno-unreachable-code-break # 115
C_WARN_CLANG_CL_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE -Wno-inconsistent-missing-destructor-override # 104
C_WARN_CLANG_CL_FORMAT_PEDANTIC -Wno-format-pedantic # 97
C_WARN_CLANG_CL_NONPORTABLE_SYSTEM_INCLUDE_PATH -Wno-nonportable-system-include-path # 95
C_WARN_CLANG_CL_UNDEF -Wno-undef # 94
C_WARN_CLANG_CL_IGNORED_QUALIFIERS -Wno-ignored-qualifiers # 93
C_WARN_CLANG_CL_USED_BUT_MARKED_UNUSED -Wno-used-but-marked-unused # 83
C_WARN_CLANG_CL_HEADER_HYGIENE -Wno-header-hygiene # 79
C_WARN_CLANG_CL_CHAR_SUBSCRIPTS -Wno-char-subscripts # 76
C_WARN_CLANG_CL_UNREACHABLE_CODE_RETURN -Wno-unreachable-code-return # 71
C_WARN_CLANG_CL_UNUSED_TEMPLATE -Wno-unused-template # 66
C_WARN_CLANG_CL_GNU_ANONYMOUS_STRUCT -Wno-gnu-anonymous-struct # 63
C_WARN_CLANG_CL_DEPRECATED_COPY_WITH_USER_PROVIDED_DTOR -Wno-deprecated-copy-with-user-provided-dtor # 62
C_WARN_CLANG_CL_INCONSISTENT_MISSING_OVERRIDE -Wno-inconsistent-missing-override # 54
C_WARN_CLANG_CL_UNREACHABLE_CODE -Wno-unreachable-code # 52
C_WARN_CLANG_CL_DEPRECATED_DYNAMIC_EXCEPTION_SPEC -Wno-deprecated-dynamic-exception-spec # 51
C_WARN_CLANG_CL_BAD_FUNCTION_CAST -Wno-bad-function-cast # 50
C_WARN_CLANG_CL_MICROSOFT_ENUM_VALUE -Wno-microsoft-enum-value # 47
C_WARN_CLANG_CL_DEPRECATED_COPY_WITH_USER_PROVIDED_COPY -Wno-deprecated-copy-with-user-provided-copy # 41
C_WARN_CLANG_CL_ZERO_LENGTH_ARRAY -Wno-zero-length-array # 39
C_WARN_CLANG_CL_UNUSED_FUNCTION -Wno-unused-function # 38
C_WARN_CLANG_CL_PEDANTIC -Wno-pedantic # 38
C_WARN_CLANG_CL_DEPRECATED_COPY_WITH_DTOR -Wno-deprecated-copy-with-dtor # 37
C_WARN_CLANG_CL_DOCUMENTATION_UNKNOWN_COMMAND -Wno-documentation-unknown-command # 34
C_WARN_CLANG_CL_UNDEFINED_REINTERPRET_CAST -Wno-undefined-reinterpret-cast # 33
C_WARN_CLANG_CL_FORMAT_NONLITERAL -Wno-format-nonliteral # 29
C_WARN_CLANG_CL_COMMA -Wno-comma # 27
C_WARN_CLANG_CL_DOCUMENTATION_DEPRECATED_SYNC -Wno-documentation-deprecated-sync # 26
C_WARN_CLANG_CL_SHIFT_SIGN_OVERFLOW -Wno-shift-sign-overflow # 24
C_WARN_CLANG_CL_PRE_C++17_COMPAT_PEDANTIC -Wno-pre-c++17-compat-pedantic # 24
C_WARN_CLANG_CL_C++98_COMPAT_UNNAMED_TYPE_TEMPLATE_ARGS -Wno-c++98-compat-unnamed-type-template-args # 22
C_WARN_CLANG_CL_SIGN_COMPARE -Wno-sign-compare # 21
C_WARN_CLANG_CL_FORMAT -Wno-format # 21
C_WARN_CLANG_CL_C++98_COMPAT_BIND_TO_TEMPORARY_COPY -Wno-c++98-compat-bind-to-temporary-copy # 21
C_WARN_CLANG_CL_ENUM_ENUM_CONVERSION -Wno-enum-enum-conversion # 20
C_WARN_CLANG_CL_ANON_ENUM_ENUM_CONVERSION -Wno-anon-enum-enum-conversion # 14
C_WARN_CLANG_CL_RANGE_LOOP_BIND_REFERENCE -Wno-range-loop-bind-reference # 14
C_WARN_CLANG_CL_ENUM_FLOAT_CONVERSION -Wno-enum-float-conversion # 12
C_WARN_CLANG_CL_KEYWORD_MACRO -Wno-keyword-macro # 10
C_WARN_CLANG_CL_DEPRECATED_COPY -Wno-deprecated-copy # 10
C_WARN_CLANG_CL_UNUSED_MEMBER_FUNCTION -Wno-unused-member-function # 9
C_WARN_CLANG_CL_MISSING_NORETURN -Wno-missing-noreturn # 8
C_WARN_CLANG_CL_MISSING_VARIABLE_DECLARATIONS -Wno-missing-variable-declarations # 8
C_WARN_CLANG_CL_DOCUMENTATION_HTML -Wno-documentation-html # 6
C_WARN_CLANG_CL_GNU_REDECLARED_ENUM -Wno-gnu-redeclared-enum # 6
C_WARN_CLANG_CL_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations # 6
C_WARN_CLANG_CL_OVERLOADED_VIRTUAL -Wno-overloaded-virtual # 5
C_WARN_CLANG_CL_C++98_C++11_COMPAT_BINARY_LITERAL -Wno-c++98-c++11-compat-binary-literal # 4
C_WARN_CLANG_CL_DEPRECATED_REDUNDANT_CONSTEXPR_STATIC_DEF -Wno-deprecated-redundant-constexpr-static-def # 4
C_WARN_CLANG_CL_MISSING_BRACES -Wno-missing-braces # 4
C_WARN_CLANG_CL_C99_EXTENSIONS -Wno-c99-extensions # 4
C_WARN_CLANG_CL_STRICT_PROTOTYPES -Wno-strict-prototypes # 4
C_WARN_CLANG_CL_UNREACHABLE_CODE_LOOP_INCREMENT -Wno-unreachable-code-loop-increment # 4
C_WARN_CLANG_CL_GNU_CASE_RANGE -Wno-gnu-case-range # 4
C_WARN_CLANG_CL_DUPLICATE_ENUM -Wno-duplicate-enum # 3
C_WARN_CLANG_CL_NULL_POINTER_SUBTRACTION -Wno-null-pointer-subtraction # 2
C_WARN_CLANG_CL_DEPRECATED_LITERAL_OPERATOR -Wno-deprecated-literal-operator # 2
C_WARN_CLANG_CL_NEWLINE_EOF -Wno-newline-eof # 2
C_WARN_CLANG_CL_MICROSOFT_CAST -Wno-microsoft-cast # 2
C_WARN_CLANG_CL_DATE_TIME -Wno-date-time # 2
C_WARN_CLANG_CL_DELETE_NON_ABSTRACT_NON_VIRTUAL_DTOR -Wno-delete-non-abstract-non-virtual-dtor # 2
C_WARN_CLANG_CL_UNUSED_PRIVATE_FIELD -Wno-unused-private-field # 2
C_WARN_CLANG_CL_FLEXIBLE_ARRAY_EXTENSIONS -Wno-flexible-array-extensions # 2
C_WARN_CLANG_CL_STRING_CONVERSION -Wno-string-conversion # 2
C_WARN_CLANG_CL_FINAL_DTOR_NON_FINAL_CLASS -Wno-final-dtor-non-final-class # 2
C_WARN_CLANG_CL_MICROSOFT_UNQUALIFIED_FRIEND -Wno-microsoft-unqualified-friend # 2
C_WARN_CLANG_CL_INVALID_NORETURN -Wno-invalid-noreturn # 1
C_WARN_CLANG_CL_INVALID_UTF8 -Wno-invalid-utf8 # 1
C_WARN_CLANG_CL_FOUR_CHAR_CONSTANTS -Wno-four-char-constants # 1
C_WARN_CLANG_CL_PARENTHESES -Wno-parentheses # 1
C_WARN_CLANG_CL_PESSIMIZING_MOVE -Wno-pessimizing-move # 1
C_WARN_CLANG_CL_DEPRECATED_NON_PROTOTYPE -Wno-deprecated-non-prototype # 1
C_WARN_CLANG_CL_BITFIELD_ENUM_CONVERSION -Wno-bitfield-enum-conversion # 1
C_WARN_CLANG_CL_UNUSED_LAMBDA_CAPTURE -Wno-unused-lambda-capture # 1
C_WARN_CLANG_CL_SHADOW_FIELD_IN_CONSTRUCTOR_MODIFIED -Wno-shadow-field-in-constructor-modified # 1
)
add_check_cxx_compiler_flags(
CXX_WARNINGS
CXX_WARN_CLANG_CL_W3 /W3
CXX_WARN_CLANG_CL_C++98_COMPAT -Wno-c++98-compat # 352692
CXX_WARN_CLANG_CL_OLD_STYLE_CAST -Wno-old-style-cast # 178608
CXX_WARN_CLANG_CL_UNSAFE_BUFFER_USAGE -Wno-unsafe-buffer-usage # 89032
CXX_WARN_CLANG_CL_MISSING_PROTOTYPES -Wno-missing-prototypes # 25587
CXX_WARN_CLANG_CL_SIGN_CONVERSION -Wno-sign-conversion # 20109
CXX_WARN_CLANG_CL_MISSING_FIELD_INITIALIZERS -Wno-missing-field-initializers # 20060
CXX_WARN_CLANG_CL_EXTRA_SEMI -Wno-extra-semi # 12513
CXX_WARN_CLANG_CL_LANGUAGE_EXTENSION_TOKEN -Wno-language-extension-token # 11032
CXX_WARN_CLANG_CL_IMPLICIT_FLOAT_CONVERSION -Wno-implicit-float-conversion # 11003
CXX_WARN_CLANG_CL_C++98_COMPAT_PEDANTIC -Wno-c++98-compat-pedantic # 10336
CXX_WARN_CLANG_CL_IMPLICIT_INT_FLOAT_CONVERSION -Wno-implicit-int-float-conversion # 7354
CXX_WARN_CLANG_CL_DOUBLE_PROMOTION -Wno-double-promotion # 7350
CXX_WARN_CLANG_CL_PRE_C++17_COMPAT -Wno-pre-c++17-compat # 7303
CXX_WARN_CLANG_CL_SHORTEN_64_TO_32 -Wno-shorten-64-to-32 # 7085
CXX_WARN_CLANG_CL_C++98_COMPAT_LOCAL_TYPE_TEMPLATE_ARGS -Wno-c++98-compat-local-type-template-args # 6906
CXX_WARN_CLANG_CL_RESERVED_IDENTIFIER -Wno-reserved-identifier # 5886
CXX_WARN_CLANG_CL_CAST_ALIGN -Wno-cast-align # 5513
CXX_WARN_CLANG_CL_DOCUMENTATION -Wno-documentation # 5107
CXX_WARN_CLANG_CL_DISABLED_MACRO_EXPANSION -Wno-disabled-macro-expansion # 4449
CXX_WARN_CLANG_CL_EXTRA_SEMI_STMT -Wno-extra-semi-stmt # 4349
CXX_WARN_CLANG_CL_ZERO_AS_NULL_POINTER_CONSTANT -Wno-zero-as-null-pointer-constant # 3209
CXX_WARN_CLANG_CL_FLOAT_CONVERSION -Wno-float-conversion # 2869
CXX_WARN_CLANG_CL_RESERVED_MACRO_IDENTIFIER -Wno-reserved-macro-identifier # 2862
CXX_WARN_CLANG_CL_CAST_FUNCTION_TYPE_STRICT -Wno-cast-function-type-strict # 2663
CXX_WARN_CLANG_CL_FLOAT_EQUAL -Wno-float-equal # 2153
CXX_WARN_CLANG_CL_IMPLICIT_INT_CONVERSION -Wno-implicit-int-conversion # 2117
CXX_WARN_CLANG_CL_SHADOW -Wno-shadow # 2068
CXX_WARN_CLANG_CL_SHADOW_FIELD_IN_CONSTRUCTOR -Wno-shadow-field-in-constructor # 1829
CXX_WARN_CLANG_CL_CAST_QUAL -Wno-cast-qual # 1742
CXX_WARN_CLANG_CL_PRE_C++14_COMPAT -Wno-pre-c++14-compat # 1569
CXX_WARN_CLANG_CL_GLOBAL_CONSTRUCTORS -Wno-global-constructors # 1402
CXX_WARN_CLANG_CL_SWITCH_ENUM -Wno-switch-enum # 973
CXX_WARN_CLANG_CL_EXIT_TIME_DESTRUCTORS -Wno-exit-time-destructors # 940
CXX_WARN_CLANG_CL_CTAD_MAYBE_UNSUPPORTED -Wno-ctad-maybe-unsupported # 891
CXX_WARN_CLANG_CL_UNDEFINED_FUNC_TEMPLATE -Wno-undefined-func-template # 863
CXX_WARN_CLANG_CL_C++98_COMPAT_EXTRA_SEMI -Wno-c++98-compat-extra-semi # 848
CXX_WARN_CLANG_CL_CAST_FUNCTION_TYPE -Wno-cast-function-type # 807
CXX_WARN_CLANG_CL_NULLABILITY_EXTENSION -Wno-nullability-extension # 602
CXX_WARN_CLANG_CL_SHADOW_FIELD -Wno-shadow-field # 585
CXX_WARN_CLANG_CL_CONDITIONAL_UNINITIALIZED -Wno-conditional-uninitialized # 555
CXX_WARN_CLANG_CL_UNUSED_PARAMETER -Wno-unused-parameter # 539
CXX_WARN_CLANG_CL_SUGGEST_DESTRUCTOR_OVERRIDE -Wno-suggest-destructor-override # 356
CXX_WARN_CLANG_CL_SHADOW_UNCAPTURED_LOCAL -Wno-shadow-uncaptured-local # 355
CXX_WARN_CLANG_CL_UNUSED_MACROS -Wno-unused-macros # 289
CXX_WARN_CLANG_CL_COVERED_SWITCH_DEFAULT -Wno-covered-switch-default # 233
CXX_WARN_CLANG_CL_SIGNED_ENUM_BITFIELD -Wno-signed-enum-bitfield # 229
CXX_WARN_CLANG_CL_DECLARATION_AFTER_STATEMENT -Wno-declaration-after-statement # 228
CXX_WARN_CLANG_CL_IMPLICIT_FALLTHROUGH -Wno-implicit-fallthrough # 164
CXX_WARN_CLANG_CL_NON_VIRTUAL_DTOR -Wno-non-virtual-dtor # 161
CXX_WARN_CLANG_CL_NESTED_ANON_TYPES -Wno-nested-anon-types # 140
CXX_WARN_CLANG_CL_GNU_ZERO_VARIADIC_MACRO_ARGUMENTS -Wno-gnu-zero-variadic-macro-arguments # 132
CXX_WARN_CLANG_CL_UNREACHABLE_CODE_BREAK -Wno-unreachable-code-break # 115
CXX_WARN_CLANG_CL_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE -Wno-inconsistent-missing-destructor-override # 104
CXX_WARN_CLANG_CL_FORMAT_PEDANTIC -Wno-format-pedantic # 97
CXX_WARN_CLANG_CL_NONPORTABLE_SYSTEM_INCLUDE_PATH -Wno-nonportable-system-include-path # 95
CXX_WARN_CLANG_CL_UNDEF -Wno-undef # 94
CXX_WARN_CLANG_CL_IGNORED_QUALIFIERS -Wno-ignored-qualifiers # 93
CXX_WARN_CLANG_CL_USED_BUT_MARKED_UNUSED -Wno-used-but-marked-unused # 83
CXX_WARN_CLANG_CL_HEADER_HYGIENE -Wno-header-hygiene # 79
CXX_WARN_CLANG_CL_CHAR_SUBSCRIPTS -Wno-char-subscripts # 76
CXX_WARN_CLANG_CL_UNREACHABLE_CODE_RETURN -Wno-unreachable-code-return # 71
CXX_WARN_CLANG_CL_UNUSED_TEMPLATE -Wno-unused-template # 66
CXX_WARN_CLANG_CL_GNU_ANONYMOUS_STRUCT -Wno-gnu-anonymous-struct # 63
CXX_WARN_CLANG_CL_DEPRECATED_COPY_WITH_USER_PROVIDED_DTOR -Wno-deprecated-copy-with-user-provided-dtor # 62
CXX_WARN_CLANG_CL_INCONSISTENT_MISSING_OVERRIDE -Wno-inconsistent-missing-override # 54
CXX_WARN_CLANG_CL_UNREACHABLE_CODE -Wno-unreachable-code # 52
CXX_WARN_CLANG_CL_DEPRECATED_DYNAMIC_EXCEPTION_SPEC -Wno-deprecated-dynamic-exception-spec # 51
CXX_WARN_CLANG_CL_BAD_FUNCTION_CAST -Wno-bad-function-cast # 50
CXX_WARN_CLANG_CL_MICROSOFT_ENUM_VALUE -Wno-microsoft-enum-value # 47
CXX_WARN_CLANG_CL_DEPRECATED_COPY_WITH_USER_PROVIDED_COPY -Wno-deprecated-copy-with-user-provided-copy # 41
CXX_WARN_CLANG_CL_ZERO_LENGTH_ARRAY -Wno-zero-length-array # 39
CXX_WARN_CLANG_CL_UNUSED_FUNCTION -Wno-unused-function # 38
CXX_WARN_CLANG_CL_PEDANTIC -Wno-pedantic # 38
CXX_WARN_CLANG_CL_DEPRECATED_COPY_WITH_DTOR -Wno-deprecated-copy-with-dtor # 37
CXX_WARN_CLANG_CL_DOCUMENTATION_UNKNOWN_COMMAND -Wno-documentation-unknown-command # 34
CXX_WARN_CLANG_CL_UNDEFINED_REINTERPRET_CAST -Wno-undefined-reinterpret-cast # 33
CXX_WARN_CLANG_CL_FORMAT_NONLITERAL -Wno-format-nonliteral # 29
CXX_WARN_CLANG_CL_COMMA -Wno-comma # 27
CXX_WARN_CLANG_CL_DOCUMENTATION_DEPRECATED_SYNC -Wno-documentation-deprecated-sync # 26
CXX_WARN_CLANG_CL_SHIFT_SIGN_OVERFLOW -Wno-shift-sign-overflow # 24
CXX_WARN_CLANG_CL_PRE_C++17_COMPAT_PEDANTIC -Wno-pre-c++17-compat-pedantic # 24
CXX_WARN_CLANG_CL_C++98_COMPAT_UNNAMED_TYPE_TEMPLATE_ARGS -Wno-c++98-compat-unnamed-type-template-args # 22
CXX_WARN_CLANG_CL_SIGN_COMPARE -Wno-sign-compare # 21
CXX_WARN_CLANG_CL_FORMAT -Wno-format # 21
CXX_WARN_CLANG_CL_C++98_COMPAT_BIND_TO_TEMPORARY_COPY -Wno-c++98-compat-bind-to-temporary-copy # 21
CXX_WARN_CLANG_CL_ENUM_ENUM_CONVERSION -Wno-enum-enum-conversion # 20
CXX_WARN_CLANG_CL_ANON_ENUM_ENUM_CONVERSION -Wno-anon-enum-enum-conversion # 14
CXX_WARN_CLANG_CL_RANGE_LOOP_BIND_REFERENCE -Wno-range-loop-bind-reference # 14
CXX_WARN_CLANG_CL_ENUM_FLOAT_CONVERSION -Wno-enum-float-conversion # 12
CXX_WARN_CLANG_CL_KEYWORD_MACRO -Wno-keyword-macro # 10
CXX_WARN_CLANG_CL_DEPRECATED_COPY -Wno-deprecated-copy # 10
CXX_WARN_CLANG_CL_UNUSED_MEMBER_FUNCTION -Wno-unused-member-function # 9
CXX_WARN_CLANG_CL_MISSING_NORETURN -Wno-missing-noreturn # 8
CXX_WARN_CLANG_CL_MISSING_VARIABLE_DECLARATIONS -Wno-missing-variable-declarations # 8
CXX_WARN_CLANG_CL_DOCUMENTATION_HTML -Wno-documentation-html # 6
CXX_WARN_CLANG_CL_GNU_REDECLARED_ENUM -Wno-gnu-redeclared-enum # 6
CXX_WARN_CLANG_CL_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations # 6
CXX_WARN_CLANG_CL_OVERLOADED_VIRTUAL -Wno-overloaded-virtual # 5
CXX_WARN_CLANG_CL_C++98_C++11_COMPAT_BINARY_LITERAL -Wno-c++98-c++11-compat-binary-literal # 4
CXX_WARN_CLANG_CL_DEPRECATED_REDUNDANT_CONSTEXPR_STATIC_DEF -Wno-deprecated-redundant-constexpr-static-def # 4
CXX_WARN_CLANG_CL_MISSING_BRACES -Wno-missing-braces # 4
CXX_WARN_CLANG_CL_C99_EXTENSIONS -Wno-c99-extensions # 4
CXX_WARN_CLANG_CL_STRICT_PROTOTYPES -Wno-strict-prototypes # 4
CXX_WARN_CLANG_CL_UNREACHABLE_CODE_LOOP_INCREMENT -Wno-unreachable-code-loop-increment # 4
CXX_WARN_CLANG_CL_GNU_CASE_RANGE -Wno-gnu-case-range # 4
CXX_WARN_CLANG_CL_DUPLICATE_ENUM -Wno-duplicate-enum # 3
CXX_WARN_CLANG_CL_NULL_POINTER_SUBTRACTION -Wno-null-pointer-subtraction # 2
CXX_WARN_CLANG_CL_DEPRECATED_LITERAL_OPERATOR -Wno-deprecated-literal-operator # 2
CXX_WARN_CLANG_CL_NEWLINE_EOF -Wno-newline-eof # 2
CXX_WARN_CLANG_CL_MICROSOFT_CAST -Wno-microsoft-cast # 2
CXX_WARN_CLANG_CL_DATE_TIME -Wno-date-time # 2
CXX_WARN_CLANG_CL_DELETE_NON_ABSTRACT_NON_VIRTUAL_DTOR -Wno-delete-non-abstract-non-virtual-dtor # 2
CXX_WARN_CLANG_CL_UNUSED_PRIVATE_FIELD -Wno-unused-private-field # 2
CXX_WARN_CLANG_CL_FLEXIBLE_ARRAY_EXTENSIONS -Wno-flexible-array-extensions # 2
CXX_WARN_CLANG_CL_STRING_CONVERSION -Wno-string-conversion # 2
CXX_WARN_CLANG_CL_FINAL_DTOR_NON_FINAL_CLASS -Wno-final-dtor-non-final-class # 2
CXX_WARN_CLANG_CL_MICROSOFT_UNQUALIFIED_FRIEND -Wno-microsoft-unqualified-friend # 2
CXX_WARN_CLANG_CL_INVALID_NORETURN -Wno-invalid-noreturn # 1
CXX_WARN_CLANG_CL_INVALID_UTF8 -Wno-invalid-utf8 # 1
CXX_WARN_CLANG_CL_FOUR_CHAR_CONSTANTS -Wno-four-char-constants # 1
CXX_WARN_CLANG_CL_PARENTHESES -Wno-parentheses # 1
CXX_WARN_CLANG_CL_PESSIMIZING_MOVE -Wno-pessimizing-move # 1
CXX_WARN_CLANG_CL_DEPRECATED_NON_PROTOTYPE -Wno-deprecated-non-prototype # 1
CXX_WARN_CLANG_CL_BITFIELD_ENUM_CONVERSION -Wno-bitfield-enum-conversion # 1
CXX_WARN_CLANG_CL_UNUSED_LAMBDA_CAPTURE -Wno-unused-lambda-capture # 1
CXX_WARN_CLANG_CL_SHADOW_FIELD_IN_CONSTRUCTOR_MODIFIED -Wno-shadow-field-in-constructor-modified # 1
)
endif()
# ---------------------

View File

@ -152,8 +152,6 @@ bool SimpleImage::initFromPpm(std::string filename)
rowsize = windW * 3;
}
unsigned char *pic = new unsigned char[size]; // (GLubyte *)malloc (size);
// Read in maximum value (ignore) , could be scanned with sscanf as well, but this should be
// 255... 3rd line
if (fgets(line, MAXLINE, fp) == nullptr) {
@ -162,6 +160,8 @@ bool SimpleImage::initFromPpm(std::string filename)
return 0;
}
unsigned char *pic = new unsigned char[size]; // (GLubyte *)malloc (size);
// Read in the pixel array row-by-row: 1st row = top scanline */
unsigned char *ptr = nullptr;
ptr = &pic[(windH - 1) * rowsize];

View File

@ -4693,6 +4693,12 @@ def km_grease_pencil_sculpt_mode(params):
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
# Invoke sculpt operator
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
"ctrl": True}, {"properties": [("mode", 'INVERT')]}),
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
"shift": True}, {"properties": [("mode", 'SMOOTH')]}),
*_template_paint_radial_control("gpencil_sculpt_paint"),
])

View File

@ -474,9 +474,14 @@ class _draw_tool_settings_context_mode:
)
# direction
if not capabilities.has_direction:
if brush.gpencil_sculpt_tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
layout.row().prop(brush, "direction", expand=True, text="")
# Brush falloff
layout.popover("VIEW3D_PT_tools_brush_falloff")
# Active layer only switch
layout.prop(brush.gpencil_settings, "use_active_layer_only")
return True
@staticmethod
@ -894,11 +899,11 @@ class VIEW3D_HT_header(Header):
row = layout.row(align=True)
row.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'WEIGHT_GPENCIL'}:
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
row = layout.row(align=True)
row.prop(tool_settings, "use_grease_pencil_multi_frame_editing", text="")
if object_mode in {'EDIT', 'WEIGHT_GPENCIL'}:
if object_mode in {'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
sub = row.row(align=True)
sub.enabled = tool_settings.use_grease_pencil_multi_frame_editing
sub.popover(
@ -975,6 +980,35 @@ class VIEW3D_HT_header(Header):
text="Multiframe",
)
# Grease Pencil
if obj and obj.type == 'GREASEPENCIL':
if object_mode == 'PAINT_GREASE_PENCIL':
row = layout.row()
sub = row.row(align=True)
sub.prop(tool_settings, "use_gpencil_draw_onback", text="", icon='MOD_OPACITY')
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_automerge_strokes", text="")
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_weight_data_add", text="", icon='WPAINT_HLT')
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
# Select mode for Editing
if object_mode == 'EDIT':
row = layout.row(align=True)
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='POINT')
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='STROKE')
subrow = row.row(align=True)
subrow.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='SEGMENT')
# Select mode for Sculpt
if object_mode == 'SCULPT_GPENCIL':
row = layout.row(align=True)
row.prop(tool_settings, "use_gpencil_select_mask_point", text="")
row.prop(tool_settings, "use_gpencil_select_mask_stroke", text="")
row.prop(tool_settings, "use_gpencil_select_mask_segment", text="")
overlay = view.overlay
VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
@ -1188,7 +1222,12 @@ class VIEW3D_MT_editor_menus(Menu):
mode_string = context.mode
edit_object = context.edit_object
gp_edit = obj and obj.type == 'GPENCIL' and obj.mode in {
'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL',
'EDIT_GPENCIL',
'PAINT_GPENCIL',
'SCULPT_GPENCIL',
'SCULPT_GREASE_PENCIL',
'WEIGHT_GPENCIL',
'VERTEX_GPENCIL',
}
tool_settings = context.tool_settings
@ -1196,18 +1235,16 @@ class VIEW3D_MT_editor_menus(Menu):
# Select Menu
if gp_edit:
if mode_string not in {'PAINT_GPENCIL', 'WEIGHT_GPENCIL'}:
if (
mode_string == 'SCULPT_GPENCIL' and
(tool_settings.use_gpencil_select_mask_point or
tool_settings.use_gpencil_select_mask_stroke or
tool_settings.use_gpencil_select_mask_segment)
):
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string == 'EDIT_GPENCIL':
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string == 'VERTEX_GPENCIL':
layout.menu("VIEW3D_MT_select_edit_gpencil")
use_gpencil_masking = (tool_settings.use_gpencil_select_mask_point or
tool_settings.use_gpencil_select_mask_stroke or
tool_settings.use_gpencil_select_mask_segment)
if mode_string in {
'EDIT_GPENCIL',
'VERTEX_GPENCIL'} or (
mode_string in {
'SCULPT_GPENCIL',
'SCULPT_GREASE_PENCIL'} and use_gpencil_masking):
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
mesh = obj.data
if mesh.use_paint_mask:

View File

@ -71,9 +71,7 @@ static void blf_load_datafiles_dir()
}
const char *filepath = file_list[i].path;
if (!BLI_path_extension_check_n(
filepath, ".ttf", ".ttc", ".otf", ".otc", ".woff", ".woff2", nullptr))
{
if (!BLI_path_extension_check_n(filepath, ".ttf", ".otf", ".woff", ".woff2", nullptr)) {
continue;
}
if (BLF_is_loaded(filepath)) {

View File

@ -298,18 +298,17 @@ struct SculptPoseIKChainSegment {
float len;
blender::float3 scale;
float rot[4];
float *weights;
blender::Array<float> weights;
/* Store a 4x4 transform matrix for each of the possible combinations of enabled XYZ symmetry
* axis. */
float trans_mat[PAINT_SYMM_AREAS][4][4];
float pivot_mat[PAINT_SYMM_AREAS][4][4];
float pivot_mat_inv[PAINT_SYMM_AREAS][4][4];
std::array<blender::float4x4, PAINT_SYMM_AREAS> trans_mat;
std::array<blender::float4x4, PAINT_SYMM_AREAS> pivot_mat;
std::array<blender::float4x4, PAINT_SYMM_AREAS> pivot_mat_inv;
};
struct SculptPoseIKChain {
SculptPoseIKChainSegment *segments;
int tot_segments;
blender::Array<SculptPoseIKChainSegment> segments;
blender::float3 grab_delta_offset;
};
@ -594,7 +593,7 @@ struct SculptSession {
/* Pose Brush Preview */
blender::float3 pose_origin;
SculptPoseIKChain *pose_ik_chain_preview;
std::unique_ptr<SculptPoseIKChain> pose_ik_chain_preview;
/* Boundary Brush Preview */
SculptBoundary *boundary_preview;

View File

@ -811,7 +811,7 @@ Vector<AttributeTransferData> retrieve_attributes_for_transfer(
return true;
}
const GVArray src = *src_attributes.lookup(id, meta_data.domain);
GVArray src = *src_attributes.lookup(id, meta_data.domain);
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, meta_data.domain, meta_data.data_type);
attributes.append({std::move(src), meta_data, std::move(dst)});

View File

@ -1619,14 +1619,6 @@ void BKE_sculptsession_free(Object *ob)
BKE_image_pool_free(ss->tex_pool);
}
if (ss->pose_ik_chain_preview) {
for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights);
}
MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments);
MEM_SAFE_FREE(ss->pose_ik_chain_preview);
}
if (ss->boundary_preview) {
MEM_SAFE_FREE(ss->boundary_preview->verts);
MEM_SAFE_FREE(ss->boundary_preview->edges);

View File

@ -78,10 +78,10 @@ class Task {
other.freedata = nullptr;
}
// TBB has a check in tbb/include/task_group.h where __TBB_CPP11_RVALUE_REF_PRESENT should evaluate
// to true as with the other MSVC build. However, because of the clang compiler it does not and we
// attempt to call a deleted constructor in the tbb_task_pool_run function. This check fixes this
// issue and keeps our Task constructor valid
/* TBB has a check in `tbb/include/task_group.h` where `__TBB_CPP11_RVALUE_REF_PRESENT` should
* evaluate to true as with the other MSVC build. However, because of the clang compiler
* it does not and we attempt to call a deleted constructor in the tbb_task_pool_run function.
* This check fixes this issue and keeps our Task constructor valid. */
#if (defined(WITH_TBB) && TBB_INTERFACE_VERSION_MAJOR < 10) || \
(defined(_MSC_VER) && defined(__clang__) && TBB_INTERFACE_VERSION_MAJOR < 12)
Task(const Task &other)

View File

@ -303,7 +303,8 @@ void BM_mesh_decimate_dissolve_ex(BMesh *bm,
delimit_data.cd_loop_type = CD_PROP_FLOAT2;
delimit_data.cd_loop_size = CustomData_sizeof(eCustomDataType(delimit_data.cd_loop_type));
delimit_data.cd_loop_offset = CustomData_get_n_offset(&bm->ldata, CD_PROP_FLOAT2, 0);
delimit_data.cd_loop_offset_end = delimit_data.cd_loop_size * layer_len;
delimit_data.cd_loop_offset_end = delimit_data.cd_loop_offset +
delimit_data.cd_loop_size * layer_len;
}
}

View File

@ -526,11 +526,19 @@ static void write_lightcache_texture(BlendWriter *writer, LightCacheTexture *tex
{
if (tex->data) {
size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2];
if (tex->data_type == LIGHTCACHETEX_FLOAT) {
data_size *= sizeof(float);
}
else if (tex->data_type == LIGHTCACHETEX_UINT) {
data_size *= sizeof(uint);
switch (tex->data_type) {
case LIGHTCACHETEX_BYTE:
data_size *= sizeof(uint8_t);
break;
case LIGHTCACHETEX_UINT:
data_size *= sizeof(uint32_t);
break;
case LIGHTCACHETEX_FLOAT:
data_size *= sizeof(float);
break;
default:
BLI_assert_unreachable();
break;
}
/* FIXME: We can't save more than what 32bit systems can handle.
@ -565,15 +573,20 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe
int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] *
lctex->tex_size[2];
if (lctex->data_type == LIGHTCACHETEX_FLOAT) {
BLO_read_float_array(reader, data_size, (float **)&lctex->data);
}
else if (lctex->data_type == LIGHTCACHETEX_UINT) {
BLO_read_uint32_array(reader, data_size, (uint **)&lctex->data);
}
else {
BLI_assert_unreachable();
lctex->data = nullptr;
switch (lctex->data_type) {
case LIGHTCACHETEX_BYTE:
BLO_read_uint8_array(reader, data_size, (uint8_t **)&lctex->data);
break;
case LIGHTCACHETEX_UINT:
BLO_read_uint32_array(reader, data_size, (uint32_t **)&lctex->data);
break;
case LIGHTCACHETEX_FLOAT:
BLO_read_float_array(reader, data_size, (float **)&lctex->data);
break;
default:
BLI_assert_unreachable();
lctex->data = nullptr;
break;
}
}
@ -585,8 +598,8 @@ static void direct_link_lightcache_texture(BlendDataReader *reader, LightCacheTe
void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache)
{
cache->flag &= ~LIGHTCACHE_NOT_USABLE;
direct_link_lightcache_texture(reader, &cache->cube_tx);
direct_link_lightcache_texture(reader, &cache->grid_tx);
direct_link_lightcache_texture(reader, &cache->cube_tx);
if (cache->cube_mips) {
BLO_read_struct_array(reader, LightCacheTexture, cache->mips_len, &cache->cube_mips);
@ -595,8 +608,8 @@ void EEVEE_lightcache_blend_read_data(BlendDataReader *reader, LightCache *cache
}
}
BLO_read_struct_array(reader, LightGridCache, cache->grid_len, &cache->cube_data);
BLO_read_struct_array(reader, LightProbeCache, cache->cube_len, &cache->grid_data);
BLO_read_struct_array(reader, LightGridCache, cache->grid_len, &cache->grid_data);
BLO_read_struct_array(reader, LightProbeCache, cache->cube_len, &cache->cube_data);
}
/** \} */

View File

@ -110,7 +110,7 @@ void main()
float cone_cos = cone_cosine_from_roughness(mip_roughness_clamped);
vec3 out_direction = sphere_probe_texel_to_direction(
out_local_texel, out_texel_area, sample_coord);
vec2(out_local_texel), out_texel_area, sample_coord);
out_direction = normalize(out_direction);
mat3x3 basis = tangent_basis(out_direction);

View File

@ -27,24 +27,24 @@ SphereProbeUvArea reinterpret_as_atlas_coord(ivec4 packed_coord)
/* local_texel is the texel coordinate inside the probe area [0..texel_area.extent) range.
* Returned vector is not normalized. */
vec3 sphere_probe_texel_to_direction(ivec2 local_texel,
vec3 sphere_probe_texel_to_direction(vec2 local_texel,
SphereProbePixelArea texel_area,
SphereProbeUvArea uv_area,
out vec2 sampling_uv)
{
/* Texel in probe atlas. */
ivec2 texel = local_texel + texel_area.offset;
vec2 texel = local_texel + vec2(texel_area.offset);
/* UV in sampling area. No half pixel bias to texel as the octahedral map edges area lined up
* with texel center. Note that we don't use the last row & column of pixel, hence the -2 instead
* of -1. See sphere_probe_miplvl_scale_bias. */
sampling_uv = vec2(texel) / vec2(texel_area.extent - 2);
sampling_uv = texel / vec2(texel_area.extent - 2);
/* Direction in world space. */
return octahedral_uv_to_direction(sampling_uv);
}
/* local_texel is the texel coordinate inside the probe area [0..texel_area.extent) range.
* Returned vector is not normalized. */
vec3 sphere_probe_texel_to_direction(ivec2 local_texel,
vec3 sphere_probe_texel_to_direction(vec2 local_texel,
SphereProbePixelArea texel_area,
SphereProbeUvArea uv_area)
{

View File

@ -28,40 +28,58 @@ float octahedral_texel_solid_angle(ivec2 local_texel,
/* Do not weight these border pixels that are redundant. */
return 0.0;
}
/* Since we are pouting texel centers on the edges of the octahedron, the shape of a texel can be
/* Since we are putting texel centers on the edges of the octahedron, the shape of a texel can be
* anything from a simple quad (at the Z=0 poles), to a 4 pointed start (at the Z=+-1 poles)
* passing by arrow tail shapes (at the X=0 and Y=0 edges). So while it would be more correct to
* account for all these shapes (using 8 triangles), it proves to be quite involved with all the
* corner cases. Instead, we compute the area as if the texels were not aligned with the edges.
* This simplify things at the cost of making the weighting a tiny bit off for every pixels.
* The sum of all texels is still giving 4 PI. */
vec3 v00 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
vec3 v10 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, -1), write_co, sample_co);
vec3 v20 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, -1), write_co, sample_co);
vec3 v01 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +0), write_co, sample_co);
vec3 v11 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +0), write_co, sample_co);
vec3 v21 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +0), write_co, sample_co);
vec3 v02 = sphere_probe_texel_to_direction(local_texel + ivec2(-1, +1), write_co, sample_co);
vec3 v12 = sphere_probe_texel_to_direction(local_texel + ivec2(+0, +1), write_co, sample_co);
vec3 v22 = sphere_probe_texel_to_direction(local_texel + ivec2(+1, +1), write_co, sample_co);
* passing by arrow tail shapes (at the X=0 and Y=0 edges).
*
* But we can leverage the symetries of the octahedral mapping. Given that all oddly shaped
* texels are on the X=0 and Y=0 planes, we first fold all texels to the first quadrant.
*
* The texel footprint clipped to a quadrant is a well defined spherical quad. We then multiply
* by the number of clipped shape the real texel sustains. This number is 2 at the X=0 and Y=0
* edges (the arrow shaped pixels) and 4 at the Z=+-1 poles (4 pointed start shaped pixels).
*
* The sum of all texels solid angle should be 4 PI (area of sphere). */
/* Wrap to bottom left quadrant. */
int half_size = write_co.extent >> 1;
int padded_size = write_co.extent - 2;
ivec2 wrapped_texel;
wrapped_texel.x = (local_texel.x >= half_size) ? (padded_size - local_texel.x) : local_texel.x;
wrapped_texel.y = (local_texel.y >= half_size) ? (padded_size - local_texel.y) : local_texel.y;
vec2 texel_corner_v00 = vec2(wrapped_texel) + vec2(-0.5, -0.5);
vec2 texel_corner_v10 = vec2(wrapped_texel) + vec2(+0.5, -0.5);
vec2 texel_corner_v01 = vec2(wrapped_texel) + vec2(-0.5, +0.5);
vec2 texel_corner_v11 = vec2(wrapped_texel) + vec2(+0.5, +0.5);
/* Clamp to well defined shape in spherical domain. */
texel_corner_v00 = clamp(texel_corner_v00, vec2(0.0), vec2(half_size - 1));
texel_corner_v10 = clamp(texel_corner_v10, vec2(0.0), vec2(half_size - 1));
texel_corner_v01 = clamp(texel_corner_v01, vec2(0.0), vec2(half_size - 1));
texel_corner_v11 = clamp(texel_corner_v11, vec2(0.0), vec2(half_size - 1));
/* Convert to point on sphere. */
vec3 v00 = sphere_probe_texel_to_direction(texel_corner_v00, write_co, sample_co);
vec3 v10 = sphere_probe_texel_to_direction(texel_corner_v10, write_co, sample_co);
vec3 v01 = sphere_probe_texel_to_direction(texel_corner_v01, write_co, sample_co);
vec3 v11 = sphere_probe_texel_to_direction(texel_corner_v11, write_co, sample_co);
/* The solid angle functions expect normalized vectors. */
v00 = normalize(v00);
v10 = normalize(v10);
v20 = normalize(v20);
v01 = normalize(v01);
v11 = normalize(v11);
v21 = normalize(v21);
v02 = normalize(v02);
v12 = normalize(v12);
v22 = normalize(v22);
#if 0 /* Has artifacts, is marginally more correct. */
/* For some reason quad_solid_angle(v10, v20, v11, v21) gives some strange artifacts at Z=0. */
return 0.25 * (quad_solid_angle(v00, v10, v01, v11) + quad_solid_angle(v10, v20, v11, v21) +
quad_solid_angle(v01, v11, v02, v12) + quad_solid_angle(v11, v21, v12, v22));
#else
/* Choosing the positive quad (0,0) > (+1,+1) for stability. */
return quad_solid_angle(v11, v21, v12, v22);
#endif
/* Compute solid angle of the spherical quad. */
float texel_clipped_solid_angle = quad_solid_angle(v00, v10, v01, v11);
/* Multiply by the symetric halfs that we omited.
* Also important to note that we avoid weighting the same pixel more than it's total sampled
* footprint if it is duplicated in another pixel of the map. So border pixels do not require any
* special treatment. Only the center cross needs it. */
if (wrapped_texel.x == half_size - 1) {
texel_clipped_solid_angle *= 2.0;
}
if (wrapped_texel.y == half_size - 1) {
texel_clipped_solid_angle *= 2.0;
}
return texel_clipped_solid_angle;
}
void main()
@ -75,7 +93,7 @@ void main()
vec2 wrapped_uv;
vec3 direction = sphere_probe_texel_to_direction(
local_texel, write_coord, sample_coord, wrapped_uv);
vec2(local_texel), write_coord, sample_coord, wrapped_uv);
vec4 radiance_and_transmittance = texture(cubemap_tx, direction);
vec3 radiance = radiance_and_transmittance.xyz;
@ -118,11 +136,12 @@ void main()
* Note that this is an approximation since the footprint of a thread-group is not
* necessarily a convex polygons (with center of gravity at midpoint).
* But the actual error introduce by this approximation is not perceivable. */
/* FIXME(fclem): The error IS very perceivable for resolution lower than a quadrant. */
ivec2 max_group_texel = local_texel + ivec2(gl_WorkGroupSize.xy);
/* Min direction is the local direction since this is only ran by thread 0. */
vec3 min_direction = normalize(direction);
vec3 max_direction = normalize(
sphere_probe_texel_to_direction(max_group_texel, write_coord, sample_coord));
sphere_probe_texel_to_direction(vec2(max_group_texel), write_coord, sample_coord));
vec3 L = normalize(min_direction + max_direction);
/* Convert radiance to spherical harmonics. */
SphericalHarmonicL1 sh;

View File

@ -718,12 +718,13 @@ SphericalHarmonicL1 spherical_harmonics_clamp(SphericalHarmonicL1 sh, float clam
{
/* Convert coefficients to per channel column. */
mat4x4 per_channel = transpose(mat4x4(sh.L0.M0, sh.L1.Mn1, sh.L1.M0, sh.L1.Mp1));
/* Maximum per channel. */
vec3 max_L1 = vec3(reduce_max(abs(per_channel[0].yzw)),
reduce_max(abs(per_channel[1].yzw)),
reduce_max(abs(per_channel[2].yzw)));
/* Magnitute per channel. */
vec3 mag_L1;
mag_L1.r = length(per_channel[0].yzw);
mag_L1.g = length(per_channel[1].yzw);
mag_L1.b = length(per_channel[2].yzw);
/* Find maximum of the sh function over all channels. */
vec3 max_sh = abs(sh.L0.M0.rgb) * 0.282094792 + max_L1 * 0.488602512;
vec3 max_sh = abs(sh.L0.M0.rgb) * 0.282094792 + mag_L1 * 0.488602512;
float fac = clamp_value * safe_rcp(reduce_max(max_sh));
if (fac > 1.0) {

View File

@ -834,6 +834,52 @@ static void updateDuplicateTransformConstraintSettings(Object *ob,
mul_m4_v3(imat, trans->to_max_scale);
}
static void track_axis_x_swap(int &value)
{
/* Swap track axis X <> -X. */
if (value == TRACK_X) {
value = TRACK_nX;
}
else if (value == TRACK_nX) {
value = TRACK_X;
}
}
static void track_axis_x_swap(char &value)
{
/* Swap track axis X <> -X. */
if (value == TRACK_X) {
value = TRACK_nX;
}
else if (value == TRACK_nX) {
value = TRACK_X;
}
}
static void updateDuplicateConstraintTrackToSettings(bConstraint *curcon)
{
bTrackToConstraint *data = static_cast<bTrackToConstraint *>(curcon->data);
track_axis_x_swap(data->reserved1);
}
static void updateDuplicateConstraintLockTrackSettings(bConstraint *curcon)
{
bLockTrackConstraint *data = static_cast<bLockTrackConstraint *>(curcon->data);
track_axis_x_swap(data->trackflag);
}
static void updateDuplicateConstraintDampTrackSettings(bConstraint *curcon)
{
bDampTrackConstraint *data = static_cast<bDampTrackConstraint *>(curcon->data);
track_axis_x_swap(data->trackflag);
}
static void updateDuplicateConstraintShrinkwrapSettings(bConstraint *curcon)
{
bShrinkwrapConstraint *data = static_cast<bShrinkwrapConstraint *>(curcon->data);
track_axis_x_swap(data->trackAxis);
}
static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig_bone, Object *ob)
{
/* If an edit bone has been duplicated, lets update its constraints if the
@ -863,6 +909,18 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig
case CONSTRAINT_TYPE_TRANSFORM:
updateDuplicateTransformConstraintSettings(ob, pchan, curcon);
break;
case CONSTRAINT_TYPE_TRACKTO:
updateDuplicateConstraintTrackToSettings(curcon);
break;
case CONSTRAINT_TYPE_LOCKTRACK:
updateDuplicateConstraintLockTrackSettings(curcon);
break;
case CONSTRAINT_TYPE_DAMPTRACK:
updateDuplicateConstraintDampTrackSettings(curcon);
break;
case CONSTRAINT_TYPE_SHRINKWRAP:
updateDuplicateConstraintShrinkwrapSettings(curcon);
break;
}
}
}

View File

@ -54,7 +54,7 @@ void send_redraw_notifier(const bContext &C)
static bool asset_shelf_type_poll(const bContext &C,
const SpaceType &space_type,
AssetShelfType *shelf_type)
const AssetShelfType *shelf_type)
{
if (!shelf_type) {
return false;
@ -71,13 +71,13 @@ static bool asset_shelf_type_poll(const bContext &C,
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
}
static AssetShelfType *asset_shelf_type_ensure(SpaceType &space_type, AssetShelf &shelf)
static AssetShelfType *asset_shelf_type_ensure(const SpaceType &space_type, AssetShelf &shelf)
{
if (shelf.type) {
return shelf.type;
}
for (std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (STREQ(shelf.idname, shelf_type->idname)) {
shelf.type = shelf_type.get();
return shelf_type.get();
@ -141,7 +141,7 @@ static void activate_shelf(RegionAssetShelf &shelf_regiondata, AssetShelf &shelf
* current context (all polls failed).
*/
static AssetShelf *update_active_shelf(const bContext &C,
SpaceType &space_type,
const SpaceType &space_type,
RegionAssetShelf &shelf_regiondata,
FunctionRef<void(AssetShelf &new_shelf)> on_create)
{
@ -172,7 +172,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
}
/* Case 3: */
for (std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (asset_shelf_type_poll(C, space_type, shelf_type.get())) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);

View File

@ -31,7 +31,7 @@ void build_asset_view(uiLayout &layout,
const AssetLibraryReference &library_ref,
const AssetShelf &shelf,
const bContext &C,
ARegion &region);
const ARegion &region);
void catalog_selector_panel_register(ARegionType *region_type);

View File

@ -269,7 +269,7 @@ void build_asset_view(uiLayout &layout,
const AssetLibraryReference &library_ref,
const AssetShelf &shelf,
const bContext &C,
ARegion &region)
const ARegion &region)
{
list::storage_fetch(&library_ref, &C);
list::ensure_previews_job(&library_ref, &C);

View File

@ -2193,18 +2193,20 @@ static void GREASE_PENCIL_OT_separate(wmOperatorType *ot)
* \{ */
/* Global clipboard for Grease Pencil curves. */
struct Clipboard {
static struct Clipboard {
bke::CurvesGeometry curves;
/* We store the material uid's of the copied curves, so we can match those when pasting the
* clipboard into another object. */
Vector<std::pair<uint, int>> materials;
int materials_in_source_num;
};
} *grease_pencil_clipboard = nullptr;
static Clipboard &get_grease_pencil_clipboard()
static Clipboard &ensure_grease_pencil_clipboard()
{
static Clipboard clipboard;
return clipboard;
if (grease_pencil_clipboard == nullptr) {
grease_pencil_clipboard = MEM_new<Clipboard>(__func__);
}
return *grease_pencil_clipboard;
}
static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
@ -2217,8 +2219,6 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
const bool paste_on_back = RNA_boolean_get(op->ptr, "paste_back");
Clipboard &clipboard = get_grease_pencil_clipboard();
/* Get active layer in the target object. */
if (!grease_pencil.has_active_layer()) {
BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
@ -2251,60 +2251,8 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
selection_in_target.finish();
});
/* Get a list of all materials in the scene. */
Map<uint, Material *> scene_materials;
LISTBASE_FOREACH (Material *, material, &bmain->materials) {
scene_materials.add(material->id.session_uid, material);
}
clipboard_paste_strokes(*bmain, *object, *target_drawing, paste_on_back);
/* Map the materials used in the clipboard curves to the materials in the target object. */
Array<int> clipboard_material_remap(clipboard.materials_in_source_num, 0);
for (const int i : clipboard.materials.index_range()) {
/* Check if the material name exists in the scene. */
int target_index;
uint material_id = clipboard.materials[i].first;
Material *material = scene_materials.lookup_default(material_id, nullptr);
if (!material) {
/* Material is removed, so create a new material. */
BKE_grease_pencil_object_material_new(bmain, object, nullptr, &target_index);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
continue;
}
/* Find or add the material to the target object. */
target_index = BKE_object_material_ensure(bmain, object, material);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
}
/* Get the index range of the pasted curves in the target layer. */
IndexRange pasted_curves_range = paste_on_back ?
IndexRange(0, clipboard.curves.curves_num()) :
IndexRange(target_drawing->strokes().curves_num(),
clipboard.curves.curves_num());
/* Append the geometry from the clipboard to the target layer. */
Curves *clipboard_curves = curves_new_nomain(clipboard.curves);
Curves *target_curves = curves_new_nomain(std::move(target_drawing->strokes_for_write()));
Array<bke::GeometrySet> geometry_sets = {
bke::GeometrySet::from_curves(paste_on_back ? clipboard_curves : target_curves),
bke::GeometrySet::from_curves(paste_on_back ? target_curves : clipboard_curves)};
bke::GeometrySet joined_curves = geometry::join_geometries(geometry_sets, {});
target_drawing->strokes_for_write() = std::move(
joined_curves.get_curves_for_write()->geometry.wrap());
/* Remap the material indices of the pasted curves to the target object material indices. */
bke::MutableAttributeAccessor attributes =
target_drawing->strokes_for_write().attributes_for_write();
bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Curve);
if (material_indices) {
for (const int i : pasted_curves_range) {
material_indices.span[i] = clipboard_material_remap[material_indices.span[i]];
}
material_indices.finish();
}
target_drawing->tag_topology_changed();
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
@ -2319,7 +2267,7 @@ static int grease_pencil_copy_strokes_exec(bContext *C, wmOperator *op)
const bke::AttrDomain selection_domain = ED_grease_pencil_selection_domain_get(
scene->toolsettings);
Clipboard &clipboard = get_grease_pencil_clipboard();
Clipboard &clipboard = ensure_grease_pencil_clipboard();
bool anything_copied = false;
int num_copied = 0;
@ -2400,8 +2348,7 @@ static bool grease_pencil_paste_strokes_poll(bContext *C)
}
/* Check for curves in the Grease Pencil clipboard. */
Clipboard &clipboard = get_grease_pencil_clipboard();
return (clipboard.curves.curves_num() > 0);
return (grease_pencil_clipboard && grease_pencil_clipboard->curves.curves_num() > 0);
}
static void GREASE_PENCIL_OT_paste(wmOperatorType *ot)
@ -2439,6 +2386,94 @@ static void GREASE_PENCIL_OT_copy(wmOperatorType *ot)
/** \} */
void clipboard_free()
{
if (grease_pencil_clipboard) {
MEM_delete(grease_pencil_clipboard);
grease_pencil_clipboard = nullptr;
}
}
const bke::CurvesGeometry &clipboard_curves()
{
using namespace blender::ed::greasepencil;
return ensure_grease_pencil_clipboard().curves;
}
static Array<int> clipboard_materials_remap(Main &bmain, Object &object)
{
using namespace blender::ed::greasepencil;
/* Get a list of all materials in the scene. */
Map<uint, Material *> scene_materials;
LISTBASE_FOREACH (Material *, material, &bmain.materials) {
scene_materials.add(material->id.session_uid, material);
}
Clipboard &clipboard = ensure_grease_pencil_clipboard();
Array<int> clipboard_material_remap(clipboard.materials_in_source_num, 0);
for (const int i : clipboard.materials.index_range()) {
/* Check if the material name exists in the scene. */
int target_index;
uint material_id = clipboard.materials[i].first;
Material *material = scene_materials.lookup_default(material_id, nullptr);
if (!material) {
/* Material is removed, so create a new material. */
BKE_grease_pencil_object_material_new(&bmain, &object, nullptr, &target_index);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
continue;
}
/* Find or add the material to the target object. */
target_index = BKE_object_material_ensure(&bmain, &object, material);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
}
return clipboard_material_remap;
}
IndexRange clipboard_paste_strokes(Main &bmain,
Object &object,
bke::greasepencil::Drawing &drawing,
const bool paste_back)
{
const bke::CurvesGeometry &clipboard_curves = ed::greasepencil::clipboard_curves();
/* Get a list of all materials in the scene. */
const Array<int> clipboard_material_remap = ed::greasepencil::clipboard_materials_remap(bmain,
object);
/* Get the index range of the pasted curves in the target layer. */
const IndexRange pasted_curves_range = paste_back ?
IndexRange(0, clipboard_curves.curves_num()) :
IndexRange(drawing.strokes().curves_num(),
clipboard_curves.curves_num());
/* Append the geometry from the clipboard to the target layer. */
Curves *clipboard_id = bke::curves_new_nomain(clipboard_curves);
Curves *target_id = curves_new_nomain(std::move(drawing.strokes_for_write()));
const Array<bke::GeometrySet> geometry_sets = {
bke::GeometrySet::from_curves(paste_back ? clipboard_id : target_id),
bke::GeometrySet::from_curves(paste_back ? target_id : clipboard_id)};
bke::GeometrySet joined_curves = geometry::join_geometries(geometry_sets, {});
drawing.strokes_for_write() = std::move(joined_curves.get_curves_for_write()->geometry.wrap());
/* Remap the material indices of the pasted curves to the target object material indices. */
bke::MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Curve);
if (material_indices) {
for (const int i : pasted_curves_range) {
material_indices.span[i] = clipboard_material_remap[material_indices.span[i]];
}
material_indices.finish();
}
drawing.tag_topology_changed();
return pasted_curves_range;
}
} // namespace blender::ed::greasepencil
void ED_operatortypes_grease_pencil_edit()

View File

@ -563,6 +563,47 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
return editable_drawings;
}
Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer_with_falloff(
const Scene &scene,
GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &layer)
{
using namespace blender::bke::greasepencil;
const int current_frame = scene.r.cfra;
const ToolSettings *toolsettings = scene.toolsettings;
const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
GP_USE_MULTI_FRAME_EDITING) != 0;
const bool use_multi_frame_falloff = use_multi_frame_editing &&
(toolsettings->gp_sculpt.flag &
GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
const int layer_index = *grease_pencil.get_layer_index(layer);
int center_frame;
std::pair<int, int> minmax_frame;
if (use_multi_frame_falloff) {
BKE_curvemapping_init(toolsettings->gp_sculpt.cur_falloff);
minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
}
Vector<MutableDrawingInfo> editable_drawings;
const Array<int> frame_numbers = get_editable_frames_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
const float falloff = use_multi_frame_falloff ?
get_multi_frame_falloff(frame_number,
center_frame,
minmax_frame.first,
minmax_frame.second,
toolsettings->gp_sculpt.cur_falloff) :
1.0f;
editable_drawings.append({*drawing, layer_index, frame_number, falloff});
}
}
return editable_drawings;
}
Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil,
const bool do_onion_skinning)

View File

@ -34,7 +34,8 @@ struct ViewContext;
namespace blender {
namespace bke {
enum class AttrDomain : int8_t;
}
class CurvesGeometry;
} // namespace bke
} // namespace blender
enum {
@ -62,6 +63,7 @@ void ED_primitivetool_modal_keymap(wmKeyConfig *keyconf);
void GREASE_PENCIL_OT_stroke_cutter(wmOperatorType *ot);
void ED_undosys_type_grease_pencil(UndoType *undo_type);
/**
* Get the selection mode for Grease Pencil selection operators: point, stroke, segment.
*/
@ -256,6 +258,8 @@ Array<Vector<MutableDrawingInfo>> retrieve_editable_drawings_grouped_per_frame(
const Scene &scene, GreasePencil &grease_pencil);
Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
const Scene &scene, GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer);
Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer_with_falloff(
const Scene &scene, GreasePencil &grease_pencil, const bke::greasepencil::Layer &layer);
Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil,
bool do_onion_skinning);
@ -381,4 +385,16 @@ void normalize_vertex_weights(MDeformVert &dvert,
Span<bool> vertex_group_is_locked,
Span<bool> vertex_group_is_bone_deformed);
void clipboard_free();
const bke::CurvesGeometry &clipboard_curves();
/**
* Paste curves from the clipboard into the drawing.
* \param paste_back Render behind existing curves by inserting curves at the front.
* \return Index range of the new curves in the drawing after pasting.
*/
IndexRange clipboard_paste_strokes(Main &bmain,
Object &object,
bke::greasepencil::Drawing &drawing,
bool paste_back);
} // namespace blender::ed::greasepencil

View File

@ -104,6 +104,9 @@ void ED_slider_allow_increments_set(tSlider *slider, bool value);
void ED_slider_mode_set(tSlider *slider, SliderMode mode);
SliderMode ED_slider_mode_get(const tSlider *slider);
void ED_slider_unit_set(tSlider *slider, const char *unit);
/* Set a name that will show next to the slider to indicate which property is modified currently.
* To clear, set to an empty string. */
void ED_slider_property_label_set(tSlider *slider, const char *prop_name);
/* ************** XXX OLD CRUFT WARNING ************* */

View File

@ -577,8 +577,7 @@ static int collection_exporter_export(bContext *C,
filepath, sizeof(filepath), "//", fh->get_default_filename(collection_name).c_str());
}
else {
char filename[FILENAME_MAX];
BLI_path_split_file_part(filepath, filename, sizeof(filename));
const char *filename = BLI_path_basename(filepath);
if (!filename[0] || !BLI_path_extension(filename)) {
BKE_reportf(op->reports, RPT_ERROR, "File path '%s' is not a valid file", filepath);

View File

@ -47,6 +47,16 @@ set(SRC
grease_pencil_draw_ops.cc
grease_pencil_erase.cc
grease_pencil_paint.cc
grease_pencil_sculpt_clone.cc
grease_pencil_sculpt_common.cc
grease_pencil_sculpt_grab.cc
grease_pencil_sculpt_pinch.cc
grease_pencil_sculpt_push.cc
grease_pencil_sculpt_randomize.cc
grease_pencil_sculpt_smooth.cc
grease_pencil_sculpt_strength.cc
grease_pencil_sculpt_thickness.cc
grease_pencil_sculpt_twist.cc
grease_pencil_tint.cc
grease_pencil_weight_average.cc
grease_pencil_weight_blur.cc

View File

@ -27,6 +27,7 @@
#include "WM_message.hh"
#include "WM_toolsystem.hh"
#include "curves_sculpt_intern.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
@ -208,30 +209,31 @@ static bool grease_pencil_sculpt_paint_poll(bContext *C)
return true;
}
static GreasePencilStrokeOperation *grease_pencil_sculpt_paint_operation(bContext &C)
static GreasePencilStrokeOperation *grease_pencil_sculpt_paint_operation(
bContext &C, const BrushStrokeMode stroke_mode)
{
const Scene &scene = *CTX_data_scene(&C);
const GpSculptPaint &gp_sculptpaint = *scene.toolsettings->gp_sculptpaint;
const Brush &brush = *BKE_paint_brush_for_read(&gp_sculptpaint.paint);
switch (eBrushGPSculptTool(brush.gpencil_sculpt_tool)) {
case GPSCULPT_TOOL_SMOOTH:
return nullptr;
return greasepencil::new_smooth_operation(stroke_mode).release();
case GPSCULPT_TOOL_THICKNESS:
return nullptr;
return greasepencil::new_thickness_operation(stroke_mode).release();
case GPSCULPT_TOOL_STRENGTH:
return nullptr;
return greasepencil::new_strength_operation(stroke_mode).release();
case GPSCULPT_TOOL_GRAB:
return nullptr;
return greasepencil::new_grab_operation(stroke_mode).release();
case GPSCULPT_TOOL_PUSH:
return nullptr;
return greasepencil::new_push_operation(stroke_mode).release();
case GPSCULPT_TOOL_TWIST:
return nullptr;
return greasepencil::new_twist_operation(stroke_mode).release();
case GPSCULPT_TOOL_PINCH:
return nullptr;
return greasepencil::new_pinch_operation(stroke_mode).release();
case GPSCULPT_TOOL_RANDOMIZE:
return nullptr;
return greasepencil::new_randomize_operation(stroke_mode).release();
case GPSCULPT_TOOL_CLONE:
return nullptr;
return greasepencil::new_clone_operation(stroke_mode).release();
}
return nullptr;
}
@ -240,7 +242,8 @@ static bool grease_pencil_sculpt_paint_test_start(bContext *C,
wmOperator *op,
const float mouse[2])
{
GreasePencilStrokeOperation *operation = grease_pencil_sculpt_paint_operation(*C);
const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
GreasePencilStrokeOperation *operation = grease_pencil_sculpt_paint_operation(*C, stroke_mode);
if (operation) {
stroke_start(*C, *op, float2(mouse), *operation);
return true;

View File

@ -4,9 +4,18 @@
#pragma once
#include "DNA_scene_types.h"
#include "ED_grease_pencil.hh"
#include "paint_intern.hh"
#include "BLI_math_vector.hh"
namespace blender::bke::greasepencil {
class Drawing;
class Layer;
} // namespace blender::bke::greasepencil
namespace blender::bke::crazyspace {
struct GeometryDeformation;
}
namespace blender::ed::sculpt_paint {
@ -25,6 +34,94 @@ class GreasePencilStrokeOperation {
namespace greasepencil {
/* Get list of drawings the tool should be operating on. */
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_sculpt(const bContext &C);
/* Make sure the brush has all necessary grease pencil settings. */
void init_brush(Brush &brush);
/* Index mask of all points within the brush radius. */
IndexMask brush_influence_mask(const Scene &scene,
const Brush &brush,
const float2 &mouse_position,
float pressure,
float multi_frame_falloff,
const IndexMask &selection,
Span<float2> view_positions,
Vector<float> &influences,
IndexMaskMemory &memory);
/* Influence value at point co for the brush. */
float brush_influence(const Scene &scene,
const Brush &brush,
const float2 &co,
const InputSample &sample,
float multi_frame_falloff);
/* True if influence of the brush should be inverted. */
bool is_brush_inverted(const Brush &brush, BrushStrokeMode stroke_mode);
/* Common parameters for stroke callbacks that can be passed to utility functions. */
struct GreasePencilStrokeParams {
const ToolSettings &toolsettings;
const ARegion &region;
Object &ob_orig;
Object &ob_eval;
const bke::greasepencil::Layer &layer;
int layer_index;
int frame_number;
float multi_frame_falloff;
ed::greasepencil::DrawingPlacement placement;
bke::greasepencil::Drawing &drawing;
/* Note: accessing region in worker threads will return null,
* this has to be done on the main thread and passed explicitly. */
static GreasePencilStrokeParams from_context(const Scene &scene,
const Depsgraph &depsgraph,
const ARegion &region,
const View3D &view3d,
Object &object,
int layer_index,
int frame_number,
float multi_frame_falloff,
bke::greasepencil::Drawing &drawing);
};
/* Point index mask for a drawing based on selection tool settings. */
IndexMask point_selection_mask(const GreasePencilStrokeParams &params, IndexMaskMemory &memory);
bke::crazyspace::GeometryDeformation get_drawing_deformation(
const GreasePencilStrokeParams &params);
/* Project points from layer space into 2D view space. */
Array<float2> calculate_view_positions(const GreasePencilStrokeParams &params,
const IndexMask &selection);
/* Stroke operation base class that performs various common initializations. */
class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
public:
using MutableDrawingInfo = blender::ed::greasepencil::MutableDrawingInfo;
using DrawingPlacement = ed::greasepencil::DrawingPlacement;
BrushStrokeMode stroke_mode;
/* Previous mouse position for computing the direction. */
float2 prev_mouse_position;
GreasePencilStrokeOperationCommon(const BrushStrokeMode stroke_mode) : stroke_mode(stroke_mode)
{
}
bool is_inverted(const Brush &brush) const;
float2 mouse_delta(const InputSample &input_sample) const;
void init_stroke(const bContext &C, const InputSample &start_sample);
void stroke_extended(const InputSample &extension_sample);
void foreach_editable_drawing(
const bContext &C, FunctionRef<bool(const GreasePencilStrokeParams &params)> fn) const;
};
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation();
std::unique_ptr<GreasePencilStrokeOperation> new_erase_operation();
std::unique_ptr<GreasePencilStrokeOperation> new_tint_operation();
@ -33,6 +130,15 @@ std::unique_ptr<GreasePencilStrokeOperation> new_weight_paint_draw_operation(
std::unique_ptr<GreasePencilStrokeOperation> new_weight_paint_blur_operation();
std::unique_ptr<GreasePencilStrokeOperation> new_weight_paint_average_operation();
std::unique_ptr<GreasePencilStrokeOperation> new_weight_paint_smear_operation();
std::unique_ptr<GreasePencilStrokeOperation> new_smooth_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_thickness_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_strength_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_randomize_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_grab_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_push_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_pinch_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_twist_operation(BrushStrokeMode stroke_mode);
std::unique_ptr<GreasePencilStrokeOperation> new_clone_operation(BrushStrokeMode stroke_mode);
} // namespace greasepencil

View File

@ -331,13 +331,14 @@ struct PaintOperationExecutor {
* stable) fit. */
Array<float2> coords_pre_blur(smooth_window.size());
const int pre_blur_iterations = 3;
geometry::gaussian_blur_1D(coords_to_smooth,
pre_blur_iterations,
settings_->active_smooth,
true,
true,
false,
coords_pre_blur.as_mutable_span());
geometry::gaussian_blur_1D(
coords_to_smooth,
pre_blur_iterations,
VArray<float>::ForSingle(settings_->active_smooth, smooth_window.size()),
true,
true,
false,
coords_pre_blur.as_mutable_span());
/* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
const float max_error_threshold_px = 5.0f;

View File

@ -0,0 +1,90 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_curves.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class CloneOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
static float2 arithmetic_mean(Span<float2> values)
{
return std::accumulate(values.begin(), values.end(), float2(0)) / values.size();
}
void CloneOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
Main &bmain = *CTX_data_main(&C);
Object &object = *CTX_data_active_object(&C);
this->init_stroke(C, start_sample);
/* Note: Only one copy is created at the beginning of each stroke.
* GPv2 supposedly has 2 modes:
* - Stamp: Clone on stroke start and then transform (the transform part doesn't work)
* - Continuous: Create multiple copies during the stroke (disabled)
*
* Here we only have the GPv2 behavior that actually works for now. */
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
const IndexRange pasted_curves = ed::greasepencil::clipboard_paste_strokes(
bmain, object, params.drawing, false);
if (pasted_curves.is_empty()) {
return false;
}
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
const OffsetIndices<int> pasted_points_by_curve = curves.points_by_curve().slice(
pasted_curves);
const IndexRange pasted_points = IndexRange::from_begin_size(
pasted_points_by_curve[0].start(),
pasted_points_by_curve.total_size() - pasted_points_by_curve[0].start());
Array<float2> view_positions = calculate_view_positions(params, pasted_points);
const float2 center = arithmetic_mean(view_positions.as_mutable_span().slice(pasted_points));
const float2 &mouse_delta = start_sample.mouse_position - center;
MutableSpan<float3> positions = curves.positions_for_write();
threading::parallel_for(pasted_points, 4096, [&](const IndexRange range) {
for (const int point_i : range) {
positions[point_i] = params.placement.project(view_positions[point_i] + mouse_delta);
}
});
params.drawing.tag_positions_changed();
return true;
});
}
void CloneOperation::on_stroke_extended(const bContext & /*C*/,
const InputSample &extension_sample)
{
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_clone_operation(const BrushStrokeMode stroke_mode)
{
return std::make_unique<CloneOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,284 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_brush.hh"
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BLI_index_mask.hh"
#include "BLI_math_vector.hh"
#include "BLI_task.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_brush_types.h"
#include "DNA_node_tree_interface_types.h"
#include "DNA_screen_types.h"
#include "DNA_view3d_types.h"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "grease_pencil_intern.hh"
#include <iostream>
namespace blender::ed::sculpt_paint::greasepencil {
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_sculpt(const bContext &C)
{
using namespace blender::bke::greasepencil;
const Scene &scene = *CTX_data_scene(&C);
Object &ob_orig = *CTX_data_active_object(&C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const bool active_layer_only = ((brush.gpencil_settings->flag & GP_BRUSH_ACTIVE_LAYER_ONLY) !=
0);
if (active_layer_only) {
/* Apply only to the drawing at the current frame of the active layer. */
if (!grease_pencil.has_active_layer()) {
return {};
}
const Layer &active_layer = *grease_pencil.get_active_layer();
return ed::greasepencil::retrieve_editable_drawings_from_layer_with_falloff(
scene, grease_pencil, active_layer);
}
/* Apply to all editable drawings. */
return ed::greasepencil::retrieve_editable_drawings_with_falloff(scene, grease_pencil);
}
void init_brush(Brush &brush)
{
if (brush.gpencil_settings == nullptr) {
BKE_brush_init_gpencil_settings(&brush);
}
BLI_assert(brush.gpencil_settings != nullptr);
BKE_curvemapping_init(brush.gpencil_settings->curve_strength);
BKE_curvemapping_init(brush.gpencil_settings->curve_sensitivity);
BKE_curvemapping_init(brush.gpencil_settings->curve_jitter);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_pressure);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_strength);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_uv);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_hue);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_saturation);
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_value);
}
static float brush_radius(const Scene &scene, const Brush &brush, const float pressure = 1.0f)
{
float radius = BKE_brush_size_get(&scene, &brush);
if (BKE_brush_use_size_pressure(&brush)) {
radius *= BKE_curvemapping_evaluateF(brush.gpencil_settings->curve_sensitivity, 0, pressure);
}
return radius;
}
float brush_influence(const Scene &scene,
const Brush &brush,
const float2 &co,
const InputSample &sample,
const float multi_frame_falloff)
{
const float radius = brush_radius(scene, brush, sample.pressure);
/* Basic strength factor from brush settings. */
const float brush_pressure = BKE_brush_use_alpha_pressure(&brush) ? sample.pressure : 1.0f;
const float influence_base = BKE_brush_alpha_get(&scene, &brush) * brush_pressure *
multi_frame_falloff;
/* Distance falloff. */
const int2 mval_i = int2(math::round(sample.mouse_position));
const float distance = math::distance(mval_i, int2(co));
/* Apply Brush curve. */
const float brush_falloff = BKE_brush_curve_strength(&brush, distance, radius);
return influence_base * brush_falloff;
}
IndexMask brush_influence_mask(const Scene &scene,
const Brush &brush,
const float2 &mouse_position,
const float pressure,
const float multi_frame_falloff,
const IndexMask &selection,
const Span<float2> view_positions,
Vector<float> &influences,
IndexMaskMemory &memory)
{
if (selection.is_empty()) {
return {};
}
const float radius = brush_radius(scene, brush, pressure);
const float radius_squared = radius * radius;
const float brush_pressure = BKE_brush_use_alpha_pressure(&brush) ? pressure : 1.0f;
const float influence_base = BKE_brush_alpha_get(&scene, &brush) * brush_pressure *
multi_frame_falloff;
const int2 mval_i = int2(math::round(mouse_position));
Array<float> all_influences(selection.min_array_size());
const IndexMask influence_mask = IndexMask::from_predicate(
selection, GrainSize(4096), memory, [&](const int point) {
/* Distance falloff. */
const float distance_squared = math::distance_squared(int2(view_positions[point]), mval_i);
if (distance_squared > radius_squared) {
all_influences[point] = 0.0f;
return false;
}
/* Apply Brush curve. */
const float brush_falloff = BKE_brush_curve_strength(
&brush, math::sqrt(distance_squared), radius);
all_influences[point] = influence_base * brush_falloff;
return all_influences[point] > 0.0f;
});
influences.reinitialize(influence_mask.size());
array_utils::gather(all_influences.as_span(), influence_mask, influences.as_mutable_span());
return influence_mask;
}
bool is_brush_inverted(const Brush &brush, const BrushStrokeMode stroke_mode)
{
/* The basic setting is the brush's setting. During runtime, the user can hold down the Ctrl key
* to invert the basic behavior. */
return bool(brush.flag & BRUSH_DIR_IN) ^ (stroke_mode == BrushStrokeMode::BRUSH_STROKE_INVERT);
}
GreasePencilStrokeParams GreasePencilStrokeParams::from_context(
const Scene &scene,
const Depsgraph &depsgraph,
const ARegion &region,
const View3D &view3d,
Object &object,
const int layer_index,
const int frame_number,
const float multi_frame_falloff,
bke::greasepencil::Drawing &drawing)
{
Object &ob_eval = *DEG_get_evaluated_object(&depsgraph, &object);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
ed::greasepencil::DrawingPlacement placement(scene, region, view3d, ob_eval, layer);
return {*scene.toolsettings,
region,
object,
ob_eval,
layer,
layer_index,
frame_number,
multi_frame_falloff,
std::move(placement),
drawing};
}
IndexMask point_selection_mask(const GreasePencilStrokeParams &params, IndexMaskMemory &memory)
{
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
eGP_Sculpt_SelectMaskFlag(params.toolsettings.gpencil_selectmode_sculpt));
return (is_masking ? ed::greasepencil::retrieve_editable_and_selected_points(
params.ob_eval, params.drawing, memory) :
params.drawing.strokes().points_range());
}
bke::crazyspace::GeometryDeformation get_drawing_deformation(
const GreasePencilStrokeParams &params)
{
return bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
&params.ob_eval, params.ob_orig, params.layer_index, params.frame_number);
}
Array<float2> calculate_view_positions(const GreasePencilStrokeParams &params,
const IndexMask &selection)
{
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
Array<float2> view_positions(deformation.positions.size());
/* Compute screen space positions. */
const float4x4 transform = params.layer.to_world_space(params.ob_eval);
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
eV3DProjStatus result = ED_view3d_project_float_global(
&params.region,
math::transform_point(transform, deformation.positions[point_i]),
view_positions[point_i],
V3D_PROJ_TEST_NOP);
if (result != V3D_PROJ_RET_OK) {
view_positions[point_i] = float2(0);
}
});
return view_positions;
}
bool GreasePencilStrokeOperationCommon::is_inverted(const Brush &brush) const
{
return is_brush_inverted(brush, this->stroke_mode);
}
float2 GreasePencilStrokeOperationCommon::mouse_delta(const InputSample &input_sample) const
{
return input_sample.mouse_position - this->prev_mouse_position;
}
void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
const bContext &C, FunctionRef<bool(const GreasePencilStrokeParams &params)> fn) const
{
using namespace blender::bke::greasepencil;
const Scene &scene = *CTX_data_scene(&C);
const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
const View3D &view3d = *CTX_wm_view3d(&C);
const ARegion &region = *CTX_wm_region(&C);
Object &object = *CTX_data_active_object(&C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
std::atomic<bool> changed = false;
const Vector<MutableDrawingInfo> drawings = get_drawings_for_sculpt(C);
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
scene,
depsgraph,
region,
view3d,
object,
info.layer_index,
info.frame_number,
info.multi_frame_falloff,
info.drawing);
if (fn(params)) {
changed = true;
}
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
}
}
void GreasePencilStrokeOperationCommon::init_stroke(const bContext &C,
const InputSample &start_sample)
{
Paint &paint = *BKE_paint_get_active_from_context(&C);
Brush &brush = *BKE_paint_brush(&paint);
init_brush(brush);
this->prev_mouse_position = start_sample.mouse_position;
}
void GreasePencilStrokeOperationCommon::stroke_extended(const InputSample &extension_sample)
{
this->prev_mouse_position = extension_sample.mouse_position;
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,233 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "BLI_index_mask.hh"
#include "BLI_math_matrix.hh"
#include "BLI_task.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_grease_pencil_types.h"
#include "DNA_view3d_types.h"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class GrabOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
using MutableDrawingInfo = blender::ed::greasepencil::MutableDrawingInfo;
using DrawingPlacement = ed::greasepencil::DrawingPlacement;
/* Cached point mask and influence for a particular drawing. */
struct PointWeights {
int layer_index;
int frame_number;
float multi_frame_falloff;
/* Layer space to view space projection at the start of the stroke. */
float4x4 layer_to_win;
/* Points that are grabbed at the beginning of the stroke. */
IndexMask point_mask;
/* Influence weights for grabbed points. */
Vector<float> weights;
IndexMaskMemory memory;
};
/* Cached point data for each affected drawing. */
Array<PointWeights> drawing_data;
void foreach_grabbed_drawing(const bContext &C,
FunctionRef<bool(const GreasePencilStrokeParams &params,
const IndexMask &mask,
Span<float> weights)> fn) const;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void GrabOperation::foreach_grabbed_drawing(
const bContext &C,
FunctionRef<bool(
const GreasePencilStrokeParams &params, const IndexMask &mask, Span<float> weights)> fn)
const
{
const Scene &scene = *CTX_data_scene(&C);
const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
const ARegion &region = *CTX_wm_region(&C);
const View3D &view3d = *CTX_wm_view3d(&C);
Object &object = *CTX_data_active_object(&C);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
bool changed = false;
threading::parallel_for_each(this->drawing_data.index_range(), [&](const int i) {
const PointWeights &data = this->drawing_data[i];
if (data.point_mask.is_empty()) {
return;
}
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[data.layer_index];
/* If a new frame is created, could be impossible find the stroke. */
const int drawing_index = layer.drawing_index_at(data.frame_number);
if (drawing_index < 0) {
return;
}
GreasePencilDrawingBase &drawing_base = *grease_pencil.drawing(drawing_index);
if (drawing_base.type != GP_DRAWING) {
return;
}
bke::greasepencil::Drawing &drawing =
reinterpret_cast<GreasePencilDrawing &>(drawing_base).wrap();
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
scene,
depsgraph,
region,
view3d,
object,
data.layer_index,
data.frame_number,
data.multi_frame_falloff,
drawing);
if (fn(params, data.point_mask, data.weights)) {
changed = true;
}
});
if (changed) {
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
}
}
void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
const ARegion &region = *CTX_wm_region(&C);
const View3D &view3d = *CTX_wm_view3d(&C);
const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
Brush &brush = *BKE_paint_brush(&paint);
const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
Object &ob_orig = *CTX_data_active_object(&C);
Object &ob_eval = *DEG_get_evaluated_object(&depsgraph, &ob_orig);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
init_brush(brush);
this->prev_mouse_position = start_sample.mouse_position;
const Vector<MutableDrawingInfo> drawings = get_drawings_for_sculpt(C);
this->drawing_data.reinitialize(drawings.size());
threading::parallel_for_each(drawings.index_range(), [&](const int i) {
const MutableDrawingInfo &info = drawings[i];
BLI_assert(info.layer_index >= 0);
PointWeights &data = this->drawing_data[i];
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[info.layer_index];
BLI_assert(layer.drawing_index_at(info.frame_number) >= 0);
BLI_assert(grease_pencil.get_drawing_at(layer, info.frame_number) == &info.drawing);
ed::greasepencil::DrawingPlacement placement(scene, region, view3d, ob_eval, layer);
GreasePencilStrokeParams params = {*scene.toolsettings,
region,
ob_orig,
ob_eval,
layer,
info.layer_index,
info.frame_number,
info.multi_frame_falloff,
std::move(placement),
info.drawing};
IndexMaskMemory selection_memory;
IndexMask selection = point_selection_mask(params, selection_memory);
Array<float2> view_positions = calculate_view_positions(params, selection);
/* Cache points under brush influence. */
Vector<float> weights;
IndexMask point_mask = brush_influence_mask(scene,
brush,
start_sample.mouse_position,
start_sample.pressure,
info.multi_frame_falloff,
selection,
view_positions,
weights,
data.memory);
if (point_mask.is_empty()) {
/* Set empty point mask to skip. */
data.point_mask = {};
return;
}
data.layer_index = info.layer_index;
data.frame_number = info.frame_number;
data.multi_frame_falloff = info.multi_frame_falloff;
data.layer_to_win = ED_view3d_ob_project_mat_get(&rv3d, &ob_eval) *
layer.to_object_space(ob_eval);
data.point_mask = std::move(point_mask);
data.weights = std::move(weights);
});
}
void GrabOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const ARegion &region = *CTX_wm_region(&C);
const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
this->foreach_grabbed_drawing(
C,
[&](const GreasePencilStrokeParams &params,
const IndexMask &mask,
const Span<float> weights) {
/* Crazyspace deformation. */
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
/* Transform mouse delta into layer space. */
const float2 mouse_delta_win = this->mouse_delta(extension_sample);
const float3 layer_origin = params.layer.to_world_space(params.ob_eval).location();
const float zfac = ED_view3d_calc_zfac(&rv3d, layer_origin);
float3 mouse_delta;
ED_view3d_win_to_delta(&region, mouse_delta_win, zfac, mouse_delta);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
MutableSpan<float3> positions = curves.positions_for_write();
mask.foreach_index(GrainSize(1024), [&](const int point_i, const int index) {
/* Translate the point with the influence factor. */
const float3 new_pos_layer = deformation.positions[point_i] +
mouse_delta * weights[index];
const float3 new_pos_world = math::transform_point(
params.layer.to_world_space(params.ob_eval), new_pos_layer);
float2 new_pos_view;
ED_view3d_project_float_global(&region, new_pos_world, new_pos_view, V3D_PROJ_TEST_NOP);
positions[point_i] = params.placement.project(new_pos_view);
});
params.drawing.tag_positions_changed();
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_grab_operation(const BrushStrokeMode stroke_mode)
{
return std::make_unique<GrabOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,79 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class PinchOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void PinchOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const bool invert = this->is_inverted(brush);
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
MutableSpan<float3> positions = curves.positions_for_write();
const float2 target = extension_sample.mouse_position;
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float scale_offset = influence * influence / 25.0f;
const float scale = invert ? 1.0 + scale_offset : 1.0f - scale_offset;
positions[point_i] = params.placement.project(target + (co - target) * scale);
});
params.drawing.tag_positions_changed();
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_pinch_operation(const BrushStrokeMode stroke_mode)
{
return std::make_unique<PinchOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,76 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class PushOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void PushOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void PushOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
MutableSpan<float3> positions = curves.positions_for_write();
const float2 mouse_delta = this->mouse_delta(extension_sample);
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
positions[point_i] = params.placement.project(co + mouse_delta * influence);
});
params.drawing.tag_positions_changed();
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_push_operation(const BrushStrokeMode stroke_mode)
{
return std::make_unique<PushOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,153 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_hash.h"
#include "BLI_math_vector.hh"
#include "BLI_rand.hh"
#include "BLI_task.hh"
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
/* Use a hash to generate random numbers. */
static float hash_rng(uint32_t seed1, uint32_t seed2, int index)
{
return BLI_hash_int_01(BLI_hash_int_3d(seed1, seed2, uint32_t(index)));
}
class RandomizeOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
/* Get a different seed value for each stroke. */
uint32_t unique_seed() const;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
uint32_t RandomizeOperation::unique_seed() const
{
return RandomNumberGenerator::from_random_seed().get_uint32();
}
void RandomizeOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
const uint32_t seed = this->unique_seed();
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
bool changed = false;
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
MutableSpan<float3> positions = curves.positions_for_write();
/* Jitter is applied perpendicular to the mouse movement vector. */
const float2 forward = math::normalize(this->mouse_delta(extension_sample));
const float2 sideways = float2(-forward.y, forward.x);
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float noise = 2.0f * hash_rng(seed, 5678, point_i) - 1.0f;
positions[point_i] = params.placement.project(co + sideways * influence * noise);
});
params.drawing.tag_positions_changed();
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
MutableSpan<float> opacities = params.drawing.opacities_for_write();
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
opacities[point_i] = math::clamp(opacities[point_i] + influence * noise, 0.0f, 1.0f);
});
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
const MutableSpan<float> radii = params.drawing.radii_for_write();
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
radii[point_i] = math::max(radii[point_i] + influence * noise * 0.001f, 0.0f);
});
curves.tag_radii_changed();
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
"rotation", bke::AttrDomain::Point);
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float noise = 2.0f * hash_rng(seed, 1212, point_i) - 1.0f;
rotations.span[point_i] = math::clamp(
rotations.span[point_i] + influence * noise, -float(M_PI_2), float(M_PI_2));
});
rotations.finish();
changed = true;
}
return changed;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_randomize_operation(
const BrushStrokeMode stroke_mode)
{
return std::make_unique<RandomizeOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,140 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_attribute.hh"
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "BLI_virtual_array.hh"
#include "DNA_brush_enums.h"
#include "GEO_smooth_curves.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class SmoothOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void SmoothOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void SmoothOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
const OffsetIndices points_by_curve = curves.points_by_curve();
const VArray<bool> cyclic = curves.cyclic();
const int iterations = 2;
const VArray<float> influences = VArray<float>::ForFunc(
view_positions.size(), [&](const int64_t point_) {
return brush_influence(
scene, brush, view_positions[point_], extension_sample, params.multi_frame_falloff);
});
Array<bool> selection_array(curves.points_num());
selection.to_bools(selection_array);
const VArray<bool> selection_varray = VArray<bool>::ForSpan(selection_array);
bool changed = false;
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
MutableSpan<float3> positions = curves.positions_for_write();
geometry::smooth_curve_attribute(curves.curves_range(),
points_by_curve,
selection_varray,
cyclic,
iterations,
influences,
false,
false,
positions);
params.drawing.tag_positions_changed();
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_STRENGTH) {
MutableSpan<float> opacities = params.drawing.opacities_for_write();
geometry::smooth_curve_attribute(curves.curves_range(),
points_by_curve,
selection_varray,
cyclic,
iterations,
influences,
true,
false,
opacities);
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_THICKNESS) {
const MutableSpan<float> radii = params.drawing.radii_for_write();
geometry::smooth_curve_attribute(curves.curves_range(),
points_by_curve,
selection_varray,
cyclic,
iterations,
influences,
true,
false,
radii);
curves.tag_radii_changed();
changed = true;
}
if (sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_UV) {
bke::SpanAttributeWriter<float> rotations = attributes.lookup_or_add_for_write_span<float>(
"rotation", bke::AttrDomain::Point);
geometry::smooth_curve_attribute(curves.curves_range(),
points_by_curve,
selection_varray,
cyclic,
iterations,
influences,
true,
false,
rotations.span);
rotations.finish();
changed = true;
}
return changed;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_smooth_operation(
const BrushStrokeMode stroke_mode)
{
return std::make_unique<SmoothOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,73 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_task.hh"
#include "BKE_context.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class StrengthOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void StrengthOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void StrengthOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const bool invert = this->is_inverted(brush);
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
MutableSpan<float> opacities = params.drawing.opacities_for_write();
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
float &opacity = opacities[point_i];
const float influence = brush_influence(
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
/* Brush influence mapped to opacity by a factor of 0.125. */
const float delta_opacity = (invert ? -influence : influence) * 0.125f;
opacity = std::clamp(opacity + delta_opacity, 0.0f, 1.0f);
});
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_strength_operation(
const BrushStrokeMode stroke_mode)
{
return std::make_unique<StrengthOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,77 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_task.hh"
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class ThicknessOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
void ThicknessOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void ThicknessOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const bool invert = this->is_inverted(brush);
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
BLI_assert(view_positions.size() == curves.points_num());
MutableSpan<float> radii = params.drawing.radii_for_write();
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
float &radius = radii[point_i];
const float influence = brush_influence(
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
/* Factor 1/1000 is used to map arbitrary influence value to a sensible radius. */
const float delta_radius = (invert ? -influence : influence) * 0.001f;
radius = std::max(radius + delta_radius, 0.0f);
});
curves.tag_radii_changed();
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_thickness_operation(
const BrushStrokeMode stroke_mode)
{
return std::make_unique<ThicknessOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -0,0 +1,86 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_paint.hh"
#include "ED_grease_pencil.hh"
#include "ED_view3d.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint::greasepencil {
class TwistOperation : public GreasePencilStrokeOperationCommon {
public:
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
void on_stroke_done(const bContext & /*C*/) override {}
};
static float2 rotate_by_angle(const float2 &vec, const float angle)
{
const float cos_angle = math::cos(angle);
const float sin_angle = math::sin(angle);
return float2(vec.x * cos_angle - vec.y * sin_angle, vec.x * sin_angle + vec.y * cos_angle);
}
void TwistOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
{
this->init_stroke(C, start_sample);
}
void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
{
const Scene &scene = *CTX_data_scene(&C);
Paint &paint = *BKE_paint_get_active_from_context(&C);
const Brush &brush = *BKE_paint_brush(&paint);
const bool invert = this->is_inverted(brush);
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams &params) {
IndexMaskMemory selection_memory;
const IndexMask selection = point_selection_mask(params, selection_memory);
if (selection.is_empty()) {
return false;
}
Array<float2> view_positions = calculate_view_positions(params, selection);
bke::CurvesGeometry &curves = params.drawing.strokes_for_write();
MutableSpan<float3> positions = curves.positions_for_write();
const float2 mouse_pos = extension_sample.mouse_position;
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
const float2 &co = view_positions[point_i];
const float influence = brush_influence(
scene, brush, co, extension_sample, params.multi_frame_falloff);
if (influence <= 0.0f) {
return;
}
const float angle = DEG2RADF(invert ? -1.0f : 1.0f) * influence;
positions[point_i] = params.placement.project(rotate_by_angle(co - mouse_pos, angle) +
mouse_pos);
});
params.drawing.tag_positions_changed();
return true;
});
this->stroke_extended(extension_sample);
}
std::unique_ptr<GreasePencilStrokeOperation> new_twist_operation(const BrushStrokeMode stroke_mode)
{
return std::make_unique<TwistOperation>(stroke_mode);
}
} // namespace blender::ed::sculpt_paint::greasepencil

View File

@ -1686,8 +1686,8 @@ static void paint_cursor_pose_brush_segments_draw(PaintCursorContext *pcontext)
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
GPU_line_width(2.0f);
immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->tot_segments * 2);
for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->segments.size() * 2);
for (const int i : ss->pose_ik_chain_preview->segments.index_range()) {
immVertex3fv(pcontext->pos, ss->pose_ik_chain_preview->segments[i].initial_orig);
immVertex3fv(pcontext->pos, ss->pose_ik_chain_preview->segments[i].initial_head);
}
@ -1700,7 +1700,7 @@ static void paint_cursor_pose_brush_origins_draw(PaintCursorContext *pcontext)
SculptSession *ss = pcontext->ss;
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
for (const int i : ss->pose_ik_chain_preview->segments.index_range()) {
cursor_draw_point_screen_space(pcontext->pos,
pcontext->region,
ss->pose_ik_chain_preview->segments[i].initial_orig,
@ -1813,7 +1813,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
/* Free the previous pose brush preview. */
if (ss->pose_ik_chain_preview) {
pose::ik_chain_free(ss->pose_ik_chain_preview);
ss->pose_ik_chain_preview.reset();
}
/* Generate a new pose brush preview from the current cursor location. */

View File

@ -1704,8 +1704,19 @@ static void vpaint_do_draw(bContext *C,
float tex_alpha = 1.0;
if (vpd->is_texbrush) {
/* NOTE: we may want to paint alpha as vertex color alpha. */
tex_alpha = paint_and_tex_color_alpha<Color>(
vp, vpd, vpd->vertexcosnos[v_index].co, &color_final);
/* If the active area is being applied for symmetry, flip it
* across the symmetry axis and rotate it back to the original
* position in order to project it. This insures that the
* brush texture will be oriented correctly.
* This is the method also used in #sculpt_apply_texture(). */
float symm_point[3];
if (cache->radial_symmetry_pass) {
mul_m4_v3(cache->symm_rot_mat_inv.ptr(), vpd->vertexcosnos[v_index].co);
}
flip_v3_v3(symm_point, vpd->vertexcosnos[v_index].co, cache->mirror_symmetry_pass);
tex_alpha = paint_and_tex_color_alpha<Color>(vp, vpd, symm_point, &color_final);
}
Color color_orig(0, 0, 0, 0);
@ -1858,8 +1869,7 @@ static void vpaint_do_radial_symmetry(bContext *C,
}
}
/* near duplicate of: sculpt.cc's,
* 'do_symmetrical_brush_actions' and 'wpaint_do_symmetrical_brush_actions'. */
/* near duplicate of: #do_symmetrical_brush_actions and #wpaint_do_symmetrical_brush_actions. */
static void vpaint_do_symmetrical_brush_actions(bContext *C,
VPaint *vp,
VPaintData *vpd,
@ -1870,40 +1880,26 @@ static void vpaint_do_symmetrical_brush_actions(bContext *C,
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
int i = 0;
/* initial stroke */
const ePaintSymmetryFlags initial_symm = ePaintSymmetryFlags(0);
cache->mirror_symmetry_pass = ePaintSymmetryFlags(0);
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, initial_symm, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'X');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'Y');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'Z');
cache->symmetry = symm;
/* symm is a bit combination of XYZ - 1 is mirror
* X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 1; i <= symm; i++) {
if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) {
const ePaintSymmetryFlags symm_pass = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm_pass;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
/* symm is a bit combination of XYZ -
* 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (int i = 0; i <= symm; i++) {
if (i & (1 << 0)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'X');
}
if (i & (1 << 1)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y');
}
if (i & (1 << 2)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z');
}
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
const ePaintSymmetryFlags symm_pass = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm_pass;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'X');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z');
}
copy_v3_v3(cache->true_last_location, cache->true_location);

View File

@ -1726,8 +1726,7 @@ static void wpaint_do_radial_symmetry(bContext *C,
}
}
/* near duplicate of: sculpt.cc's,
* 'do_symmetrical_brush_actions' and 'vpaint_do_symmetrical_brush_actions'. */
/* near duplicate of: #do_symmetrical_brush_actions and #vpaint_do_symmetrical_brush_actions. */
static void wpaint_do_symmetrical_brush_actions(
bContext *C, Object *ob, VPaint *wp, WPaintData *wpd, WeightPaintInfo *wpi)
{
@ -1736,14 +1735,6 @@ static void wpaint_do_symmetrical_brush_actions(
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
int i = 0;
/* initial stroke */
cache->mirror_symmetry_pass = ePaintSymmetryFlags(0);
wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X', 0, 0);
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'X');
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Y');
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, ePaintSymmetryFlags(0), 'Z');
cache->symmetry = symm;
@ -1754,28 +1745,22 @@ static void wpaint_do_symmetrical_brush_actions(
return;
}
/* symm is a bit combination of XYZ - 1 is mirror
* X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 1; i <= symm; i++) {
if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) {
const ePaintSymmetryFlags symm = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0);
if (i & (1 << 0)) {
wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X', 0, 0);
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X');
}
if (i & (1 << 1)) {
wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y', 0, 0);
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y');
}
if (i & (1 << 2)) {
wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z', 0, 0);
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z');
}
/* symm is a bit combination of XYZ -
* 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (int i = 0; i <= symm; i++) {
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
const ePaintSymmetryFlags symm = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm, 0, 0);
wpaint_do_paint(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X', 0, 0);
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'X');
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Y');
wpaint_do_radial_symmetry(C, ob, wp, wpd, wpi, mesh, brush, symm, 'Z');
}
copy_v3_v3(cache->true_last_location, cache->true_location);
cache->is_last_valid = true;

View File

@ -379,7 +379,7 @@ namespace blender::ed::sculpt_paint {
namespace face_set {
int active_face_set_get(SculptSession *ss)
int active_face_set_get(const SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@ -429,7 +429,7 @@ bool vert_visible_get(const SculptSession *ss, PBVHVertRef vertex)
return true;
}
bool vert_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex)
bool vert_any_face_visible_get(const SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@ -506,7 +506,7 @@ bool vert_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex)
namespace face_set {
int vert_face_set_get(SculptSession *ss, PBVHVertRef vertex)
int vert_face_set_get(const SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@ -536,7 +536,7 @@ int vert_face_set_get(SculptSession *ss, PBVHVertRef vertex)
return 0;
}
bool vert_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set)
bool vert_has_face_set(const SculptSession *ss, PBVHVertRef vertex, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@ -565,7 +565,7 @@ bool vert_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set)
return true;
}
static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, int index)
{
if (!ss->face_sets) {
return true;
@ -588,7 +588,9 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind
* Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2
* in the base mesh are equal.
*/
static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2)
static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSession *ss,
int v1,
int v2)
{
const Span<int> vert_map = ss->vert_to_face_map[v1];
int p1 = -1, p2 = -1;
@ -615,7 +617,7 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss
return true;
}
bool vert_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex)
bool vert_has_unique_face_set(const SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@ -718,7 +720,7 @@ static void sculpt_vertex_neighbors_get_bmesh(PBVHVertRef vertex, SculptVertexNe
}
}
static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
static void sculpt_vertex_neighbors_get_faces(const SculptSession *ss,
PBVHVertRef vertex,
SculptVertexNeighborIter *iter)
{
@ -753,7 +755,7 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
}
}
static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss,
const PBVHVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
@ -797,7 +799,7 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
} // namespace blender::ed::sculpt_paint
void SCULPT_vertex_neighbors_get(SculptSession *ss,
void SCULPT_vertex_neighbors_get(const SculptSession *ss,
const PBVHVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
@ -1008,10 +1010,12 @@ namespace blender::ed::sculpt_paint {
namespace flood_fill {
void init_fill(SculptSession *ss, FillData *flood)
FillData init_fill(SculptSession *ss)
{
SCULPT_vertex_random_access_ensure(ss);
flood->visited_verts.resize(SCULPT_vertex_count_get(ss));
FillData data;
data.visited_verts.resize(SCULPT_vertex_count_get(ss));
return data;
}
void add_initial(FillData *flood, PBVHVertRef vertex)
@ -1078,11 +1082,9 @@ void add_active(Object *ob, SculptSession *ss, FillData *flood, float radius)
}
}
void execute(
SculptSession *ss,
FillData *flood,
FunctionRef<bool(SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate)>
func)
void execute(SculptSession *ss,
FillData *flood,
FunctionRef<bool(PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate)> func)
{
while (!flood->queue.empty()) {
PBVHVertRef from_v = flood->queue.front();
@ -1103,7 +1105,7 @@ void execute(
flood->visited_verts[BKE_pbvh_vertex_to_index(ss->pbvh, to_v)].set();
if (func(ss, from_v, to_v, ni.is_duplicate)) {
if (func(from_v, to_v, ni.is_duplicate)) {
flood->queue.push(to_v);
}
}
@ -3999,6 +4001,8 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob)
}
}
/* near duplicate of: #wpaint_do_symmetrical_brush_actions and
* #vpaint_do_symmetrical_brush_actions. */
static void do_symmetrical_brush_actions(Sculpt *sd,
Object *ob,
BrushActionFunc action,
@ -4142,10 +4146,6 @@ void SCULPT_cache_free(blender::ed::sculpt_paint::StrokeCache *cache)
MEM_SAFE_FREE(cache->prev_displacement);
MEM_SAFE_FREE(cache->limit_surface_co);
if (cache->pose_ik_chain) {
pose::ik_chain_free(cache->pose_ik_chain);
}
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
if (cache->boundaries[i]) {
boundary::data_free(cache->boundaries[i]);
@ -6142,6 +6142,7 @@ bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool origi
srd.ray_normal = ray_normal;
srd.depth = depth;
srd.face_normal = face_normal;
srd.corner_verts = ss->corner_verts;
isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal);
bke::pbvh::raycast(

View File

@ -626,8 +626,7 @@ static void topology_automasking_init(const Sculpt *sd, Object *ob)
/* Flood fill automask to connected vertices. Limited to vertices inside
* the brush radius if the tool requires it. */
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
flood_fill::add_active(ob, ss, &flood, radius);
@ -639,9 +638,7 @@ static void topology_automasking_init(const Sculpt *sd, Object *ob)
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
flood_fill::execute(
ss,
&flood,
[&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool /*is_duplicate*/) {
ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool /*is_duplicate*/) {
return floodfill_cb(ss, from_v, to_v, &fdata);
});
}

View File

@ -87,8 +87,7 @@ static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss
return initial_vertex;
}
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
flood_fill::add_initial(&flood, initial_vertex);
BoundaryInitialVertexFloodFillData fdata{};
@ -99,10 +98,9 @@ static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss
fdata.floodfill_steps = MEM_cnew_array<int>(SCULPT_vertex_count_get(ss), __func__);
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return boundary_initial_vertex_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return boundary_initial_vertex_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
MEM_freeN(fdata.floodfill_steps);
return fdata.boundary_initial_vertex;
@ -247,8 +245,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
MEM_malloc_arrayN(BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), __func__));
GSet *included_verts = BLI_gset_int_new_ex("included verts", BOUNDARY_INDICES_BLOCK_SIZE);
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
int initial_boundary_index = BKE_pbvh_vertex_to_index(ss->pbvh, initial_boundary_vertex);
@ -266,10 +263,9 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
fdata.included_verts = included_verts;
fdata.last_visited_vertex = {BOUNDARY_VERTEX_NONE};
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return boundary_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return boundary_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
/* Check if the boundary loops into itself and add the extra preview edge to close the loop. */
if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE &&

View File

@ -493,14 +493,12 @@ static Array<float> sculpt_expand_topology_falloff_create(Object *ob, const PBVH
const int totvert = SCULPT_vertex_count_get(ss);
Array<float> dists(totvert, 0.0f);
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
flood_fill::add_initial_with_symmetry(ob, ss, &flood, v, FLT_MAX);
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return expand_topology_floodfill_cb(ss, from_v, to_v, is_duplicate, dists);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return expand_topology_floodfill_cb(ss, from_v, to_v, is_duplicate, dists);
});
return dists;
}
@ -548,8 +546,7 @@ static Array<float> sculpt_expand_normal_falloff_create(Object *ob,
Array<float> dists(totvert, 0.0f);
Array<float> edge_factor(totvert, 1.0f);
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
flood_fill::add_initial_with_symmetry(ob, ss, &flood, v, FLT_MAX);
ExpandFloodFillData fdata;
@ -558,10 +555,9 @@ static Array<float> sculpt_expand_normal_falloff_create(Object *ob,
fdata.edge_sensitivity = edge_sensitivity;
SCULPT_vertex_normal_get(ss, v, fdata.original_normal);
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return mask_expand_normal_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return mask_expand_normal_floodfill_cb(ss, from_v, to_v, is_duplicate, &fdata);
});
for (int repeat = 0; repeat < blur_steps; repeat++) {
for (int i = 0; i < totvert; i++) {
@ -901,8 +897,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
expand_cache->vert_falloff.fill(0);
const BitVector<> boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false);
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
for (int i = 0; i < totvert; i++) {
if (!boundary_verts[i]) {
continue;
@ -913,10 +908,9 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
}
MutableSpan<float> dists = expand_cache->vert_falloff;
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return expand_topology_floodfill_cb(ss, from_v, to_v, is_duplicate, dists);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return expand_topology_floodfill_cb(ss, from_v, to_v, is_duplicate, dists);
});
}
/**

View File

@ -476,7 +476,7 @@ struct StrokeCache {
} paint_brush;
/* Pose brush */
SculptPoseIKChain *pose_ik_chain;
std::unique_ptr<SculptPoseIKChain> pose_ik_chain;
/* Enhance Details. */
float (*detail_directions)[3];
@ -884,7 +884,7 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
int deform_target,
PBVHVertexIter *iter);
void SCULPT_vertex_neighbors_get(SculptSession *ss,
void SCULPT_vertex_neighbors_get(const SculptSession *ss,
PBVHVertRef vertex,
bool include_duplicates,
SculptVertexNeighborIter *iter);
@ -952,7 +952,7 @@ namespace hide {
bool vert_visible_get(const SculptSession *ss, PBVHVertRef vertex);
bool vert_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex);
bool vert_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex);
bool vert_any_face_visible_get(const SculptSession *ss, PBVHVertRef vertex);
}
@ -964,11 +964,11 @@ bool vert_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex);
namespace face_set {
int active_face_set_get(SculptSession *ss);
int vert_face_set_get(SculptSession *ss, PBVHVertRef vertex);
int active_face_set_get(const SculptSession *ss);
int vert_face_set_get(const SculptSession *ss, PBVHVertRef vertex);
bool vert_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set);
bool vert_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex);
bool vert_has_face_set(const SculptSession *ss, PBVHVertRef vertex, int face_set);
bool vert_has_unique_face_set(const SculptSession *ss, PBVHVertRef vertex);
bke::SpanAttributeWriter<int> ensure_face_sets_mesh(Object &object);
int ensure_face_sets_bmesh(Object &object);
@ -1174,17 +1174,15 @@ struct FillData {
blender::BitVector<> visited_verts;
};
void init_fill(SculptSession *ss, FillData *flood);
FillData init_fill(SculptSession *ss);
void add_active(Object *ob, SculptSession *ss, FillData *flood, float radius);
void add_initial_with_symmetry(
Object *ob, SculptSession *ss, FillData *flood, PBVHVertRef vertex, float radius);
void add_initial(FillData *flood, PBVHVertRef vertex);
void add_and_skip_initial(FillData *flood, PBVHVertRef vertex);
void execute(
SculptSession *ss,
FillData *flood,
FunctionRef<bool(SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate)>
func);
void execute(SculptSession *ss,
FillData *flood,
FunctionRef<bool(PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate)> func);
}
@ -1891,15 +1889,14 @@ void do_pose_brush(Sculpt *sd, Object *ob, blender::Span<PBVHNode *> nodes);
*/
void calc_pose_data(Object *ob,
SculptSession *ss,
float initial_location[3],
const float3 &initial_location,
float radius,
float pose_offset,
float *r_pose_origin,
float *r_pose_factor);
float3 &r_pose_origin,
MutableSpan<float> r_pose_factor);
void pose_brush_init(Object *ob, SculptSession *ss, Brush *br);
SculptPoseIKChain *ik_chain_init(
Object *ob, SculptSession *ss, Brush *br, const float initial_location[3], float radius);
void ik_chain_free(SculptPoseIKChain *ik_chain);
std::unique_ptr<SculptPoseIKChain> ik_chain_init(
Object *ob, SculptSession *ss, Brush *br, const float3 &initial_location, float radius);
}

View File

@ -826,8 +826,7 @@ static void sculpt_mask_by_color_contiguous(Object *object,
}
}
flood_fill::FillData flood;
flood_fill::init_fill(ss, &flood);
flood_fill::FillData flood = flood_fill::init_fill(ss);
flood_fill::add_initial(&flood, vertex);
MaskByColorContiguousFloodFillData ffd;
@ -840,10 +839,9 @@ static void sculpt_mask_by_color_contiguous(Object *object,
copy_v3_v3(ffd.initial_color, color);
flood_fill::execute(
ss, &flood, [&](SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return sculpt_mask_by_color_contiguous_floodfill(ss, from_v, to_v, is_duplicate, &ffd);
});
flood_fill::execute(ss, &flood, [&](PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate) {
return sculpt_mask_by_color_contiguous_floodfill(ss, from_v, to_v, is_duplicate, &ffd);
});
Vector<PBVHNode *> nodes = blender::bke::pbvh::search_gather(ss->pbvh, {});
const SculptMaskWriteInfo mask_write = SCULPT_mask_get_for_write(ss);

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@ void uiTemplateMarker(uiLayout *layout,
0,
0,
"");
MEM_freeN(cb);
return;
}

View File

@ -2744,9 +2744,10 @@ int ED_path_extension_type(const char *path)
{
return FILE_TYPE_TEXT;
}
if (BLI_path_extension_check_n(
path, ".ttf", ".ttc", ".pfb", ".otf", ".otc", ".woff", ".woff2", nullptr))
{
/* NOTE: While `.ttc` & `.otc` files can be loaded, only a single "face" is supported,
* users will have to extract bold/italic etc manually for Blender to use them, see #44254. */
if (BLI_path_extension_check_n(path, ".ttf", ".pfb", ".otf", ".woff", ".woff2", nullptr)) {
return FILE_TYPE_FTFONT;
}
if (BLI_path_extension_check(path, ".btx")) {

View File

@ -1030,6 +1030,7 @@ static int ease_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_slider_unit_set(gso->slider, "%");
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
}
ED_slider_property_label_set(gso->slider, RNA_property_ui_name(gso->factor_prop));
ease_modal_update(C, op);
break;
}
@ -1055,6 +1056,7 @@ static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ED_slider_allow_overshoot_set(gso->slider, false, false);
ED_slider_factor_bounds_set(gso->slider, -1, 1);
ED_slider_factor_set(gso->slider, 0.0f);
ED_slider_property_label_set(gso->slider, RNA_property_ui_name(gso->factor_prop));
return invoke_result;
}

View File

@ -90,7 +90,8 @@ struct CurvesTransformData {
/**
* The offsets of every grease pencil layer into `positions` array.
* For curves only one layer is used.
* For curves layers are used to store: positions, handle_positions_left and
* handle_positions_right.
*/
blender::Vector<int> layer_offsets;
@ -171,15 +172,15 @@ void animrecord_check_state(TransInfo *t, ID *id);
/**
* Used for both curves and grease pencil objects.
*/
void curve_populate_trans_data_structs(TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &matrix,
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::IndexMask &selected_indices,
bool use_proportional_edit,
const blender::IndexMask &affected_curves,
bool use_connected_only,
int trans_data_offset);
void curve_populate_trans_data_structs(
TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &transform,
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::Span<blender::IndexMask> points_to_transform_indices,
const blender::IndexMask &affected_curves,
bool use_connected_only,
const blender::IndexMask &bezier_curves);
CurvesTransformData *create_curves_transform_custom_data(TransCustomData &custom_data);

View File

@ -16,6 +16,7 @@
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_curves_utils.hh"
#include "ED_curves.hh"
@ -63,9 +64,9 @@ static void calculate_curve_point_distances_for_proportional_editing(
}
}
static void append_positions_to_custom_data(const IndexMask selection,
Span<float3> positions,
TransCustomData &custom_data)
static MutableSpan<float3> append_positions_to_custom_data(const IndexMask selection,
Span<float3> positions,
TransCustomData &custom_data)
{
CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(custom_data.data);
transform_data.selection_by_layer.append(selection);
@ -75,32 +76,101 @@ static void append_positions_to_custom_data(const IndexMask selection,
positions,
selection,
transform_data.positions.as_mutable_span().slice(data_offset, selection.size()));
return transform_data.positions.as_mutable_span().slice(transform_data.layer_offsets.last(1),
selection.size());
}
static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
{
MutableSpan<TransDataContainer> trans_data_contrainers(t->data_container, t->data_container_len);
Array<IndexMask> selection_per_object(t->data_container_len);
Array<Vector<IndexMask>> points_to_transform_per_attribute(t->data_container_len);
Array<IndexMask> bezier_curves(t->data_container_len);
const bool use_proportional_edit = (t->flag & T_PROP_EDIT_ALL) != 0;
const bool use_connected_only = (t->flag & T_PROP_CONNECTED) != 0;
Vector<int> must_be_selected;
/* Count selected elements per object and create TransData structs. */
for (const int i : trans_data_contrainers.index_range()) {
TransDataContainer &tc = trans_data_contrainers[i];
Curves *curves_id = static_cast<Curves *>(tc.obedit->data);
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
CurvesTransformData *curves_transform_data = create_curves_transform_custom_data(
tc.custom.type);
Span<StringRef> selection_attribute_names = ed::curves::get_curves_selection_attribute_names(
curves);
std::array<IndexMask, 3> selection_per_attribute;
for (const int attribute_i : selection_attribute_names.index_range()) {
const StringRef &selection_name = selection_attribute_names[attribute_i];
selection_per_attribute[attribute_i] = ed::curves::retrieve_selected_points(
curves, selection_name, curves_transform_data->memory);
}
bezier_curves[i] = bke::curves::indices_for_type(curves.curve_types(),
curves.curve_type_counts(),
CURVE_TYPE_BEZIER,
curves.curves_range(),
curves_transform_data->memory);
/* Alter selection as in legacy curves bezt_select_to_transform_triple_flag(). */
if (!bezier_curves[i].is_empty()) {
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<int8_t> handle_types_left = curves.handle_types_left();
const VArray<int8_t> handle_types_right = curves.handle_types_right();
must_be_selected.clear();
bezier_curves[i].foreach_index([&](const int bezier_index) {
for (const int point_i : points_by_curve[bezier_index]) {
if (selection_per_attribute[0].contains(point_i)) {
const HandleType type_left = HandleType(handle_types_left[point_i]);
const HandleType type_right = HandleType(handle_types_right[point_i]);
if (ELEM(type_left, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_ALIGN) &&
ELEM(type_right, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_ALIGN))
{
must_be_selected.append(point_i);
}
}
}
});
/* Select bezier handles that must be transformed if the main control point is selected. */
IndexMask must_be_selected_mask = IndexMask::from_indices(must_be_selected.as_span(),
curves_transform_data->memory);
if (must_be_selected.size()) {
selection_per_attribute[1] = IndexMask::from_union(
selection_per_attribute[1], must_be_selected_mask, curves_transform_data->memory);
selection_per_attribute[2] = IndexMask::from_union(
selection_per_attribute[2], must_be_selected_mask, curves_transform_data->memory);
}
}
if (use_proportional_edit) {
selection_per_object[i] = curves.points_range();
tc.data_len = curves.point_num;
Array<int> bezier_point_offset_data(bezier_curves[i].size() + 1);
OffsetIndices<int> bezier_offsets = offset_indices::gather_selected_offsets(
curves.points_by_curve(), bezier_curves[i], bezier_point_offset_data);
const int bezier_point_count = bezier_offsets.total_size();
tc.data_len = curves.points_num() + 2 * bezier_point_count;
points_to_transform_per_attribute[i].append(curves.points_range());
if (bezier_point_count > 0) {
Vector<index_mask::IndexMask::Initializer> bezier_point_ranges;
OffsetIndices<int> points_by_curve = curves.points_by_curve();
bezier_curves[i].foreach_index(GrainSize(512), [&](const int bezier_curve_i) {
bezier_point_ranges.append(points_by_curve[bezier_curve_i]);
});
IndexMask bezier_points = IndexMask::from_initializers(bezier_point_ranges,
curves_transform_data->memory);
points_to_transform_per_attribute[i].append(bezier_points);
points_to_transform_per_attribute[i].append(bezier_points);
}
}
else {
selection_per_object[i] = ed::curves::retrieve_selected_points(
curves, curves_transform_data->memory);
tc.data_len = selection_per_object[i].size();
tc.data_len = 0;
for (const int selection_i : selection_attribute_names.index_range()) {
points_to_transform_per_attribute[i].append(selection_per_attribute[selection_i]);
tc.data_len += points_to_transform_per_attribute[i][selection_i].size();
}
}
if (tc.data_len > 0) {
@ -140,11 +210,10 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t)
curves,
object->object_to_world(),
value_attribute,
selection_per_object[i],
use_proportional_edit,
points_to_transform_per_attribute[i],
curves.curves_range(),
use_connected_only,
0 /* No data offset for curves. */);
bezier_curves[i]);
/* TODO: This is wrong. The attribute writer should live at least as long as the span. */
attribute_writer.finish();
@ -164,8 +233,16 @@ static void recalcData_curves(TransInfo *t)
curves.tag_normals_changed();
}
else {
copy_positions_from_curves_transform_custom_data(
tc.custom.type, 0, curves.positions_for_write());
const std::array<MutableSpan<float3>, 3> positions_per_selection_attr = {
curves.positions_for_write(),
curves.handle_positions_left_for_write(),
curves.handle_positions_right_for_write()};
for (const int selection_i :
ed::curves::get_curves_selection_attribute_names(curves).index_range())
{
copy_positions_from_curves_transform_custom_data(
tc.custom.type, selection_i, positions_per_selection_attr[selection_i]);
}
curves.tag_positions_changed();
curves.calculate_bezier_auto_handles();
}
@ -173,6 +250,45 @@ static void recalcData_curves(TransInfo *t)
}
}
static OffsetIndices<int> recent_position_offsets(TransCustomData &custom_data, int num)
{
const CurvesTransformData &transform_data = *static_cast<CurvesTransformData *>(
custom_data.data);
return OffsetIndices(transform_data.layer_offsets.as_span().slice(
transform_data.layer_offsets.size() - num - 1, num + 1));
}
/**
* Creates map of indices to `tc.data` representing curve in layout
* [L0, P0, R0, L1, P1, R1, L2,P2, R2], where [P0, P1, P2], [L0, L1, L2] and [R0, R1, R2] are
* positions, left handles and right handles respectively.
*/
static void fill_map(const CurveType curve_type,
const IndexRange curve_points,
const OffsetIndices<int> position_offsets_in_td,
const int handles_offset,
MutableSpan<int> map)
{
const int attr_num = (curve_type == CURVE_TYPE_BEZIER) ? 3 : 1;
const int left_handle_index = handles_offset + position_offsets_in_td[1].start();
const int position_index = curve_points.start() + position_offsets_in_td[0].start();
const int right_handle_index = handles_offset + position_offsets_in_td[2].start();
std::array<int, 3> first_per_attr = {curve_type == CURVE_TYPE_BEZIER ? left_handle_index :
position_index,
/* Next two unused for non Bezier curves. */
position_index,
right_handle_index};
threading::parallel_for(curve_points.index_range(), 4096, [&](const IndexRange range) {
for (const int i : range) {
for (const int attr : IndexRange(attr_num)) {
map[i * attr_num + attr] = first_per_attr[attr] + i;
}
}
});
}
} // namespace blender::ed::transform::curves
CurvesTransformData *create_curves_transform_custom_data(TransCustomData &custom_data)
@ -203,108 +319,136 @@ void copy_positions_from_curves_transform_custom_data(
array_utils::scatter(positions, selection, positions_dst);
}
void curve_populate_trans_data_structs(TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &transform,
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::IndexMask &selected_indices,
const bool use_proportional_edit,
const blender::IndexMask &affected_curves,
bool use_connected_only,
int trans_data_offset)
void curve_populate_trans_data_structs(
TransDataContainer &tc,
blender::bke::CurvesGeometry &curves,
const blender::float4x4 &transform,
std::optional<blender::MutableSpan<float>> value_attribute,
const blender::Span<blender::IndexMask> points_to_transform_per_attr,
const blender::IndexMask &affected_curves,
bool use_connected_only,
const blender::IndexMask &bezier_curves)
{
using namespace blender;
const std::array<Span<float3>, 3> src_positions_per_selection_attr = {
curves.positions(), curves.handle_positions_left(), curves.handle_positions_right()};
std::array<MutableSpan<float3>, 3> positions_per_selection_attr;
for (const int selection_i : points_to_transform_per_attr.index_range()) {
positions_per_selection_attr[selection_i] =
ed::transform::curves::append_positions_to_custom_data(
points_to_transform_per_attr[selection_i],
src_positions_per_selection_attr[selection_i],
tc.custom.type);
}
float mtx[3][3], smtx[3][3];
copy_m3_m4(mtx, transform.ptr());
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
ed::transform::curves::append_positions_to_custom_data(
selected_indices, curves.positions(), tc.custom.type);
MutableSpan<float3> positions = static_cast<CurvesTransformData *>(tc.custom.type.data)
->positions.as_mutable_span()
.slice(trans_data_offset, selected_indices.size());
MutableSpan<TransData> all_tc_data = MutableSpan(tc.data, tc.data_len);
OffsetIndices<int> position_offsets_in_td = ed::transform::curves::recent_position_offsets(
tc.custom.type, points_to_transform_per_attr.size());
if (use_proportional_edit) {
Vector<VArray<bool>> selection_attrs;
Span<StringRef> selection_attribute_names = ed::curves::get_curves_selection_attribute_names(
curves);
for (const StringRef selection_name : selection_attribute_names) {
const VArray<bool> selection_attr = *curves.attributes().lookup_or_default<bool>(
selection_name, bke::AttrDomain::Point, true);
selection_attrs.append(selection_attr);
}
for (const int selection_i : position_offsets_in_td.index_range()) {
if (position_offsets_in_td[selection_i].is_empty()) {
continue;
}
MutableSpan<TransData> tc_data = all_tc_data.slice(position_offsets_in_td[selection_i]);
MutableSpan<float3> positions = positions_per_selection_attr[selection_i];
IndexMask points_to_transform = points_to_transform_per_attr[selection_i];
VArray<bool> selection = selection_attrs[selection_i];
threading::parallel_for(points_to_transform.index_range(), 1024, [&](const IndexRange range) {
for (const int tranform_point_i : range) {
const int point_in_domain_i = points_to_transform[tranform_point_i];
TransData &td = tc_data[tranform_point_i];
float3 *elem = &positions[tranform_point_i];
copy_v3_v3(td.iloc, *elem);
copy_v3_v3(td.center, td.iloc);
td.loc = *elem;
td.flag = 0;
if (selection[point_in_domain_i]) {
td.flag = TD_SELECTED;
}
if (value_attribute) {
float *value = &((*value_attribute)[point_in_domain_i]);
td.val = value;
td.ival = *value;
}
td.ext = nullptr;
copy_m3_m3(td.smtx, smtx);
copy_m3_m3(td.mtx, mtx);
}
});
}
if (use_connected_only) {
const VArray<int8_t> curve_types = curves.curve_types();
const OffsetIndices<int> points_by_curve = curves.points_by_curve();
const VArray<bool> selection = *curves.attributes().lookup_or_default<bool>(
".selection", bke::AttrDomain::Point, true);
Array<int> bezier_offsets_in_td(curves.curves_num() + 1, 0);
offset_indices::copy_group_sizes(points_by_curve, bezier_curves, bezier_offsets_in_td);
offset_indices::accumulate_counts_to_offsets(bezier_offsets_in_td);
affected_curves.foreach_segment(GrainSize(512), [&](const IndexMaskSegment segment) {
Vector<float> closest_distances;
Array<int> map;
Array<float> closest_distances;
Array<float3> mapped_curve_positions;
for (const int curve_i : segment) {
const IndexRange points = points_by_curve[curve_i];
const bool has_any_selected = ed::curves::has_anything_selected(selection, points);
if (!has_any_selected && use_connected_only) {
for (const int point_i : points) {
TransData &td = tc.data[point_i + trans_data_offset];
const int selection_attrs_num = curve_types[curve_i] == CURVE_TYPE_BEZIER ? 3 : 1;
const IndexRange curve_points = points_by_curve[curve_i];
const int total_curve_points = selection_attrs_num * curve_points.size();
map.reinitialize(total_curve_points);
closest_distances.reinitialize(total_curve_points);
closest_distances.fill(std::numeric_limits<float>::max());
mapped_curve_positions.reinitialize(total_curve_points);
ed::transform::curves::fill_map(CurveType(curve_types[curve_i]),
curve_points,
position_offsets_in_td,
bezier_offsets_in_td[curve_i],
map);
bool has_any_selected = false;
for (const int selection_attr_i : IndexRange(selection_attrs_num)) {
has_any_selected = has_any_selected ||
ed::curves::has_anything_selected(selection_attrs[selection_attr_i],
curve_points);
}
if (!has_any_selected) {
for (const int i : map) {
TransData &td = all_tc_data[i];
td.flag |= TD_SKIP;
}
continue;
}
closest_distances.reinitialize(points.size());
closest_distances.fill(std::numeric_limits<float>::max());
for (const int i : IndexRange(points.size())) {
const int point_i = points[i];
TransData &td = tc.data[point_i + trans_data_offset];
float3 *elem = &positions[point_i];
copy_v3_v3(td.iloc, *elem);
copy_v3_v3(td.center, td.iloc);
td.loc = *elem;
td.flag = 0;
if (selection[point_i]) {
for (const int i : closest_distances.index_range()) {
TransData &td = all_tc_data[map[i]];
mapped_curve_positions[i] = td.loc;
if (td.flag & TD_SELECTED) {
closest_distances[i] = 0.0f;
td.flag = TD_SELECTED;
}
if (value_attribute) {
float *value = &((*value_attribute)[point_i]);
td.val = value;
td.ival = *value;
}
td.ext = nullptr;
copy_m3_m3(td.smtx, smtx);
copy_m3_m3(td.mtx, mtx);
}
if (use_connected_only) {
blender::ed::transform::curves::calculate_curve_point_distances_for_proportional_editing(
positions.slice(points), closest_distances.as_mutable_span());
for (const int i : IndexRange(points.size())) {
TransData &td = tc.data[points[i] + trans_data_offset];
td.dist = closest_distances[i];
}
}
}
});
}
else {
threading::parallel_for(selected_indices.index_range(), 1024, [&](const IndexRange range) {
for (const int selection_i : range) {
TransData *td = &tc.data[selection_i + trans_data_offset];
const int point_i = selected_indices[selection_i];
float3 *elem = &positions[selection_i];
copy_v3_v3(td->iloc, *elem);
copy_v3_v3(td->center, td->iloc);
td->loc = *elem;
if (value_attribute) {
float *value = &((*value_attribute)[point_i]);
td->val = value;
td->ival = *value;
blender::ed::transform::curves::calculate_curve_point_distances_for_proportional_editing(
mapped_curve_positions.as_span(), closest_distances.as_mutable_span());
for (const int i : closest_distances.index_range()) {
TransData &td = all_tc_data[map[i]];
td.dist = closest_distances[i];
}
td->flag = TD_SELECTED;
td->ext = nullptr;
copy_m3_m3(td->smtx, smtx);
copy_m3_m3(td->mtx, mtx);
}
});
}

View File

@ -726,10 +726,8 @@ struct BeztMap {
BezTriple *bezt;
/** Index of `bezt` in `fcu->bezt` array before sorting. */
uint oldIndex;
/** Swap order of handles (-1=clear; 0=not checked, 1=swap). */
short swap_handles;
/** Interpolation of previous and next segments. */
char prev_ipo, current_ipo;
/** Swap order of handles. Can happen when rotating keys around their common center. */
bool swap_handles;
};
/**
@ -743,18 +741,12 @@ static blender::Vector<BeztMap> bezt_to_beztmaps(BezTriple *bezts, const int tot
blender::Vector<BeztMap> bezms = blender::Vector<BeztMap>(totvert);
BezTriple *prevbezt = nullptr;
for (const int i : bezms.index_range()) {
BezTriple *bezt = &bezts[i];
BeztMap &bezm = bezms[i];
bezm.bezt = bezt;
bezm.swap_handles = false;
bezm.oldIndex = i;
bezm.prev_ipo = (prevbezt) ? prevbezt->ipo : bezt->ipo;
bezm.current_ipo = bezt->ipo;
prevbezt = bezt;
}
return bezms;
@ -764,8 +756,16 @@ static blender::Vector<BeztMap> bezt_to_beztmaps(BezTriple *bezts, const int tot
static void sort_time_beztmaps(const blender::MutableSpan<BeztMap> bezms)
{
BeztMap *bezm;
bool ok = true;
/* Check if handles need to be swapped. */
for (BeztMap &bezm : bezms) {
/* Handles are only swapped if they are both on the wrong side of the key. Otherwise the one
* handle out of place is just clamped at the key position later. */
bezm.swap_handles = (bezm.bezt->vec[0][0] > bezm.bezt->vec[1][0] &&
bezm.bezt->vec[2][0] < bezm.bezt->vec[1][0]);
}
bool ok = true;
/* Keep repeating the process until nothing is out of place anymore. */
while (ok) {
ok = false;
@ -779,21 +779,6 @@ static void sort_time_beztmaps(const blender::MutableSpan<BeztMap> bezms)
ok = true;
}
}
/* Do we need to check if the handles need to be swapped?
* Optimization: this only needs to be performed in the first loop. */
if (bezm->swap_handles == 0) {
if ((bezm->bezt->vec[0][0] > bezm->bezt->vec[1][0]) &&
(bezm->bezt->vec[2][0] < bezm->bezt->vec[1][0]))
{
/* Handles need to be swapped. */
bezm->swap_handles = 1;
}
else {
/* Handles need to be cleared. */
bezm->swap_handles = -1;
}
}
}
}
}
@ -827,7 +812,7 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, const blender::Span<BeztM
/* Update all transdata pointers, no need to check for selections etc,
* since only points that are really needed were created as transdata. */
if (td2d->loc2d == bezm->bezt->vec[0]) {
if (bezm->swap_handles == 1) {
if (bezm->swap_handles) {
td2d->loc2d = fcu->bezt[i].vec[2];
}
else {
@ -836,7 +821,7 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, const blender::Span<BeztM
adjusted[j] = true;
}
else if (td2d->loc2d == bezm->bezt->vec[2]) {
if (bezm->swap_handles == 1) {
if (bezm->swap_handles) {
td2d->loc2d = fcu->bezt[i].vec[0];
}
else {
@ -860,7 +845,7 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, const blender::Span<BeztM
/* The handle type pointer has to be updated too. */
if (adjusted[j] && td->flag & TD_BEZTRIPLE && td->hdata) {
if (bezm->swap_handles == 1) {
if (bezm->swap_handles) {
td->hdata->h1 = &fcu->bezt[i].h2;
td->hdata->h2 = &fcu->bezt[i].h1;
}

View File

@ -89,7 +89,6 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
int layer_points_offset = 0;
const Vector<ed::greasepencil::MutableDrawingInfo> drawings = all_drawings[i];
for (ed::greasepencil::MutableDrawingInfo info : drawings) {
const bke::greasepencil::Layer &layer = *layers[info.layer_index];
@ -115,13 +114,10 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
curves,
layer_space_to_world_space,
value_attribute,
points,
use_proportional_edit,
{points},
affected_strokes,
use_connected_only,
layer_points_offset);
layer_points_offset += points.size();
IndexMask());
layer_offset++;
}
}

View File

@ -81,6 +81,10 @@ struct tSlider {
/* How the factor number is drawn. When drawing percent it is factor*100. */
SliderMode slider_mode;
/* Optional string that will display next to the slider to indicate which property is modified
* right now. */
std::string property_label;
/* What unit to add to the slider. */
char unit_string[SLIDER_UNIT_STRING_SIZE];
@ -224,19 +228,28 @@ static void draw_backdrop(const int fontid,
const rctf *main_line_rect,
const uint8_t color_bg[4],
const short region_y_size,
const float base_tick_height)
const float base_tick_height,
const std::string &property_label)
{
float string_pixel_size[2];
float percent_string_pixel_size[2];
const char *percentage_string_placeholder = "000%%";
BLF_width_and_height(fontid,
percentage_string_placeholder,
sizeof(percentage_string_placeholder),
&string_pixel_size[0],
&string_pixel_size[1]);
const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
&percent_string_pixel_size[0],
&percent_string_pixel_size[1]);
float property_name_pixel_size[2];
BLF_width_and_height(fontid,
property_label.c_str(),
property_label.size(),
&property_name_pixel_size[0],
&property_name_pixel_size[1]);
const float pad[2] = {(region_y_size - base_tick_height) / 2 + 12.0f * U.pixelsize,
2.0f * U.pixelsize};
rctf backdrop_rect{};
backdrop_rect.xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0];
backdrop_rect.xmax = main_line_rect->xmax + pad[0];
backdrop_rect.xmin = main_line_rect->xmin - property_name_pixel_size[0] - pad[0];
backdrop_rect.xmax = main_line_rect->xmax + percent_string_pixel_size[0] + pad[0];
backdrop_rect.ymin = pad[1];
backdrop_rect.ymax = region_y_size - pad[1];
UI_draw_roundbox_3ub_alpha(&backdrop_rect, true, 4.0f, color_bg, color_bg[3]);
@ -304,7 +317,12 @@ static void slider_draw(const bContext * /*C*/, ARegion *region, void *arg)
handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * range_factor;
}
draw_backdrop(fontid, &main_line_rect, color_bg, slider->region_header->winy, base_tick_height);
draw_backdrop(fontid,
&main_line_rect,
color_bg,
slider->region_header->winy,
base_tick_height,
slider->property_label);
draw_main_line(&main_line_rect, slider->factor, slider->overshoot, color_overshoot, color_line);
@ -356,11 +374,25 @@ static void slider_draw(const bContext * /*C*/, ARegion *region, void *arg)
&factor_string_pixel_size[0],
&factor_string_pixel_size[1]);
BLF_position(fontid,
main_line_rect.xmin - 12.0 * U.pixelsize - factor_string_pixel_size[0],
(region->winy / 2) - factor_string_pixel_size[1] / 2,
0.0f);
const float text_padding = 12.0 * U.pixelsize;
const float factor_string_pos_x = main_line_rect.xmax + text_padding;
BLF_position(
fontid, factor_string_pos_x, (region->winy / 2) - factor_string_pixel_size[1] / 2, 0.0f);
BLF_draw(fontid, factor_string, sizeof(factor_string));
if (!slider->property_label.empty()) {
float property_name_pixel_size[2];
BLF_width_and_height(fontid,
slider->property_label.c_str(),
slider->property_label.length(),
&property_name_pixel_size[0],
&property_name_pixel_size[1]);
BLF_position(fontid,
main_line_rect.xmin - text_padding - property_name_pixel_size[0],
(region->winy / 2) - property_name_pixel_size[1] / 2,
0.0f);
BLF_draw(fontid, slider->property_label.c_str(), slider->property_label.length());
}
}
static void slider_update_factor(tSlider *slider, const wmEvent *event)
@ -584,6 +616,11 @@ void ED_slider_unit_set(tSlider *slider, const char *unit)
STRNCPY(slider->unit_string, unit);
}
void ED_slider_property_label_set(tSlider *slider, const char *property_label)
{
slider->property_label.assign(property_label);
}
/** \} */
void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_info)

View File

@ -14,13 +14,14 @@ namespace blender::geometry {
* 1D Gaussian-like smoothing function.
*
* \param iterations: Number of times to repeat the smoothing.
* \param influence: Influence factor for each point.
* \param smooth_ends: Smooth the first and last value.
* \param keep_shape: Changes the gaussian kernel to avoid severe deformations.
* \param is_cyclic: Propagate smoothing across the ends of the input as if they were connected.
*/
void gaussian_blur_1D(const GSpan src,
int iterations,
const float influence,
const VArray<float> &influence_by_point,
const bool smooth_ends,
const bool keep_shape,
const bool is_cyclic,
@ -39,4 +40,17 @@ void smooth_curve_attribute(const IndexMask &curves_to_smooth,
bool keep_shape,
GMutableSpan attribute_data);
/**
* Smooths the \a attribute_data using a 1D gaussian blur.
*/
void smooth_curve_attribute(const IndexMask &curves_to_smooth,
const OffsetIndices<int> points_by_curve,
const VArray<bool> &point_selection,
const VArray<bool> &cyclic,
int iterations,
const VArray<float> &influence_by_point,
bool smooth_ends,
bool keep_shape,
GMutableSpan attribute_data);
} // namespace blender::geometry

View File

@ -18,7 +18,7 @@ namespace blender::geometry {
template<typename T>
static void gaussian_blur_1D(const Span<T> src,
const int iterations,
const float influence,
const VArray<float> &influence_by_point,
const bool smooth_ends,
const bool keep_shape,
const bool is_cyclic,
@ -133,19 +133,21 @@ static void gaussian_blur_1D(const Span<T> src,
}
/* Normalize the weights. */
threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t index : range) {
if (!is_end_and_fixed(index)) {
total_weight[index] += w - w2;
dst[index] = src[index] + influence * dst[index] / total_weight[index];
devirtualize_varray(influence_by_point, [&](const auto influence_by_point) {
threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
for (const int64_t index : range) {
if (!is_end_and_fixed(index)) {
total_weight[index] += w - w2;
dst[index] = src[index] + influence_by_point[index] * dst[index] / total_weight[index];
}
}
}
});
});
}
void gaussian_blur_1D(const GSpan src,
const int iterations,
const float influence,
const VArray<float> &influence_by_point,
const bool smooth_ends,
const bool keep_shape,
const bool is_cyclic,
@ -160,7 +162,7 @@ void gaussian_blur_1D(const GSpan src,
{
gaussian_blur_1D(src.typed<T>(),
iterations,
influence,
influence_by_point,
smooth_ends,
keep_shape,
is_cyclic,
@ -174,11 +176,13 @@ void smooth_curve_attribute(const IndexMask &curves_to_smooth,
const VArray<bool> &point_selection,
const VArray<bool> &cyclic,
const int iterations,
const float influence,
const VArray<float> &influence_by_point,
const bool smooth_ends,
const bool keep_shape,
GMutableSpan attribute_data)
{
VArraySpan<float> influences(influence_by_point);
curves_to_smooth.foreach_index(GrainSize(512), [&](const int curve_i) {
Vector<std::byte> orig_data;
const IndexRange points = points_by_curve[curve_i];
@ -196,10 +200,36 @@ void smooth_curve_attribute(const IndexMask &curves_to_smooth,
dst_data.type().copy_assign_n(dst_data.data(), orig_data.data(), range.size());
const GSpan src_data(dst_data.type(), orig_data.data(), range.size());
gaussian_blur_1D(
src_data, iterations, influence, smooth_ends, keep_shape, cyclic[curve_i], dst_data);
gaussian_blur_1D(src_data,
iterations,
VArray<float>::ForSpan(influences.slice(range)),
smooth_ends,
keep_shape,
cyclic[curve_i],
dst_data);
});
});
}
void smooth_curve_attribute(const IndexMask &curves_to_smooth,
const OffsetIndices<int> points_by_curve,
const VArray<bool> &point_selection,
const VArray<bool> &cyclic,
const int iterations,
const float influence,
const bool smooth_ends,
const bool keep_shape,
GMutableSpan attribute_data)
{
smooth_curve_attribute(curves_to_smooth,
points_by_curve,
point_selection,
cyclic,
iterations,
VArray<float>::ForSingle(influence, points_by_curve.total_size()),
smooth_ends,
keep_shape,
attribute_data);
}
} // namespace blender::geometry

View File

@ -358,11 +358,12 @@ void PackIsland::finalize_geometry_(const UVPackIsland_Params &params, MemArena
if (convex_len >= 3) {
/* Write back. */
triangle_vertices_.clear();
Array<float2> convexVertices(convex_len);
float2 *convex_verts = static_cast<float2 *>(
BLI_memarena_alloc(arena, sizeof(*convex_verts) * convex_len));
for (int i = 0; i < convex_len; i++) {
convexVertices[i] = source[index_map[i]];
convex_verts[i] = source[index_map[i]];
}
add_polygon(convexVertices, arena, heap);
add_polygon(Span(convex_verts, convex_len), arena, heap);
}
}

View File

@ -328,6 +328,7 @@ typedef enum eGP_Sculpt_Mode_Flag {
/* apply brush to uv data */
GP_SCULPT_FLAGMODE_APPLY_UV = (1 << 3),
} eGP_Sculpt_Mode_Flag;
ENUM_OPERATORS(eGP_Sculpt_Mode_Flag, GP_SCULPT_FLAGMODE_APPLY_UV)
typedef enum eAutomasking_flag {
BRUSH_AUTOMASKING_TOPOLOGY = (1 << 0),

View File

@ -318,8 +318,8 @@ static void rna_BoneCollections_active_index_range(
{
bArmature *arm = (bArmature *)ptr->data;
// TODO: Figure out what this function actually is used for, as we may want to protect the first
// collection (i.e. the default collection that should remain first).
/* TODO: Figure out what this function actually is used for, as we may want to protect the first
* collection (i.e. the default collection that should remain first). */
*min = 0;
*max = max_ii(0, arm->collection_array_num - 1);
}

View File

@ -1013,6 +1013,7 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C,
return rna_enum_dummy_DEFAULT_items;
}
case PaintMode::SculptGPencil:
case PaintMode::SculptGreasePencil:
switch (me->gpencil_sculpt_tool) {
case GPSCULPT_TOOL_THICKNESS:
case GPSCULPT_TOOL_STRENGTH:

View File

@ -661,12 +661,6 @@ static void rna_GizmoGroup_bl_label_set(PointerRNA *ptr, const char *value)
}
}
static bool rna_GizmoGroup_has_reports_get(PointerRNA *ptr)
{
wmGizmoGroup *gzgroup = static_cast<wmGizmoGroup *>(ptr->data);
return (gzgroup->reports && gzgroup->reports->list.first);
}
# ifdef WITH_PYTHON
static bool rna_gizmogroup_poll_cb(const bContext *C, wmGizmoGroupType *gzgt)
@ -1501,14 +1495,6 @@ static void rna_def_gizmogroup(BlenderRNA *brna)
prop, "rna_GizmoGroup_name_get", "rna_GizmoGroup_name_length", nullptr);
RNA_def_property_ui_text(prop, "Name", "");
prop = RNA_def_property(srna, "has_reports", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* this is 'virtual' property */
RNA_def_property_boolean_funcs(prop, "rna_GizmoGroup_has_reports_get", nullptr);
RNA_def_property_ui_text(
prop,
"Has Reports",
"GizmoGroup has a set of reports (warnings and errors) from last execution");
RNA_define_verify_sdna(false); /* not in sdna */
prop = RNA_def_property(srna, "gizmos", PROP_COLLECTION, PROP_NONE);

View File

@ -47,6 +47,8 @@
#include "COM_node_operation.hh"
#include "NOD_socket_search_link.hh"
#include "node_composite_util.hh"
/* **************** OUTPUT FILE ******************** */
@ -306,6 +308,16 @@ static void update_output_file(bNodeTree *ntree, bNode *node)
}
}
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
{
if (params.in_out() == SOCK_IN) {
params.add_item(IFACE_("Image"), [](LinkSearchOpParams &params) {
bNode &node = params.add_node("CompositorNodeOutputFile");
params.update_and_connect_available_socket(node, "Image");
});
}
}
static void node_composit_buts_file_output(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
PointerRNA imfptr = RNA_pointer_get(ptr, "format");
@ -764,6 +776,7 @@ void register_node_type_cmp_output_file()
node_type_storage(
&ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
ntype.updatefunc = file_ns::update_output_file;
ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);

View File

@ -477,8 +477,6 @@ struct wmGizmoGroup {
/** Python stores the class instance here. */
void *py_instance;
/** Errors and warnings storage. */
ReportList *reports;
/** Has the same result as hiding all gizmos individually. */
union {

View File

@ -101,11 +101,6 @@ void wm_gizmogroup_free(bContext *C, wmGizmoGroup *gzgroup)
}
#endif
if (gzgroup->reports && (gzgroup->reports->flag & RPT_FREE)) {
BKE_reports_free(gzgroup->reports);
MEM_freeN(gzgroup->reports);
}
if (gzgroup->customdata_free) {
gzgroup->customdata_free(gzgroup->customdata);
}

View File

@ -88,6 +88,7 @@
#include "ED_anim_api.hh"
#include "ED_asset.hh"
#include "ED_gpencil_legacy.hh"
#include "ED_grease_pencil.hh"
#include "ED_keyframes_edit.hh"
#include "ED_keyframing.hh"
#include "ED_node.hh"
@ -583,6 +584,7 @@ void WM_exit_ex(bContext *C, const bool do_python_exit, const bool do_user_exit_
BKE_mask_clipboard_free();
BKE_vfont_clipboard_free();
ED_node_clipboard_free();
ed::greasepencil::clipboard_free();
UV_clipboard_free();
wm_clipboard_free();

View File

@ -520,7 +520,7 @@ struct BlendePyContextStore {
bool has_win;
};
static void arg_py_context_backup(bContext *C, BlendePyContextStore *c_py, const char *script_id)
static void arg_py_context_backup(bContext *C, BlendePyContextStore *c_py)
{
c_py->wm = CTX_wm_manager(C);
c_py->scene = CTX_data_scene(C);
@ -530,11 +530,11 @@ static void arg_py_context_backup(bContext *C, BlendePyContextStore *c_py, const
CTX_wm_window_set(C, static_cast<wmWindow *>(c_py->wm->windows.first));
}
else {
/* NOTE: this should never happen, although it may be possible when loading
* `.blend` files without windowing data. Whatever the case, it shouldn't crash,
* although typical scripts that accesses the context is not expected to work usefully. */
c_py->win = nullptr;
fprintf(stderr,
"Python script \"%s\" "
"running with missing context data.\n",
script_id);
fprintf(stderr, "Python script running with missing context data.\n");
}
}
@ -558,7 +558,7 @@ static void arg_py_context_restore(bContext *C, BlendePyContextStore *c_py)
# define BPY_CTX_SETUP(_cmd) \
{ \
BlendePyContextStore py_c; \
arg_py_context_backup(C, &py_c, argv[1]); \
arg_py_context_backup(C, &py_c); \
{ \
_cmd; \
} \
@ -2277,7 +2277,7 @@ static int arg_handle_python_expr_run(int argc, const char **argv, void *data)
static const char arg_handle_python_console_run_doc[] =
"\n\t"
"Run Blender with an interactive console.";
static int arg_handle_python_console_run(int /*argc*/, const char **argv, void *data)
static int arg_handle_python_console_run(int /*argc*/, const char ** /*argv*/, void *data)
{
# ifdef WITH_PYTHON
bContext *C = static_cast<bContext *>(data);