WIP: Brush assets project #106303
302
CMakeLists.txt
302
CMakeLists.txt
|
@ -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()
|
||||
|
||||
|
||||
# ---------------------
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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"),
|
||||
])
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)});
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -31,7 +31,7 @@ void build_asset_view(uiLayout &layout,
|
|||
const AssetLibraryReference &library_ref,
|
||||
const AssetShelf &shelf,
|
||||
const bContext &C,
|
||||
ARegion ®ion);
|
||||
const ARegion ®ion);
|
||||
|
||||
void catalog_selector_panel_register(ARegionType *region_type);
|
||||
|
||||
|
|
|
@ -269,7 +269,7 @@ void build_asset_view(uiLayout &layout,
|
|||
const AssetLibraryReference &library_ref,
|
||||
const AssetShelf &shelf,
|
||||
const bContext &C,
|
||||
ARegion ®ion)
|
||||
const ARegion ®ion)
|
||||
{
|
||||
list::storage_fetch(&library_ref, &C);
|
||||
list::ensure_previews_job(&library_ref, &C);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ************* */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ®ion;
|
||||
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 ®ion,
|
||||
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 ¶ms, IndexMaskMemory &memory);
|
||||
|
||||
bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
const GreasePencilStrokeParams ¶ms);
|
||||
|
||||
/* Project points from layer space into 2D view space. */
|
||||
Array<float2> calculate_view_positions(const GreasePencilStrokeParams ¶ms,
|
||||
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 ¶ms)> 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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ®ion,
|
||||
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 ¶ms, 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 ¶ms)
|
||||
{
|
||||
return bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
|
||||
¶ms.ob_eval, params.ob_orig, params.layer_index, params.frame_number);
|
||||
}
|
||||
|
||||
Array<float2> calculate_view_positions(const GreasePencilStrokeParams ¶ms,
|
||||
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(
|
||||
¶ms.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 ¶ms)> 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 ®ion = *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
|
|
@ -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 ¶ms,
|
||||
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 ¶ms, 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 ®ion = *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 ®ion = *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 ®ion = *CTX_wm_region(&C);
|
||||
const RegionView3D &rv3d = *CTX_wm_region_view3d(&C);
|
||||
|
||||
this->foreach_grabbed_drawing(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
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(®ion, 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(®ion, 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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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 ¶ms) {
|
||||
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
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -461,7 +461,7 @@ void uiTemplateMarker(uiLayout *layout,
|
|||
0,
|
||||
0,
|
||||
"");
|
||||
|
||||
MEM_freeN(cb);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -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")) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -358,11 +358,12 @@ void PackIsland::finalize_geometry_(const UVPackIsland_Params ¶ms, 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ¶ms)
|
||||
{
|
||||
if (params.in_out() == SOCK_IN) {
|
||||
params.add_item(IFACE_("Image"), [](LinkSearchOpParams ¶ms) {
|
||||
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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue