WIP: UI: Status Bar Keymaps When Idle #121059

Draft
Harley Acheson wants to merge 8 commits from Harley/blender:IdleKeymaps into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
157 changed files with 4520 additions and 1649 deletions
Showing only changes of commit 8ed596972d - Show all commits

View File

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

View File

@ -21,3 +21,20 @@ diff -Naur orig/src/cmake/compiler.cmake external_openimageio/src/cmake/compiler
endif (MSVC)
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD"
diff --git a/src/libOpenImageIO/imageioplugin.cpp b/src/libOpenImageIO/imageioplugin.cpp
index 9ad45042e..d2a0210ff 100644
--- a/src/libOpenImageIO/imageioplugin.cpp
+++ b/src/libOpenImageIO/imageioplugin.cpp
@@ -469,12 +469,6 @@ pvt::catalog_all_plugins(std::string searchpath)
std::unique_lock<std::recursive_mutex> lock(imageio_mutex);
append_if_env_exists(searchpath, "OIIO_LIBRARY_PATH", true);
-#ifdef __APPLE__
- append_if_env_exists(searchpath, "DYLD_LIBRARY_PATH");
-#endif
-#if defined(__linux__) || defined(__FreeBSD__)
- append_if_env_exists(searchpath, "LD_LIBRARY_PATH");
-#endif
size_t patlen = pattern.length();
std::vector<std::string> dirs;

View File

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

View File

@ -64,6 +64,13 @@ BlenderAttributeType blender_attribute_name_split_type(ustring name, string *r_r
void python_thread_state_save(void **python_thread_state);
void python_thread_state_restore(void **python_thread_state);
static bool mesh_use_corner_normals(BL::Mesh &mesh, Mesh::SubdivisionType subdivision_type)
{
return mesh && (subdivision_type == Mesh::SUBDIVISION_NONE) &&
(static_cast<const ::Mesh *>(mesh.ptr.data)->normals_domain(true) ==
blender::bke::MeshNormalDomain::Corner);
}
static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
BObjectInfo &b_ob_info,
BL::Depsgraph & /*depsgraph*/,
@ -88,27 +95,33 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) :
BL::Mesh(PointerRNA_NULL);
const bool split_faces = (mesh) && (subdivision_type == Mesh::SUBDIVISION_NONE) &&
(static_cast<const ::Mesh *>(mesh.ptr.data)->normals_domain(true) ==
blender::bke::MeshNormalDomain::Corner);
bool use_corner_normals = false;
if (b_ob_info.is_real_object_data()) {
if (mesh) {
/* Make a copy to split faces if we use auto-smooth, otherwise not needed.
* Also in edit mode do we need to make a copy, to ensure data layers like
* UV are not empty. */
if (mesh.is_editmode() || split_faces) {
if (mesh.is_editmode()) {
/* Flush editmesh to mesh, including all data layers. */
BL::Depsgraph depsgraph(PointerRNA_NULL);
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
}
else if (mesh_use_corner_normals(mesh, subdivision_type)) {
/* Make a copy to split faces. */
BL::Depsgraph depsgraph(PointerRNA_NULL);
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
use_corner_normals = true;
}
}
else {
BL::Depsgraph depsgraph(PointerRNA_NULL);
mesh = b_ob_info.real_object.to_mesh(false, depsgraph);
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
}
}
else {
/* TODO: what to do about non-mesh geometry instances? */
use_corner_normals = mesh_use_corner_normals(mesh, subdivision_type);
}
#if 0
@ -121,7 +134,7 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/,
#endif
if (mesh) {
if (split_faces) {
if (use_corner_normals) {
mesh.split_faces();
}

View File

@ -12,12 +12,14 @@ shader node_glass_bsdf(color Color = 0.8,
normal Normal = N,
output closure color BSDF = 0)
{
float r2 = Roughness * Roughness;
color base_color = max(Color, color(0.0));
float r2 = clamp(Roughness, 0.0, 1.0);
r2 = r2 * r2;
float eta = max(IOR, 1e-5);
eta = backfacing() ? 1.0 / eta : eta;
color F0 = F0_from_ior(eta);
color F90 = color(1.0);
BSDF = generalized_schlick_bsdf(
Normal, vector(0.0), Color, Color, r2, r2, F0, F90, -eta, distribution);
Normal, vector(0.0), base_color, base_color, r2, r2, F0, F90, -eta, distribution);
}

View File

@ -15,7 +15,9 @@ shader node_glossy_bsdf(color Color = 0.8,
output closure color BSDF = 0)
{
/* compute roughness */
float roughness = Roughness * Roughness;
color base_color = max(Color, color(0.0));
float roughness = clamp(Roughness, 0.0, 1.0);
roughness = roughness * roughness;
float roughness_u, roughness_v;
float aniso = clamp(Anisotropy, -0.99, 0.99);
@ -41,7 +43,8 @@ shader node_glossy_bsdf(color Color = 0.8,
}
if (distribution == "Multiscatter GGX")
BSDF = Color * microfacet_multi_ggx_aniso(Normal, T, roughness_u, roughness_v, Color);
BSDF = base_color *
microfacet_multi_ggx_aniso(Normal, T, roughness_u, roughness_v, base_color);
else
BSDF = Color * microfacet(distribution, Normal, T, roughness_u, roughness_v, 0.0, 0);
BSDF = base_color * microfacet(distribution, Normal, T, roughness_u, roughness_v, 0.0, 0);
}

View File

@ -11,10 +11,11 @@ shader node_sheen_bsdf(color Color = 0.8,
normal Normal = N,
output closure color BSDF = 0)
{
color base_color = max(Color, color(0.0));
float roughness = clamp(Roughness, 0.0, 1.0);
if (distribution == "ashikhmin")
BSDF = Color * ashikhmin_velvet(Normal, roughness);
BSDF = base_color * ashikhmin_velvet(Normal, roughness);
else if (distribution == "microfiber")
BSDF = Color * sheen(Normal, roughness);
BSDF = base_color * sheen(Normal, roughness);
}

View File

@ -13,14 +13,15 @@ shader node_subsurface_scattering(color Color = 0.8,
normal Normal = N,
output closure color BSSRDF = 0)
{
BSSRDF = Color * bssrdf(method,
Normal,
Scale * Radius,
Color,
"ior",
IOR,
"anisotropy",
Anisotropy,
"roughness",
1.0);
color base_color = max(Color, color(0.0));
BSSRDF = base_color * bssrdf(method,
Normal,
Scale * Radius,
base_color,
"ior",
IOR,
"anisotropy",
Anisotropy,
"roughness",
1.0);
}

View File

@ -470,24 +470,24 @@ ccl_device
break;
}
float roughness = sqr(param1);
float roughness = sqr(saturatef(param1));
bsdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bsdf->ior = 1.0f;
/* compute roughness */
float anisotropy = clamp(param2, -0.99f, 0.99f);
if (data_node.y == SVM_STACK_INVALID || fabsf(anisotropy) <= 1e-4f) {
if (data_node.w == SVM_STACK_INVALID || fabsf(anisotropy) <= 1e-4f) {
/* Isotropic case. */
bsdf->T = zero_float3();
bsdf->alpha_x = roughness;
bsdf->alpha_y = roughness;
}
else {
bsdf->T = stack_load_float3(stack, data_node.y);
bsdf->T = stack_load_float3(stack, data_node.w);
/* rotate tangent */
float rotation = stack_load_float(stack, data_node.z);
float rotation = stack_load_float(stack, data_node.y);
if (rotation != 0.0f) {
bsdf->T = rotate_around_axis(bsdf->T, bsdf->N, rotation * M_2PI_F);
}
@ -512,8 +512,9 @@ ccl_device
else {
sd->flag |= bsdf_microfacet_ggx_setup(bsdf);
if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) {
kernel_assert(stack_valid(data_node.w));
const Spectrum color = rgb_to_spectrum(stack_load_float3(stack, data_node.w));
kernel_assert(stack_valid(data_node.z));
const Spectrum color = max(rgb_to_spectrum(stack_load_float3(stack, data_node.z)),
zero_spectrum());
bsdf_microfacet_setup_fresnel_constant(kg, bsdf, sd, color);
}
}
@ -580,12 +581,12 @@ ccl_device
float ior = fmaxf(param2, 1e-5f);
bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / ior : ior;
bsdf->alpha_x = bsdf->alpha_y = sqr(param1);
bsdf->alpha_x = bsdf->alpha_y = sqr(saturatef(param1));
fresnel->f0 = make_float3(F0_from_ior(ior));
fresnel->f90 = one_spectrum();
fresnel->exponent = -ior;
const float3 color = stack_load_float3(stack, data_node.z);
const float3 color = max(stack_load_float3(stack, data_node.y), zero_float3());
fresnel->reflection_tint = reflective_caustics ? rgb_to_spectrum(color) : zero_spectrum();
fresnel->transmission_tint = refractive_caustics ? rgb_to_spectrum(color) :
zero_spectrum();
@ -622,7 +623,7 @@ ccl_device
if (bsdf) {
bsdf->N = N;
bsdf->roughness = param1;
bsdf->roughness = saturatef(param1);
sd->flag |= bsdf_sheen_setup(kg, sd, bsdf);
}
@ -834,10 +835,10 @@ ccl_device
bsdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bsdf->roughness1 = param1;
bsdf->roughness2 = param2;
bsdf->offset = -stack_load_float(stack, data_node.z);
bsdf->offset = -stack_load_float(stack, data_node.y);
if (stack_valid(data_node.y)) {
bsdf->T = normalize(stack_load_float3(stack, data_node.y));
if (stack_valid(data_node.w)) {
bsdf->T = normalize(stack_load_float3(stack, data_node.w));
}
else if (!(sd->type & PRIMITIVE_CURVE)) {
bsdf->T = normalize(sd->dPdv);
@ -866,12 +867,13 @@ ccl_device
ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, weight);
if (bssrdf) {
bssrdf->radius = rgb_to_spectrum(stack_load_float3(stack, data_node.z) * param1);
bssrdf->radius = max(rgb_to_spectrum(stack_load_float3(stack, data_node.y) * param1),
zero_spectrum());
bssrdf->albedo = closure_weight;
bssrdf->N = maybe_ensure_valid_specular_reflection(sd, N);
bssrdf->ior = param2;
bssrdf->alpha = 1.0f;
bssrdf->anisotropy = stack_load_float(stack, data_node.w);
bssrdf->anisotropy = stack_load_float(stack, data_node.z);
sd->flag |= bssrdf_setup(sd, bssrdf, path_flag, (ClosureType)type);
}

View File

@ -2283,14 +2283,14 @@ bool BsdfBaseNode::has_bump()
BsdfNode::BsdfNode(const NodeType *node_type) : BsdfBaseNode(node_type) {}
void BsdfNode::compile(SVMCompiler &compiler,
ShaderInput *param1,
ShaderInput *param2,
ShaderInput *param3,
ShaderInput *param4)
ShaderInput *bsdf_y,
ShaderInput *bsdf_z,
ShaderInput *data_y,
ShaderInput *data_z,
ShaderInput *data_w)
{
ShaderInput *color_in = input("Color");
ShaderInput *normal_in = input("Normal");
ShaderInput *tangent_in = input("Tangent");
if (color_in->link) {
compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in));
@ -2300,21 +2300,20 @@ void BsdfNode::compile(SVMCompiler &compiler,
}
int normal_offset = (normal_in) ? compiler.stack_assign_if_linked(normal_in) : SVM_STACK_INVALID;
int tangent_offset = (tangent_in) ? compiler.stack_assign_if_linked(tangent_in) :
SVM_STACK_INVALID;
int param3_offset = (param3) ? compiler.stack_assign(param3) : SVM_STACK_INVALID;
int param4_offset = (param4) ? compiler.stack_assign(param4) : SVM_STACK_INVALID;
int data_y_offset = (data_y) ? compiler.stack_assign(data_y) : SVM_STACK_INVALID;
int data_z_offset = (data_z) ? compiler.stack_assign(data_z) : SVM_STACK_INVALID;
int data_w_offset = (data_w) ? compiler.stack_assign(data_w) : SVM_STACK_INVALID;
compiler.add_node(
NODE_CLOSURE_BSDF,
compiler.encode_uchar4(closure,
(param1) ? compiler.stack_assign(param1) : SVM_STACK_INVALID,
(param2) ? compiler.stack_assign(param2) : SVM_STACK_INVALID,
(bsdf_y) ? compiler.stack_assign(bsdf_y) : SVM_STACK_INVALID,
(bsdf_z) ? compiler.stack_assign(bsdf_z) : SVM_STACK_INVALID,
compiler.closure_mix_weight_offset()),
__float_as_int((param1) ? get_float(param1->socket_type) : 0.0f),
__float_as_int((param2) ? get_float(param2->socket_type) : 0.0f));
__float_as_int((bsdf_y) ? get_float(bsdf_y->socket_type) : 0.0f),
__float_as_int((bsdf_z) ? get_float(bsdf_z->socket_type) : 0.0f));
compiler.add_node(normal_offset, tangent_offset, param3_offset, param4_offset);
compiler.add_node(normal_offset, data_y_offset, data_z_offset, data_w_offset);
}
void BsdfNode::compile(SVMCompiler &compiler)
@ -2392,13 +2391,21 @@ void GlossyBsdfNode::compile(SVMCompiler &compiler)
{
closure = distribution;
ShaderInput *tangent = input("Tangent");
tangent = compiler.is_linked(tangent) ? tangent : nullptr;
/* TODO: Just use weight for legacy MultiGGX? Would also simplify OSL. */
if (closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) {
BsdfNode::compile(
compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), input("Color"));
BsdfNode::compile(compiler,
input("Roughness"),
input("Anisotropy"),
input("Rotation"),
input("Color"),
tangent);
}
else {
BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
BsdfNode::compile(
compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), nullptr, tangent);
}
}
@ -3535,7 +3542,11 @@ void HairBsdfNode::compile(SVMCompiler &compiler)
{
closure = component;
BsdfNode::compile(compiler, input("RoughnessU"), input("RoughnessV"), input("Offset"));
ShaderInput *tangent = input("Tangent");
tangent = compiler.is_linked(tangent) ? tangent : nullptr;
BsdfNode::compile(
compiler, input("RoughnessU"), input("RoughnessV"), input("Offset"), nullptr, tangent);
}
void HairBsdfNode::compile(OSLCompiler &compiler)

View File

@ -479,10 +479,11 @@ class BsdfNode : public BsdfBaseNode {
SHADER_NODE_BASE_CLASS(BsdfNode)
void compile(SVMCompiler &compiler,
ShaderInput *param1,
ShaderInput *param2,
ShaderInput *param3 = NULL,
ShaderInput *param4 = NULL);
ShaderInput *bsdf_y,
ShaderInput *bsdf_z,
ShaderInput *data_y = nullptr,
ShaderInput *data_z = nullptr,
ShaderInput *data_w = nullptr);
NODE_SOCKET_API(float3, color)
NODE_SOCKET_API(float3, normal)

View File

@ -294,9 +294,14 @@ int SVMCompiler::stack_assign(ShaderOutput *output)
return output->stack_offset;
}
bool SVMCompiler::is_linked(ShaderInput *input)
{
return (input->link || input->constant_folded_in);
}
int SVMCompiler::stack_assign_if_linked(ShaderInput *input)
{
if (input->link || input->constant_folded_in) {
if (is_linked(input)) {
return stack_assign(input);
}

View File

@ -87,6 +87,7 @@ class SVMCompiler {
int stack_assign(ShaderOutput *output);
int stack_assign(ShaderInput *input);
bool is_linked(ShaderInput *input);
int stack_assign_if_linked(ShaderInput *input);
int stack_assign_if_linked(ShaderOutput *output);
int stack_find_offset(int size);

View File

@ -123,6 +123,10 @@ typedef enum {
* Supports IME text input methods (when `WITH_INPUT_IME` is defined).
*/
GHOST_kCapabilityInputIME = (1 << 6),
/**
* Support detecting the physical trackpad direction.
*/
GHOST_kCapabilityTrackpadPhysicalDirection = (1 << 7),
} GHOST_TCapabilityFlag;
/**
@ -132,7 +136,8 @@ typedef enum {
#define GHOST_CAPABILITY_FLAG_ALL \
(GHOST_kCapabilityCursorWarp | GHOST_kCapabilityWindowPosition | \
GHOST_kCapabilityPrimaryClipboard | GHOST_kCapabilityGPUReadFrontBuffer | \
GHOST_kCapabilityClipboardImages | GHOST_kCapabilityDesktopSample | GHOST_kCapabilityInputIME)
GHOST_kCapabilityClipboardImages | GHOST_kCapabilityDesktopSample | \
GHOST_kCapabilityInputIME | GHOST_kCapabilityTrackpadPhysicalDirection)
/* Xtilt and Ytilt represent how much the pen is tilted away from
* vertically upright in either the X or Y direction, with X and Y the

View File

@ -108,6 +108,8 @@ static bool has_libdecor = true;
# endif
#endif
static signed char has_wl_trackpad_physical_direction = -1;
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
@ -6410,6 +6412,8 @@ static void gwl_registry_wl_seat_add(GWL_Display *display, const GWL_RegisteryAd
display->seats.push_back(seat);
wl_seat_add_listener(seat->wl.seat, &seat_listener, seat);
gwl_registry_entry_add(display, params, static_cast<void *>(seat));
has_wl_trackpad_physical_direction = version >= 9;
}
static void gwl_registry_wl_seat_update(GWL_Display *display,
const GWL_RegisteryUpdate_Params &params)
@ -8367,6 +8371,9 @@ GHOST_TSuccess GHOST_SystemWayland::cursor_visibility_set(const bool visible)
GHOST_TCapabilityFlag GHOST_SystemWayland::getCapabilities() const
{
GHOST_ASSERT(has_wl_trackpad_physical_direction != -1,
"The trackpad direction was expected to be initialized");
return GHOST_TCapabilityFlag(
GHOST_CAPABILITY_FLAG_ALL &
~(
@ -8388,7 +8395,9 @@ GHOST_TCapabilityFlag GHOST_SystemWayland::getCapabilities() const
* is negligible. */
GHOST_kCapabilityGPUReadFrontBuffer |
/* This WAYLAND back-end has not yet implemented desktop color sample. */
GHOST_kCapabilityDesktopSample));
GHOST_kCapabilityDesktopSample |
/* This flag will eventually be removed. */
(has_wl_trackpad_physical_direction ? 0 : GHOST_kCapabilityTrackpadPhysicalDirection)));
}
bool GHOST_SystemWayland::cursor_grab_use_software_display_get(const GHOST_TGrabCursorMode mode)

View File

@ -4693,6 +4693,12 @@ def km_grease_pencil_sculpt_mode(params):
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
# Invoke sculpt operator
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
"ctrl": True}, {"properties": [("mode", 'INVERT')]}),
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
"shift": True}, {"properties": [("mode", 'SMOOTH')]}),
*_template_paint_radial_control("gpencil_sculpt_paint"),
])
@ -7138,21 +7144,53 @@ def km_image_editor_tool_uv_rip_region(params):
)
def km_image_editor_tool_uv_sculpt_stroke(params):
def km_image_editor_tool_uv_grab(params):
return (
"Image Editor Tool: Uv, Sculpt Stroke",
"Image Editor Tool: Uv, Grab",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("mode", 'INVERT')]}),
("sculpt.uv_sculpt_stroke", {"type": params.tool_mouse, "value": 'PRESS', "shift": True},
{"properties": [("mode", 'RELAX')]}),
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 0.9)]}),
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
{"properties": [("scalar", 1.0 / 0.9)]}),
*_template_paint_radial_control("uv_sculpt"),
("sculpt.uv_sculpt_grab", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_grab", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
def km_image_editor_tool_uv_relax(params):
return (
"Image Editor Tool: Uv, Relax",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
def km_image_editor_tool_uv_pinch(params):
return (
"Image Editor Tool: Uv, Pinch",
{"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'},
{"items": [
("sculpt.uv_sculpt_pinch", {"type": params.tool_mouse, "value": 'PRESS'}, None),
("sculpt.uv_sculpt_pinch", {"type": params.tool_mouse, "value": 'PRESS', "ctrl": True},
{"properties": [("use_invert", True)]}),
("sculpt.uv_sculpt_relax", {"type": params.tool_mouse, "value": 'PRESS', "shift": True}, None),
("wm.radial_control", {"type": 'F', "value": 'PRESS'},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.size"), ], }),
("wm.radial_control", {"type": 'F', "value": 'PRESS', "shift": True},
{"properties": [("data_path_primary", "tool_settings.uv_sculpt.strength"), ], }),
]},
)
@ -8914,7 +8952,9 @@ def generate_keymaps(params=None):
*(km_image_editor_tool_uv_select_circle(params, fallback=fallback) for fallback in (False, True)),
*(km_image_editor_tool_uv_select_lasso(params, fallback=fallback) for fallback in (False, True)),
km_image_editor_tool_uv_rip_region(params),
km_image_editor_tool_uv_sculpt_stroke(params),
km_image_editor_tool_uv_grab(params),
km_image_editor_tool_uv_relax(params),
km_image_editor_tool_uv_pinch(params),
km_image_editor_tool_uv_move(params),
km_image_editor_tool_uv_rotate(params),
km_image_editor_tool_uv_scale(params),

View File

@ -6,6 +6,7 @@ from __future__ import annotations
import bpy
from bpy.types import (
FileHandler,
Operator,
PropertyGroup,
)
@ -441,9 +442,27 @@ class NODE_OT_enum_definition_item_move(Operator):
return {'FINISHED'}
class NODE_FH_image_node(FileHandler):
bl_idname = "NODE_FH_image_node"
bl_label = "Image node"
bl_import_operator = "node.add_file"
bl_file_extensions = ";".join((*bpy.path.extensions_image, *bpy.path.extensions_movie))
@classmethod
def poll_drop(cls, context):
return (
(context.area is not None) and
(context.area.type == 'NODE_EDITOR') and
(context.region is not None) and
(context.region.type == 'WINDOW')
)
classes = (
NodeSetting,
NODE_FH_image_node,
NODE_OT_add_node,
NODE_OT_add_simulation_zone,
NODE_OT_add_repeat_zone,

View File

@ -292,9 +292,13 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeIsViewport")
if context.preferences.experimental.use_grease_pencil_version3:
node_add_menu.add_node_type(layout, "GeometryNodeInputNamedLayerSelection")
if context.space_data.geometry_nodes_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeToolMousePosition")
node_add_menu.add_node_type(layout, "GeometryNodeObjectInfo")
node_add_menu.add_node_type(layout, "GeometryNodeInputSceneTime")
node_add_menu.add_node_type(layout, "GeometryNodeSelfObject")
if context.space_data.geometry_nodes_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeViewportTransform")
node_add_menu.draw_assets_for_catalog(layout, "Input/Scene")

View File

@ -1287,23 +1287,29 @@ class CLIP_PT_tools_grease_pencil_draw(AnnotationDrawingToolsPanel, Panel):
class CLIP_MT_view_zoom(Menu):
bl_label = "Fractional Zoom"
bl_label = "Zoom"
def draw(self, _context):
layout = self.layout
from math import isclose
current_zoom = _context.space_data.zoom_percentage
ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
for i, (a, b) in enumerate(ratios):
if i in {3, 4}: # Draw separators around Zoom 1:1.
layout.separator()
percent = a / b * 100
layout.operator(
"clip.view_zoom_ratio",
text=iface_("Zoom %d:%d") % (a, b),
text=iface_("%g%% (%d:%d)") % (percent, a, b),
translate=False,
icon=('NONE', 'LAYER_ACTIVE')[isclose(percent, current_zoom, abs_tol=0.5)]
).ratio = a / b
layout.separator()
layout.operator("clip.view_zoom_in")
layout.operator("clip.view_zoom_out")
layout.operator("clip.view_all", text="Zoom to Fit").fit_view = True
class CLIP_MT_view(Menu):
bl_label = "View"
@ -1319,26 +1325,20 @@ class CLIP_MT_view(Menu):
layout.prop(sc, "show_region_hud")
layout.separator()
layout.operator("clip.view_selected")
layout.operator("clip.view_all")
layout.operator("clip.view_all", text="View Fit").fit_view = True
layout.operator("clip.view_center_cursor")
layout.menu("CLIP_MT_view_zoom")
layout.separator()
layout.operator("clip.view_zoom_in")
layout.operator("clip.view_zoom_out")
layout.separator()
layout.prop(sc, "show_metadata")
layout.separator()
layout.operator("clip.view_all")
layout.operator("clip.view_selected")
layout.operator("clip.view_center_cursor")
layout.menu("CLIP_MT_view_zoom")
else:
layout.operator_context = 'INVOKE_REGION_PREVIEW'
layout.operator("clip.graph_view_all")
if sc.view == 'GRAPH':
layout.operator("clip.graph_center_current_frame")
layout.operator("view2d.zoom_border", text="Zoom")
layout.operator_context = 'INVOKE_DEFAULT'
layout.separator()

View File

@ -83,29 +83,16 @@ class IMAGE_MT_view(Menu):
layout.prop(sima, "use_realtime_update")
layout.prop(uv, "show_metadata")
if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'):
layout.prop(uv, "show_texpaint")
layout.prop(tool_settings, "show_uv_local_view", text="Show Same Material")
layout.separator()
layout.operator("image.view_zoom_in")
layout.operator("image.view_zoom_out")
layout.separator()
layout.menu("IMAGE_MT_view_zoom")
layout.separator()
if show_uvedit:
layout.operator("image.view_selected", text="Frame Selected")
layout.operator("image.view_all")
layout.operator("image.view_all", text="Frame All Fit").fit_view = True
layout.operator("image.view_center_cursor", text="Center View to Cursor")
layout.menu("IMAGE_MT_view_zoom")
layout.separator()
if show_render:
@ -118,27 +105,38 @@ class IMAGE_MT_view(Menu):
layout.operator("image.cycle_render_slot", text="Render Slot Cycle Previous").reverse = True
layout.separator()
if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'):
layout.prop(uv, "show_texpaint")
layout.prop(tool_settings, "show_uv_local_view", text="Show Same Material")
layout.menu("INFO_MT_area")
class IMAGE_MT_view_zoom(Menu):
bl_label = "Fractional Zoom"
bl_label = "Zoom"
def draw(self, _context):
layout = self.layout
from math import isclose
current_zoom = _context.space_data.zoom_percentage
ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
for i, (a, b) in enumerate(ratios):
if i in {3, 4}: # Draw separators around Zoom 1:1.
layout.separator()
percent = a / b * 100
layout.operator(
"image.view_zoom_ratio",
text=iface_("Zoom %d:%d") % (a, b),
text=iface_("%g%% (%d:%d)") % (percent, a, b),
translate=False,
icon=('NONE', 'LAYER_ACTIVE')[isclose(percent, current_zoom, abs_tol=0.5)]
).ratio = a / b
layout.separator()
layout.operator("image.view_zoom_in")
layout.operator("image.view_zoom_out")
layout.operator("image.view_all", text="Zoom to Fit").fit_view = True
layout.operator("image.view_zoom_border", text="Zoom Region...")
class IMAGE_MT_select(Menu):
bl_label = "Select"
@ -714,10 +712,6 @@ class IMAGE_HT_tool_header(Header):
layout.popover("IMAGE_PT_tools_brush_display")
layout.popover("IMAGE_PT_tools_brush_texture")
layout.popover("IMAGE_PT_tools_mask_texture")
elif tool_mode == 'UV':
if (tool is not None) and tool.has_datablock:
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_mode_settings(self, context):
layout = self.layout
@ -1344,50 +1338,21 @@ class IMAGE_PT_tools_imagepaint_symmetry(BrushButtonsPanel, Panel):
row.prop(ipaint, "tile_y", text="Y", toggle=True)
class UVSculptPanel(UnifiedPaintPanel):
@classmethod
def poll(cls, context):
return cls.get_brush_mode(context) == 'UV_SCULPT'
class IMAGE_PT_uv_sculpt_brush_select(Panel, BrushSelectPanel, ImagePaintPanel, UVSculptPanel):
bl_context = ".uv_sculpt"
bl_category = "Tool"
bl_label = "Brushes"
class IMAGE_PT_uv_sculpt_brush_settings(Panel, ImagePaintPanel, UVSculptPanel):
bl_context = ".uv_sculpt"
bl_category = "Tool"
bl_label = "Brush Settings"
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
uvsculpt = tool_settings.uv_sculpt
brush = uvsculpt.brush
brush_settings(layout.column(), context, brush)
if brush:
if brush.uv_sculpt_tool == 'RELAX':
# Although this settings is stored in the scene,
# it is only used by a single tool,
# so it doesn't make sense from a user perspective to move it to the Options panel.
layout.prop(tool_settings, "uv_relax_method")
class IMAGE_PT_uv_sculpt_curve(Panel, FalloffPanel, ImagePaintPanel, UVSculptPanel):
class IMAGE_PT_uv_sculpt_curve(Panel, ImagePaintPanel):
bl_context = ".uv_sculpt" # Dot on purpose (access from top-bar).
bl_parent_id = "IMAGE_PT_uv_sculpt_brush_settings"
bl_category = "Tool"
bl_label = "Falloff"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
props = context.scene.tool_settings.uv_sculpt
layout.prop(props, "curve_preset", text="")
if props.curve_preset == 'CUSTOM':
layout.template_curve_mapping(props, "strength_curve")
class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel):
class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel):
bl_context = ".uv_sculpt" # Dot on purpose (access from top-bar).
bl_category = "Tool"
bl_label = "Options"
@ -1396,12 +1361,10 @@ class IMAGE_PT_uv_sculpt_options(Panel, ImagePaintPanel, UVSculptPanel):
layout = self.layout
tool_settings = context.tool_settings
uvsculpt = tool_settings.uv_sculpt
col = layout.column()
col.prop(tool_settings, "uv_sculpt_lock_borders")
col.prop(tool_settings, "uv_sculpt_all_islands")
col.prop(uvsculpt, "show_brush", text="Display Cursor")
class ImageScopesPanel:
@ -1776,8 +1739,6 @@ classes = (
IMAGE_PT_paint_curve,
IMAGE_PT_tools_brush_display,
IMAGE_PT_tools_imagepaint_symmetry,
IMAGE_PT_uv_sculpt_brush_select,
IMAGE_PT_uv_sculpt_brush_settings,
IMAGE_PT_uv_sculpt_options,
IMAGE_PT_uv_sculpt_curve,
IMAGE_PT_view_histogram,

View File

@ -171,6 +171,7 @@ class NODE_HT_header(Header):
if snode.node_tree:
layout.popover(panel="NODE_PT_geometry_node_tool_object_types", text="Types")
layout.popover(panel="NODE_PT_geometry_node_tool_mode", text="Modes")
layout.popover(panel="NODE_PT_geometry_node_tool_options", text="Options")
display_pin = False
else:
# Custom node tree is edited as independent ID block
@ -490,6 +491,21 @@ class NODE_PT_geometry_node_tool_mode(Panel):
row.prop(group, prop, text="")
class NODE_PT_geometry_node_tool_options(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'HEADER'
bl_label = "Options"
bl_ui_units_x = 8
def draw(self, context):
layout = self.layout
snode = context.space_data
group = snode.node_tree
layout.prop(group, "use_wait_for_click")
class NODE_PT_node_color_presets(PresetPanel, Panel):
"""Predefined node color"""
bl_label = "Color Presets"
@ -1320,6 +1336,7 @@ classes = (
NODE_PT_material_slots,
NODE_PT_geometry_node_tool_object_types,
NODE_PT_geometry_node_tool_mode,
NODE_PT_geometry_node_tool_options,
NODE_PT_node_color_presets,
NODE_MT_node_tree_interface_context_menu,
NODE_PT_node_tree_interface,

View File

@ -366,24 +366,30 @@ class SEQUENCER_MT_range(Menu):
class SEQUENCER_MT_preview_zoom(Menu):
bl_label = "Fractional Zoom"
bl_label = "Zoom"
def draw(self, _context):
layout = self.layout
layout.operator_context = 'INVOKE_REGION_PREVIEW'
from math import isclose
current_zoom = _context.space_data.zoom_percentage
ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1))
for i, (a, b) in enumerate(ratios):
if i in {3, 4}: # Draw separators around Zoom 1:1.
layout.separator()
percent = a / b * 100
layout.operator(
"sequencer.view_zoom_ratio",
text=iface_("Zoom %d:%d") % (a, b),
text=iface_("%g%% (%d:%d)") % (percent, a, b),
translate=False,
icon=('NONE', 'LAYER_ACTIVE')[isclose(percent, current_zoom, abs_tol=0.5)],
).ratio = a / b
layout.operator_context = 'INVOKE_DEFAULT'
layout.separator()
layout.operator("view2d.zoom_in")
layout.operator("view2d.zoom_out")
layout.operator("view2d.zoom_border", text="Zoom Region...")
class SEQUENCER_MT_proxy(Menu):
@ -444,7 +450,6 @@ class SEQUENCER_MT_view(Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("sequencer.view_all")
layout.operator("sequencer.view_frame")
layout.operator("view2d.zoom_border", text="Zoom to Border")
layout.prop(st, "use_clamp_view")
if is_preview:
@ -453,12 +458,10 @@ class SEQUENCER_MT_view(Menu):
layout.operator_context = 'INVOKE_REGION_PREVIEW'
layout.operator("sequencer.view_all_preview", text="Fit Preview in Window")
if is_sequencer_view:
layout.menu("SEQUENCER_MT_preview_zoom", text="Fractional Preview Zoom")
layout.menu("SEQUENCER_MT_preview_zoom", text="Preview Zoom")
else:
layout.operator("view2d.zoom_border", text="Zoom to Border")
layout.menu("SEQUENCER_MT_preview_zoom")
layout.prop(st, "use_zoom_to_fit")
layout.prop(st, "use_zoom_to_fit", text="Auto Zoom")
layout.separator()
layout.menu("SEQUENCER_MT_proxy")
layout.operator_context = 'INVOKE_DEFAULT'

View File

@ -2181,36 +2181,82 @@ class _defs_image_uv_edit:
class _defs_image_uv_sculpt:
@staticmethod
def generate_from_brushes(context):
def draw_cursor(context, _tool, xy):
@ToolDef.from_fn
def grab():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
tool_settings = context.tool_settings
uv_sculpt = tool_settings.uv_sculpt
if not uv_sculpt.show_brush:
return
ups = tool_settings.unified_paint_settings
if ups.use_unified_size:
radius = ups.size
else:
brush = tool_settings.uv_sculpt.brush
if brush is None:
return
radius = brush.size
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.uv_sculpt.",
type=bpy.types.Brush,
attr="uv_sculpt_tool",
tooldef_keywords=dict(
operator="sculpt.uv_sculpt_stroke",
keymap="Image Editor Tool: Uv, Sculpt Stroke",
draw_cursor=draw_cursor,
options={'KEYMAP_FALLBACK'},
),
return dict(
idname="sculpt.uv_sculpt_grab",
label="Grab",
icon="brush.uv_sculpt.grab",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def relax():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
props = tool.operator_properties("sculpt.uv_sculpt_relax")
layout.prop(props, "relax_method", text="Method")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return dict(
idname="sculpt.uv_sculpt_relax",
label="Relax",
icon="brush.uv_sculpt.relax",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def pinch():
def draw_settings(context, layout, tool):
uv_sculpt = context.scene.tool_settings.uv_sculpt
layout.prop(uv_sculpt, "size")
layout.prop(uv_sculpt, "strength")
layout.popover("IMAGE_PT_uv_sculpt_curve")
layout.popover("IMAGE_PT_uv_sculpt_options")
def draw_cursor(context, tool, xy):
from gpu_extras.presets import draw_circle_2d
uv_sculpt = context.scene.tool_settings.uv_sculpt
radius = uv_sculpt.size
draw_circle_2d(xy, (1.0,) * 4, radius)
return dict(
idname="sculpt.uv_sculpt_pinch",
label="Pinch",
icon="brush.uv_sculpt.pinch",
keymap=(),
draw_cursor=draw_cursor,
draw_settings=draw_settings,
options={'KEYMAP_FALLBACK'},
)
@ -3014,11 +3060,9 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel):
None,
_defs_image_uv_edit.rip_region,
None,
lambda context: (
_defs_image_uv_sculpt.generate_from_brushes(context)
if _defs_image_generic.poll_uvedit(context)
else ()
),
_defs_image_uv_sculpt.grab,
_defs_image_uv_sculpt.relax,
_defs_image_uv_sculpt.pinch,
],
'MASK': [
None,

View File

@ -1795,6 +1795,13 @@ class USERPREF_PT_input_touchpad(InputPanel, CenterAlignMixIn, Panel):
col = layout.column()
col.prop(inputs, "use_multitouch_gestures")
from _bpy import _wm_capabilities
capabilities = _wm_capabilities()
if not capabilities['TRACKPAD_PHYSICAL_DIRECTION']:
row = col.row()
row.active = inputs.use_multitouch_gestures
row.prop(inputs, "touchpad_scroll_direction", text="Scroll Direction")
class USERPREF_PT_input_tablet(InputPanel, CenterAlignMixIn, Panel):
bl_label = "Tablet"

View File

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

View File

@ -155,6 +155,14 @@ int clear_keyframe(Main *bmain,
int array_index,
eInsertKeyFlags /*flag*/);
/** Check if a flag is set for keyframing (per scene takes precedence). */
bool is_keying_flag(const Scene *scene, eKeying_Flag flag);
/**
* Get the settings for key-framing from the given scene.
*/
eInsertKeyFlags get_keyframing_flags(Scene *scene);
/** \} */
/* -------------------------------------------------------------------- */
@ -173,9 +181,6 @@ bool is_autokey_on(const Scene *scene);
/** Check the mode for auto-keyframing (per scene takes precedence). */
bool is_autokey_mode(const Scene *scene, eAutokey_Mode mode);
/** Check if a flag is set for keyframing (per scene takes precedence). */
bool is_keying_flag(const Scene *scene, eKeying_Flag flag);
/**
* Auto-keyframing feature - checks for whether anything should be done for the current frame.
*/

View File

@ -208,6 +208,27 @@ bool is_keying_flag(const Scene *scene, const eKeying_Flag flag)
return U.keying_flag & flag;
}
eInsertKeyFlags get_keyframing_flags(Scene *scene)
{
eInsertKeyFlags flag = INSERTKEY_NOFLAGS;
/* Visual keying. */
if (is_keying_flag(scene, KEYING_FLAG_VISUALKEY)) {
flag |= INSERTKEY_MATRIX;
}
/* Cycle-aware keyframe insertion - preserve cycle period and flow. */
if (is_keying_flag(scene, KEYING_FLAG_CYCLEAWARE)) {
flag |= INSERTKEY_CYCLE_AWARE;
}
if (is_keying_flag(scene, MANUALKEY_FLAG_INSERTNEEDED)) {
flag |= INSERTKEY_NEEDED;
}
return flag;
}
/** Used to make curves newly added to a cyclic Action cycle with the correct period. */
static void make_new_fcurve_cyclic(FCurve *fcu, const blender::float2 &action_range)
{

View File

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

View File

@ -10,6 +10,7 @@
* General operations for brushes.
*/
#include "DNA_brush_enums.h"
#include "DNA_color_types.h"
#include "DNA_object_enums.h"
@ -93,6 +94,10 @@ float BKE_brush_curve_strength_clamped(const Brush *br, float p, float len);
/**
* Uses the brush curve control to find a strength value.
*/
float BKE_brush_curve_strength(eBrushCurvePreset preset,
const CurveMapping *cumap,
float distance,
float brush_radius);
float BKE_brush_curve_strength(const Brush *br, float p, float len);
/* Sampling. */

View File

@ -35,6 +35,16 @@ namespace blender::bke {
namespace greasepencil {
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px`
* was the brush size which would be stored in the stroke thickness and then scaled by the
* point pressure factor. Finally, the render engine would divide this thickness value by
* 2000 (we're going from a thickness to a radius, hence the factor of two) to convert back
* into blender units. With Grease Pencil 3, the radius is no longer stored in `px` space,
* but in blender units (world space) directly. Also note that there is no longer a stroke
* "thickness" attribute, the radii are directly stored on the points.
* For compatibility, legacy thickness values have to be multiplied by this factor. */
constexpr float LEGACY_RADIUS_CONVERSION_FACTOR = 1.0f / 2000.0f;
class DrawingRuntime {
public:
/**

View File

@ -1278,6 +1278,8 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index
#define GEO_NODE_GRID_TO_MESH 2129
#define GEO_NODE_DISTRIBUTE_POINTS_IN_GRID 2130
#define GEO_NODE_SDF_GRID_BOOLEAN 2131
#define GEO_NODE_TOOL_VIEWPORT_TRANSFORM 2132
#define GEO_NODE_TOOL_MOUSE_POSITION 2133
/** \} */

View File

@ -96,7 +96,6 @@ enum class PaintMode : int8_t {
Texture3D = 3,
/** Image space (2D painting). */
Texture2D = 4,
SculptUV = 5,
GPencil = 6,
/* Grease Pencil Vertex Paint */
VertexGPencil = 7,
@ -111,8 +110,6 @@ enum class PaintMode : int8_t {
Invalid = 12,
};
#define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PaintMode::SculptUV)
/* overlay invalidation */
enum ePaintOverlayControlFlags {
PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY = 1,
@ -291,18 +288,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;
};
@ -587,7 +583,7 @@ struct SculptSession {
/* Pose Brush Preview */
blender::float3 pose_origin;
SculptPoseIKChain *pose_ik_chain_preview;
std::unique_ptr<SculptPoseIKChain> pose_ik_chain_preview;
/* Boundary Brush Preview */
SculptBoundary *boundary_preview;

View File

@ -2531,20 +2531,24 @@ void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
}
}
float BKE_brush_curve_strength(const Brush *br, float p, const float len)
float BKE_brush_curve_strength(const eBrushCurvePreset preset,
const CurveMapping *cumap,
const float distance,
const float brush_radius)
{
float p = distance;
float strength = 1.0f;
if (p >= len) {
if (p >= brush_radius) {
return 0;
}
p = p / len;
p = p / brush_radius;
p = 1.0f - p;
switch (br->curve_preset) {
switch (preset) {
case BRUSH_CURVE_CUSTOM:
strength = BKE_curvemapping_evaluateF(br->curve, 0, 1.0f - p);
strength = BKE_curvemapping_evaluateF(cumap, 0, 1.0f - p);
break;
case BRUSH_CURVE_SHARP:
strength = p * p;
@ -2578,6 +2582,11 @@ float BKE_brush_curve_strength(const Brush *br, float p, const float len)
return strength;
}
float BKE_brush_curve_strength(const Brush *br, float p, const float len)
{
return BKE_brush_curve_strength(eBrushCurvePreset(br->curve_preset), br->curve, p, len);
}
float BKE_brush_curve_strength_clamped(const Brush *br, float p, const float len)
{
float strength = BKE_brush_curve_strength(br, p, len);

View File

@ -813,13 +813,7 @@ static void legacy_gpencil_frame_to_grease_pencil_drawing(const bGPDframe &gpf,
BLI_assert(points.size() == gps->totpoints);
const Span<bGPDspoint> src_points{gps->points, gps->totpoints};
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px`
* was the brush size which would be stored in the stroke thickness and then scaled by the
* point pressure factor. Finally, the render engine would divide this thickness value by
* 2000 (we're going from a thickness to a radius, hence the factor of two) to convert back
* into blender units. Store the radius now directly in blender units. This makes it
* consistent with how hair curves handle the radius. */
const float stroke_thickness = float(gps->thickness) / 2000.0f;
const float stroke_thickness = float(gps->thickness) * LEGACY_RADIUS_CONVERSION_FACTOR;
MutableSpan<float3> dst_positions = positions.slice(points);
MutableSpan<float3> dst_handle_positions_left = has_bezier_stroke ?
handle_positions_left.slice(points) :
@ -1213,6 +1207,61 @@ static void thickness_factor_to_modifier(ConversionData &conversion_data,
animdata_thickness_transfer.fcurves_convert_finalize();
}
static void fcurve_convert_thickness_cb(FCurve &fcurve)
{
if (fcurve.bezt) {
for (uint i = 0; i < fcurve.totvert; i++) {
BezTriple &bezier_triple = fcurve.bezt[i];
bezier_triple.vec[0][1] *= LEGACY_RADIUS_CONVERSION_FACTOR;
bezier_triple.vec[1][1] *= LEGACY_RADIUS_CONVERSION_FACTOR;
bezier_triple.vec[2][1] *= LEGACY_RADIUS_CONVERSION_FACTOR;
}
}
if (fcurve.fpt) {
for (uint i = 0; i < fcurve.totvert; i++) {
FPoint &fpoint = fcurve.fpt[i];
fpoint.vec[1] *= LEGACY_RADIUS_CONVERSION_FACTOR;
}
}
fcurve.flag &= ~FCURVE_INT_VALUES;
BKE_fcurve_handles_recalc(&fcurve);
}
static void legacy_object_thickness_modifier_thickness_anim(ConversionData &conversion_data,
Object &object)
{
if (BKE_animdata_from_id(&object.id) == nullptr) {
return;
}
/* Note: At this point, the animation was already transferred to the destination object. Now we
* just need to convert the fcurve data to be in the right space. */
AnimDataConvertor animdata_convert_thickness(
conversion_data,
object.id,
object.id,
{{".thickness", ".thickness", fcurve_convert_thickness_cb}});
LISTBASE_FOREACH (ModifierData *, tmd, &object.modifiers) {
if (ModifierType(tmd->type) != eModifierType_GreasePencilThickness) {
continue;
}
char modifier_name[MAX_NAME * 2];
BLI_str_escape(modifier_name, tmd->name, sizeof(modifier_name));
animdata_convert_thickness.root_path_src = fmt::format("modifiers[\"{}\"]", modifier_name);
animdata_convert_thickness.root_path_dst = fmt::format("modifiers[\"{}\"]", modifier_name);
if (!animdata_convert_thickness.source_has_animation_to_convert()) {
continue;
}
animdata_convert_thickness.fcurves_convert();
}
animdata_convert_thickness.fcurves_convert_finalize();
DEG_relations_tag_update(&conversion_data.bmain);
}
static void layer_adjustments_to_modifiers(ConversionData &conversion_data,
bGPdata &src_object_data,
Object &dst_object)
@ -1225,26 +1274,6 @@ static void layer_adjustments_to_modifiers(ConversionData &conversion_data,
src_object_data.id,
{{".tint_color", ".color"}, {".tint_factor", ".factor"}});
/* Ensure values are divided by 2k, to match conversion done for non-animated value. */
constexpr float thickness_adjustement_factor = 1.0f / 2000.0f;
auto fcurve_convert_thickness_cb = [&](FCurve &fcurve) {
if (fcurve.bezt) {
for (uint i = 0; i < fcurve.totvert; i++) {
BezTriple &bezier_triple = fcurve.bezt[i];
bezier_triple.vec[0][1] *= thickness_adjustement_factor;
bezier_triple.vec[1][1] *= thickness_adjustement_factor;
bezier_triple.vec[2][1] *= thickness_adjustement_factor;
}
}
if (fcurve.fpt) {
for (uint i = 0; i < fcurve.totvert; i++) {
FPoint &fpoint = fcurve.fpt[i];
fpoint.vec[1] *= thickness_adjustement_factor;
}
}
fcurve.flag &= ~FCURVE_INT_VALUES;
BKE_fcurve_handles_recalc(&fcurve);
};
AnimDataConvertor animdata_thickness_transfer(
conversion_data,
dst_object.id,
@ -1301,7 +1330,7 @@ static void layer_adjustments_to_modifiers(ConversionData &conversion_data,
/* Convert the "pixel" offset value into a radius value.
* GPv2 used a conversion of 1 "px" = 0.001. */
/* Note: this offset may be negative. */
const float radius_offset = float(thickness_px) * thickness_adjustement_factor;
const float radius_offset = float(thickness_px) * LEGACY_RADIUS_CONVERSION_FACTOR;
const auto offset_radius_ntree_ensure = [&](Library *owner_library) {
if (bNodeTree **ntree = conversion_data.offset_radius_ntree_by_library.lookup_ptr(
@ -2252,7 +2281,7 @@ static void legacy_object_modifier_thickness(ConversionData &conversion_data,
md_thickness.flag |= MOD_GREASE_PENCIL_THICK_WEIGHT_FACTOR;
}
md_thickness.thickness_fac = legacy_md_thickness.thickness_fac;
md_thickness.thickness = legacy_md_thickness.thickness;
md_thickness.thickness = legacy_md_thickness.thickness * LEGACY_RADIUS_CONVERSION_FACTOR;
legacy_object_modifier_influence(md_thickness.influence,
legacy_md_thickness.layername,
@ -2914,6 +2943,8 @@ static void legacy_gpencil_object_ex(ConversionData &conversion_data, Object &ob
}
legacy_object_modifiers(conversion_data, object);
/* Convert the animation of the "uniform thickness" setting of the thickness modifier. */
legacy_object_thickness_modifier_thickness_anim(conversion_data, object);
/* Layer adjustments should be added after all other modifiers. */
layer_adjustments_to_modifiers(conversion_data, *gpd, object);
@ -2975,95 +3006,87 @@ void legacy_main(Main &bmain, BlendFileReadReport & /*reports*/)
void lineart_wrap_v3(const LineartGpencilModifierData *lmd_legacy,
GreasePencilLineartModifierData *lmd)
{
#define LMD_WRAP(var) lmd->var = lmd_legacy->var
LMD_WRAP(edge_types);
LMD_WRAP(source_type);
LMD_WRAP(use_multiple_levels);
LMD_WRAP(level_start);
LMD_WRAP(level_end);
LMD_WRAP(source_camera);
LMD_WRAP(light_contour_object);
LMD_WRAP(source_object);
LMD_WRAP(source_collection);
LMD_WRAP(target_material);
lmd->edge_types = lmd_legacy->edge_types;
lmd->source_type = lmd_legacy->source_type;
lmd->use_multiple_levels = lmd_legacy->use_multiple_levels;
lmd->level_start = lmd_legacy->level_start;
lmd->level_end = lmd_legacy->level_end;
lmd->source_camera = lmd_legacy->source_camera;
lmd->light_contour_object = lmd_legacy->light_contour_object;
lmd->source_object = lmd_legacy->source_object;
lmd->source_collection = lmd_legacy->source_collection;
lmd->target_material = lmd_legacy->target_material;
STRNCPY(lmd->source_vertex_group, lmd_legacy->source_vertex_group);
STRNCPY(lmd->vgname, lmd_legacy->vgname);
LMD_WRAP(overscan);
LMD_WRAP(shadow_camera_fov);
LMD_WRAP(shadow_camera_size);
LMD_WRAP(shadow_camera_near);
LMD_WRAP(shadow_camera_far);
LMD_WRAP(opacity);
lmd->overscan = lmd_legacy->overscan;
lmd->shadow_camera_fov = lmd_legacy->shadow_camera_fov;
lmd->shadow_camera_size = lmd_legacy->shadow_camera_size;
lmd->shadow_camera_near = lmd_legacy->shadow_camera_near;
lmd->shadow_camera_far = lmd_legacy->shadow_camera_far;
lmd->opacity = lmd_legacy->opacity;
lmd->thickness = lmd_legacy->thickness / 2;
LMD_WRAP(mask_switches);
LMD_WRAP(material_mask_bits);
LMD_WRAP(intersection_mask);
LMD_WRAP(shadow_selection);
LMD_WRAP(silhouette_selection);
LMD_WRAP(crease_threshold);
LMD_WRAP(angle_splitting_threshold);
LMD_WRAP(chain_smooth_tolerance);
LMD_WRAP(chaining_image_threshold);
LMD_WRAP(calculation_flags);
LMD_WRAP(flags);
LMD_WRAP(stroke_depth_offset);
LMD_WRAP(level_start_override);
LMD_WRAP(level_end_override);
LMD_WRAP(edge_types_override);
LMD_WRAP(shadow_selection_override);
LMD_WRAP(shadow_use_silhouette_override);
LMD_WRAP(cache);
LMD_WRAP(la_data_ptr);
#undef LMD_WRAP
lmd->mask_switches = lmd_legacy->mask_switches;
lmd->material_mask_bits = lmd_legacy->material_mask_bits;
lmd->intersection_mask = lmd_legacy->intersection_mask;
lmd->shadow_selection = lmd_legacy->shadow_selection;
lmd->silhouette_selection = lmd_legacy->silhouette_selection;
lmd->crease_threshold = lmd_legacy->crease_threshold;
lmd->angle_splitting_threshold = lmd_legacy->angle_splitting_threshold;
lmd->chain_smooth_tolerance = lmd_legacy->chain_smooth_tolerance;
lmd->chaining_image_threshold = lmd_legacy->chaining_image_threshold;
lmd->calculation_flags = lmd_legacy->calculation_flags;
lmd->flags = lmd_legacy->flags;
lmd->stroke_depth_offset = lmd_legacy->stroke_depth_offset;
lmd->level_start_override = lmd_legacy->level_start_override;
lmd->level_end_override = lmd_legacy->level_end_override;
lmd->edge_types_override = lmd_legacy->edge_types_override;
lmd->shadow_selection_override = lmd_legacy->shadow_selection_override;
lmd->shadow_use_silhouette_override = lmd_legacy->shadow_use_silhouette_override;
lmd->cache = lmd_legacy->cache;
lmd->la_data_ptr = lmd_legacy->la_data_ptr;
}
void lineart_unwrap_v3(LineartGpencilModifierData *lmd_legacy,
const GreasePencilLineartModifierData *lmd)
{
#define LMD_UNWRAP(var) lmd_legacy->var = lmd->var
LMD_UNWRAP(edge_types);
LMD_UNWRAP(source_type);
LMD_UNWRAP(use_multiple_levels);
LMD_UNWRAP(level_start);
LMD_UNWRAP(level_end);
LMD_UNWRAP(source_camera);
LMD_UNWRAP(light_contour_object);
LMD_UNWRAP(source_object);
LMD_UNWRAP(source_collection);
LMD_UNWRAP(target_material);
lmd_legacy->edge_types = lmd->edge_types;
lmd_legacy->source_type = lmd->source_type;
lmd_legacy->use_multiple_levels = lmd->use_multiple_levels;
lmd_legacy->level_start = lmd->level_start;
lmd_legacy->level_end = lmd->level_end;
lmd_legacy->source_camera = lmd->source_camera;
lmd_legacy->light_contour_object = lmd->light_contour_object;
lmd_legacy->source_object = lmd->source_object;
lmd_legacy->source_collection = lmd->source_collection;
lmd_legacy->target_material = lmd->target_material;
STRNCPY(lmd_legacy->source_vertex_group, lmd->source_vertex_group);
STRNCPY(lmd_legacy->vgname, lmd->vgname);
LMD_UNWRAP(overscan);
LMD_UNWRAP(shadow_camera_fov);
LMD_UNWRAP(shadow_camera_size);
LMD_UNWRAP(shadow_camera_near);
LMD_UNWRAP(shadow_camera_far);
LMD_UNWRAP(opacity);
lmd_legacy->overscan = lmd->overscan;
lmd_legacy->shadow_camera_fov = lmd->shadow_camera_fov;
lmd_legacy->shadow_camera_size = lmd->shadow_camera_size;
lmd_legacy->shadow_camera_near = lmd->shadow_camera_near;
lmd_legacy->shadow_camera_far = lmd->shadow_camera_far;
lmd_legacy->opacity = lmd->opacity;
lmd_legacy->thickness = lmd->thickness * 2;
LMD_UNWRAP(mask_switches);
LMD_UNWRAP(material_mask_bits);
LMD_UNWRAP(intersection_mask);
LMD_UNWRAP(shadow_selection);
LMD_UNWRAP(silhouette_selection);
LMD_UNWRAP(crease_threshold);
LMD_UNWRAP(angle_splitting_threshold);
LMD_UNWRAP(chain_smooth_tolerance);
LMD_UNWRAP(chaining_image_threshold);
LMD_UNWRAP(calculation_flags);
LMD_UNWRAP(flags);
LMD_UNWRAP(stroke_depth_offset);
LMD_UNWRAP(level_start_override);
LMD_UNWRAP(level_end_override);
LMD_UNWRAP(edge_types_override);
LMD_UNWRAP(shadow_selection_override);
LMD_UNWRAP(shadow_use_silhouette_override);
LMD_UNWRAP(cache);
LMD_UNWRAP(la_data_ptr);
#undef LMD_UNWRAP
lmd_legacy->mask_switches = lmd->mask_switches;
lmd_legacy->material_mask_bits = lmd->material_mask_bits;
lmd_legacy->intersection_mask = lmd->intersection_mask;
lmd_legacy->shadow_selection = lmd->shadow_selection;
lmd_legacy->silhouette_selection = lmd->silhouette_selection;
lmd_legacy->crease_threshold = lmd->crease_threshold;
lmd_legacy->angle_splitting_threshold = lmd->angle_splitting_threshold;
lmd_legacy->chain_smooth_tolerance = lmd->chain_smooth_tolerance;
lmd_legacy->chaining_image_threshold = lmd->chaining_image_threshold;
lmd_legacy->calculation_flags = lmd->calculation_flags;
lmd_legacy->flags = lmd->flags;
lmd_legacy->stroke_depth_offset = lmd->stroke_depth_offset;
lmd_legacy->level_start_override = lmd->level_start_override;
lmd_legacy->level_end_override = lmd->level_end_override;
lmd_legacy->edge_types_override = lmd->edge_types_override;
lmd_legacy->shadow_selection_override = lmd->shadow_selection_override;
lmd_legacy->shadow_use_silhouette_override = lmd->shadow_use_silhouette_override;
lmd_legacy->cache = lmd->cache;
lmd_legacy->la_data_ptr = lmd->la_data_ptr;
}
} // namespace blender::bke::greasepencil::convert

View File

@ -346,9 +346,6 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, PaintMode mode)
paint_tmp = (Paint *)&ts->imapaint;
paint_ptr = &paint_tmp;
break;
case PaintMode::SculptUV:
paint_ptr = (Paint **)&ts->uvsculpt;
break;
case PaintMode::GPencil:
paint_ptr = (Paint **)&ts->gp_paint;
break;
@ -392,8 +389,6 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return &ts->imapaint.paint;
case PaintMode::SculptUV:
return &ts->uvsculpt->paint;
case PaintMode::GPencil:
return &ts->gp_paint->paint;
case PaintMode::VertexGPencil:
@ -428,8 +423,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return rna_enum_brush_image_tool_items;
case PaintMode::SculptUV:
return rna_enum_brush_uv_sculpt_tool_items;
case PaintMode::GPencil:
return rna_enum_brush_gpencil_types_items;
case PaintMode::VertexGPencil:
@ -460,8 +453,6 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return "image_tool";
case PaintMode::SculptUV:
return "uv_sculpt_tool";
case PaintMode::GPencil:
return "gpencil_tool";
case PaintMode::VertexGPencil:
@ -492,7 +483,6 @@ const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const Pai
return BLT_I18NCONTEXT_ID_BRUSH;
case PaintMode::Vertex:
case PaintMode::Weight:
case PaintMode::SculptUV:
case PaintMode::VertexGPencil:
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:
@ -533,8 +523,6 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
return &ts->gp_weightpaint->paint;
case OB_MODE_SCULPT_CURVES:
return &ts->curves_sculpt->paint;
case OB_MODE_EDIT:
return ts->uvsculpt ? &ts->uvsculpt->paint : nullptr;
default:
break;
}
@ -563,9 +551,6 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
if (sima->mode == SI_MODE_PAINT) {
return &ts->imapaint.paint;
}
if (sima->mode == SI_MODE_UV) {
return &ts->uvsculpt->paint;
}
}
else {
return &ts->imapaint.paint;
@ -594,9 +579,6 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
if (sima->mode == SI_MODE_PAINT) {
return PaintMode::Texture2D;
}
if (sima->mode == SI_MODE_UV) {
return PaintMode::SculptUV;
}
}
else {
return PaintMode::Texture2D;
@ -624,8 +606,6 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
return PaintMode::Weight;
case OB_MODE_TEXTURE_PAINT:
return PaintMode::Texture3D;
case OB_MODE_EDIT:
return PaintMode::SculptUV;
case OB_MODE_SCULPT_CURVES:
return PaintMode::SculptCurves;
default:
@ -674,8 +654,6 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
switch (tref->mode) {
case SI_MODE_PAINT:
return PaintMode::Texture2D;
case SI_MODE_UV:
return PaintMode::SculptUV;
}
}
@ -721,10 +699,6 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
}
else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) {
paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool);
paint->runtime.ob_mode = OB_MODE_EDIT;
}
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
@ -762,8 +736,6 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
return offsetof(Brush, vertexpaint_tool);
case PaintMode::Weight:
return offsetof(Brush, weightpaint_tool);
case PaintMode::SculptUV:
return offsetof(Brush, uv_sculpt_tool);
case PaintMode::GPencil:
return offsetof(Brush, gpencil_tool);
case PaintMode::VertexGPencil:
@ -1104,8 +1076,6 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return OB_MODE_TEXTURE_PAINT;
case PaintMode::SculptUV:
return OB_MODE_EDIT;
case PaintMode::SculptCurves:
return OB_MODE_SCULPT_CURVES;
case PaintMode::GPencil:
@ -1140,7 +1110,6 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
(Paint *)ts->sculpt,
(Paint *)ts->vpaint,
(Paint *)ts->wpaint,
(Paint *)ts->uvsculpt,
(Paint *)ts->curves_sculpt,
(Paint *)&ts->imapaint));
#ifndef NDEBUG
@ -1182,10 +1151,6 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
GpWeightPaint *data = MEM_cnew<GpWeightPaint>(__func__);
paint = &data->paint;
}
else if ((UvSculpt **)r_paint == &ts->uvsculpt) {
UvSculpt *data = MEM_cnew<UvSculpt>(__func__);
paint = &data->paint;
}
else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) {
CurvesSculpt *data = MEM_cnew<CurvesSculpt>(__func__);
paint = &data->paint;
@ -1211,17 +1176,15 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
BKE_paint_ensure_from_paintmode(sce, mode);
/* If there's no brush, create one */
if (PAINT_MODE_HAS_BRUSH(mode)) {
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
}
copy_v3_v3_uchar(paint->paint_cursor_col, col);
@ -1529,14 +1492,6 @@ void BKE_sculptsession_free(Object *ob)
BKE_image_pool_free(ss->tex_pool);
}
if (ss->pose_ik_chain_preview) {
for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights);
}
MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments);
MEM_SAFE_FREE(ss->pose_ik_chain_preview);
}
if (ss->boundary_preview) {
MEM_SAFE_FREE(ss->boundary_preview->verts);
MEM_SAFE_FREE(ss->boundary_preview->edges);

View File

@ -89,9 +89,6 @@ void BKE_paint_toolslots_init_from_main(Main *bmain)
if (ts->wpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->wpaint->paint);
}
if (ts->uvsculpt) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->uvsculpt->paint);
}
if (ts->gp_paint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_paint->paint);
}

View File

@ -725,14 +725,6 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
}
toolsett_old->sculpt->gravity_object = gravity_object_old;
}
if (toolsett_old->uvsculpt) {
paint = toolsett->uvsculpt ? &toolsett->uvsculpt->paint : nullptr;
paint_old = &toolsett_old->uvsculpt->paint;
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL(
data,
do_undo_restore,
scene_foreach_paint(data, paint, do_undo_restore, reader, paint_old));
}
if (toolsett_old->gp_paint) {
paint = toolsett->gp_paint ? &toolsett->gp_paint->paint : nullptr;
paint_old = &toolsett_old->gp_paint->paint;
@ -1055,9 +1047,8 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
BKE_paint_blend_write(writer, &tos->sculpt->paint);
}
if (tos->uvsculpt) {
BLO_write_struct(writer, UvSculpt, tos->uvsculpt);
BKE_paint_blend_write(writer, &tos->uvsculpt->paint);
if (tos->uvsculpt.strength_curve) {
BKE_curvemapping_blend_write(writer, tos->uvsculpt.strength_curve);
}
if (tos->gp_paint) {
BLO_write_struct(writer, GpPaint, tos->gp_paint);
@ -1259,7 +1250,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->sculpt);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->vpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->wpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->uvsculpt);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_paint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_vertexpaint);
direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_sculptpaint);
@ -1272,6 +1262,11 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
sce->toolsettings->particle.scene = nullptr;
sce->toolsettings->particle.object = nullptr;
sce->toolsettings->gp_sculpt.paintcursor = nullptr;
if (sce->toolsettings->uvsculpt.strength_curve) {
BLO_read_struct(reader, CurveMapping, &sce->toolsettings->uvsculpt.strength_curve);
BKE_curvemapping_blend_read(reader, sce->toolsettings->uvsculpt.strength_curve);
BKE_curvemapping_init(sce->toolsettings->uvsculpt.strength_curve);
}
if (sce->toolsettings->sculpt) {
BLO_read_struct(reader, CurveMapping, &sce->toolsettings->sculpt->automasking_cavity_curve);
@ -1671,9 +1666,9 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
BKE_curvemapping_init(ts->sculpt->automasking_cavity_curve_op);
}
}
if (ts->uvsculpt) {
ts->uvsculpt = static_cast<UvSculpt *>(MEM_dupallocN(ts->uvsculpt));
BKE_paint_copy(&ts->uvsculpt->paint, &ts->uvsculpt->paint, flag);
if (ts->uvsculpt.strength_curve) {
ts->uvsculpt.strength_curve = BKE_curvemapping_copy(ts->uvsculpt.strength_curve);
BKE_curvemapping_init(ts->uvsculpt.strength_curve);
}
if (ts->gp_paint) {
ts->gp_paint = static_cast<GpPaint *>(MEM_dupallocN(ts->gp_paint));
@ -1737,9 +1732,8 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_paint_free(&toolsettings->sculpt->paint);
MEM_freeN(toolsettings->sculpt);
}
if (toolsettings->uvsculpt) {
BKE_paint_free(&toolsettings->uvsculpt->paint);
MEM_freeN(toolsettings->uvsculpt);
if (toolsettings->uvsculpt.strength_curve) {
BKE_curvemapping_free(toolsettings->uvsculpt.strength_curve);
}
if (toolsettings->gp_paint) {
BKE_paint_free(&toolsettings->gp_paint->paint);

View File

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

View File

@ -797,9 +797,6 @@ static void do_version_curvemapping_walker(Main *bmain, void (*callback)(CurveMa
if (ts->sculpt) {
callback(ts->sculpt->paint.cavity_curve);
}
if (ts->uvsculpt) {
callback(ts->uvsculpt->paint.cavity_curve);
}
if (ts->gp_paint) {
callback(ts->gp_paint->paint.cavity_curve);
}

View File

@ -3491,12 +3491,6 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 302, 6)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts->uv_relax_method == 0) {
ts->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN;
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
tool_settings->snap_flag_seq = tool_settings->snap_flag &

View File

@ -1963,13 +1963,6 @@ static void versioning_nodes_dynamic_sockets_2(bNodeTree &ntree)
static void versioning_grease_pencil_stroke_radii_scaling(GreasePencil *grease_pencil)
{
using namespace blender;
/* Previously, Grease Pencil used a radius convention where 1 `px` = 0.001 units. This `px` was
* the brush size which would be stored in the stroke thickness and then scaled by the point
* pressure factor. Finally, the render engine would divide this thickness value by 2000 (we're
* going from a thickness to a radius, hence the factor of two) to convert back into blender
* units.
* Store the radius now directly in blender units. This makes it consistent with how hair curves
* handle the radius. */
for (GreasePencilDrawingBase *base : grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
@ -1978,7 +1971,7 @@ static void versioning_grease_pencil_stroke_radii_scaling(GreasePencil *grease_p
MutableSpan<float> radii = drawing.radii_for_write();
threading::parallel_for(radii.index_range(), 8192, [&](const IndexRange range) {
for (const int i : range) {
radii[i] /= 2000.0f;
radii[i] *= bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
}
});
}
@ -2953,9 +2946,6 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
input_sample_values[2] = ts->curves_sculpt != nullptr ?
ts->curves_sculpt->paint.num_input_samples_deprecated :
1;
input_sample_values[3] = ts->uvsculpt != nullptr ?
ts->uvsculpt->paint.num_input_samples_deprecated :
1;
input_sample_values[4] = ts->gp_paint != nullptr ?
ts->gp_paint->paint.num_input_samples_deprecated :
@ -3129,7 +3119,7 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 8)) {
LISTBASE_FOREACH (Light *, light, &bmain->lights) {
light->shadow_filter_radius = 3.0f;
light->shadow_filter_radius = 1.0f;
}
}
@ -3234,6 +3224,18 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 23)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (!ts->uvsculpt.strength_curve) {
ts->uvsculpt.size = 50;
ts->uvsculpt.strength = 1.0f;
ts->uvsculpt.curve_preset = BRUSH_CURVE_SMOOTH;
ts->uvsculpt.strength_curve = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

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

View File

@ -59,15 +59,17 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
this->color = float3(&la->r) * la->energy;
float3 scale;
this->object_mat = ob->object_to_world();
this->object_mat.view<3, 3>() = normalize_and_get_size(this->object_mat.view<3, 3>(), scale);
float4x4 object_to_world = ob->object_to_world();
object_to_world.view<3, 3>() = normalize_and_get_size(object_to_world.view<3, 3>(), scale);
/* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
float3 back = cross(float3(this->_right), float3(this->_up));
if (dot(back, float3(this->_back)) < 0.0f) {
negate_v3(this->_up);
float3 back = cross(float3(object_to_world.x_axis()), float3(object_to_world.y_axis()));
if (dot(back, float3(object_to_world.z_axis())) < 0.0f) {
negate_v3(object_to_world.y_axis());
}
this->object_to_world = object_to_world;
shape_parameters_set(la, scale, threshold);
const bool diffuse_visibility = (ob->visibility_flag & OB_HIDE_DIFFUSE) == 0;
@ -93,7 +95,7 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
if (la->mode & LA_SHADOW) {
shadow_ensure(shadows);
if (is_sun_light(this->type)) {
this->directional->sync(this->object_mat,
this->directional->sync(object_to_world,
1.0f,
la->sun_angle * la->shadow_softness_factor,
la->shadow_trace_distance);
@ -113,7 +115,7 @@ void Light::sync(ShadowModule &shadows, const Object *ob, float threshold)
shadow_radius = la->shadow_softness_factor * la->radius;
}
this->punctual->sync(this->type,
this->object_mat,
object_to_world,
la->spotsize,
radius,
this->local.influence_radius_max,
@ -293,7 +295,9 @@ float Light::point_radiance_get()
void Light::debug_draw()
{
#ifndef NDEBUG
drw_debug_sphere(float3(_position), local.influence_radius_max, float4(0.8f, 0.3f, 0.0f, 1.0f));
drw_debug_sphere(transform_location(this->object_to_world),
local.influence_radius_max,
float4(0.8f, 0.3f, 0.0f, 1.0f));
#endif
}

View File

@ -43,6 +43,80 @@ constexpr GPUSamplerState with_filter = {GPU_SAMPLER_FILTERING_LINEAR};
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
/* -------------------------------------------------------------------- */
/** \name Transform
* \{ */
struct Transform {
/* The transform is stored transposed for compactness. */
float4 x, y, z;
#if IS_CPP
Transform() = default;
Transform(const float4x4 &tx)
: x(tx[0][0], tx[1][0], tx[2][0], tx[3][0]),
y(tx[0][1], tx[1][1], tx[2][1], tx[3][1]),
z(tx[0][2], tx[1][2], tx[2][2], tx[3][2])
{
}
operator float4x4() const
{
return float4x4(float4(x.x, y.x, z.x, 0.0f),
float4(x.y, y.y, z.y, 0.0f),
float4(x.z, y.z, z.z, 0.0f),
float4(x.w, y.w, z.w, 1.0f));
}
#endif
};
static inline float3 transform_x_axis(Transform t)
{
return float3(t.x.x, t.y.x, t.z.x);
}
static inline float3 transform_y_axis(Transform t)
{
return float3(t.x.y, t.y.y, t.z.y);
}
static inline float3 transform_z_axis(Transform t)
{
return float3(t.x.z, t.y.z, t.z.z);
}
static inline float3 transform_location(Transform t)
{
return float3(t.x.w, t.y.w, t.z.w);
}
static inline float3 transform_point(Transform t, float3 point)
{
return float4(point, 1.0f) * float3x4(t.x, t.y, t.z);
}
static inline float3 transform_direction(Transform t, float3 direction)
{
return direction * float3x3(float3(t.x.x, t.x.y, t.x.z),
float3(t.y.x, t.y.y, t.y.z),
float3(t.z.x, t.z.y, t.z.z));
}
static inline float3 transform_direction_transposed(Transform t, float3 direction)
{
return float3x3(float3(t.x.x, t.x.y, t.x.z),
float3(t.y.x, t.y.y, t.y.z),
float3(t.z.x, t.z.y, t.z.z)) *
direction;
}
/* Assumes the transform has unit scale. */
static inline float3 transform_point_inversed(Transform t, float3 point)
{
return float3x3(float3(t.x.x, t.x.y, t.x.z),
float3(t.y.x, t.y.y, t.y.z),
float3(t.z.x, t.z.y, t.z.z)) *
(point - transform_location(t));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debug Mode
* \{ */
@ -870,21 +944,8 @@ BLI_STATIC_ASSERT(sizeof(LightSunData) == sizeof(LightLocalData), "Data size mus
#endif
struct LightData {
/** Normalized object to world matrix. */
/* TODO(fclem): Use float4x3. */
float4x4 object_mat;
/** Aliases for axes. */
#ifndef USE_GPU_SHADER_CREATE_INFO
# define _right object_mat[0]
# define _up object_mat[1]
# define _back object_mat[2]
# define _position object_mat[3]
#else
# define _right object_mat[0].xyz
# define _up object_mat[1].xyz
# define _back object_mat[2].xyz
# define _position object_mat[3].xyz
#endif
/** Normalized object to world matrix. Stored transposed for compactness. */
Transform object_to_world;
/** Power depending on shader type. Referenced by LightingType. */
float4 power;
@ -922,6 +983,23 @@ struct LightData {
};
BLI_STATIC_ASSERT_ALIGN(LightData, 16)
static inline float3 light_x_axis(LightData light)
{
return transform_x_axis(light.object_to_world);
}
static inline float3 light_y_axis(LightData light)
{
return transform_y_axis(light.object_to_world);
}
static inline float3 light_z_axis(LightData light)
{
return transform_z_axis(light.object_to_world);
}
static inline float3 light_position_get(LightData light)
{
return transform_location(light.object_to_world);
}
#ifdef GPU_SHADER
# define CHECK_TYPE_PAIR(a, b)
# define CHECK_TYPE(a, b)

View File

@ -370,11 +370,7 @@ void ShadowPunctual::end_sync(Light &light, float lod_bias)
compute_projection_boundaries(
light.type, light_radius_, shadow_radius_, max_distance_, near, far, side, shift);
float4x4 obmat_tmp = light.object_mat;
/* Clear embedded custom data. */
obmat_tmp[0][3] = obmat_tmp[1][3] = obmat_tmp[2][3] = 0.0f;
obmat_tmp[3][3] = 1.0f;
float4x4 obmat_tmp = light.object_to_world;
/* Acquire missing tile-maps. */
while (tilemaps_.size() < tilemaps_needed_) {
@ -519,7 +515,9 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
float2 farthest_tilemap_center = local_view_direction * half_size * (levels_range.size() - 1);
/* Offset for smooth level transitions. */
light.object_mat.location() = near_point;
light.object_to_world.x.w = near_point.x;
light.object_to_world.y.w = near_point.y;
light.object_to_world.z.w = near_point.z;
/* Offset in tiles from the scene origin to the center of the first tile-maps. */
int2 origin_offset = int2(round(float2(near_point) / tile_size));
@ -637,7 +635,10 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light,
light.type = LIGHT_SUN;
/* Used for selecting the clipmap level. */
light.object_mat.location() = camera.position() * float3x3(object_mat_.view<3, 3>());
float3 location = camera.position() * float3x3(object_mat_.view<3, 3>());
light.object_to_world.x.w = location.x;
light.object_to_world.y.w = location.y;
light.object_to_world.z.w = location.z;
/* Used as origin for the clipmap_base_offset trick. */
light.sun.clipmap_origin = float2(level_offset_max * tile_size_max);

View File

@ -117,10 +117,8 @@ void SyncModule::sync_mesh(Object *ob,
return;
}
if ((ob->dt < OB_SOLID) && !DRW_state_is_scene_render()) {
/** NOTE:
* EEVEE doesn't render meshes with bounds or wire display type in the viewport,
* but Cycles does. */
if ((ob->dt < OB_SOLID) && ((inst_.is_viewport() && inst_.v3d->shading.type != OB_RENDER))) {
/** Do not render objects with display type lower than solid when in material preview mode. */
return;
}

View File

@ -38,11 +38,14 @@ void main()
LightSpotData spot = light_spot_data_get(light);
/* Only for < ~170 degree Cone due to plane extraction precision. */
if (spot.spot_tan < 10.0) {
vec3 x_axis = light_x_axis(light);
vec3 y_axis = light_y_axis(light);
vec3 z_axis = light_z_axis(light);
Pyramid pyramid = shape_pyramid_non_oblique(
light._position,
light._position - light._back * spot.influence_radius_max,
light._right * spot.influence_radius_max * spot.spot_tan / spot.spot_size_inv.x,
light._up * spot.influence_radius_max * spot.spot_tan / spot.spot_size_inv.y);
light_position_get(light),
light_position_get(light) - z_axis * spot.influence_radius_max,
x_axis * spot.influence_radius_max * spot.spot_tan / spot.spot_size_inv.x,
y_axis * spot.influence_radius_max * spot.spot_tan / spot.spot_size_inv.y);
if (!intersect_view(pyramid)) {
return;
}
@ -52,7 +55,7 @@ void main()
case LIGHT_ELLIPSE:
case LIGHT_OMNI_SPHERE:
case LIGHT_OMNI_DISK:
sphere.center = light._position;
sphere.center = light_position_get(light);
sphere.radius = light_local_data_get(light).influence_radius_max;
break;
default:
@ -66,7 +69,7 @@ void main()
if (intersect_view(sphere)) {
uint index = atomicAdd(light_cull_buf.visible_count, 1u);
float z_dist = dot(drw_view_forward(), light._position) -
float z_dist = dot(drw_view_forward(), light_position_get(light)) -
dot(drw_view_forward(), drw_view_position());
out_zdist_buf[index] = z_dist;
out_key_buf[index] = l_idx;

View File

@ -148,10 +148,10 @@ void main()
LightData light = light_buf[l_idx];
/* Culling in view space for precision and simplicity. */
vec3 vP = drw_point_world_to_view(light._position);
vec3 v_right = drw_normal_world_to_view(light._right);
vec3 v_up = drw_normal_world_to_view(light._up);
vec3 v_back = drw_normal_world_to_view(light._back);
vec3 vP = drw_point_world_to_view(light_position_get(light));
vec3 v_right = drw_normal_world_to_view(light_x_axis(light));
vec3 v_up = drw_normal_world_to_view(light_y_axis(light));
vec3 v_back = drw_normal_world_to_view(light_z_axis(light));
float radius = light_local_data_get(light).influence_radius_max;
Sphere sphere = shape_sphere(vP, radius);

View File

@ -36,7 +36,7 @@ void main()
continue;
}
LightData light = light_buf[index];
vec3 P = light._position;
vec3 P = light_position_get(light);
/* TODO(fclem): Could have better bounds for spot and area lights. */
float radius = light_local_data_get(light).influence_radius_max;
float z_dist = dot(drw_view_forward(), P) - dot(drw_view_forward(), drw_view_position());

View File

@ -24,11 +24,11 @@ LightVector light_vector_get(LightData light, const bool is_directional, vec3 P)
{
LightVector lv;
if (is_directional) {
lv.L = light._back;
lv.L = light_z_axis(light);
lv.dist = 1.0;
}
else {
lv.L = light._position - P;
lv.L = light_position_get(light) - P;
float inv_distance = inversesqrt(length_squared(lv.L));
lv.L *= inv_distance;
lv.dist = 1.0 / inv_distance;
@ -42,21 +42,17 @@ LightVector light_shape_vector_get(LightData light, const bool is_directional, v
if (!is_directional && is_area_light(light.type)) {
LightAreaData area = light_area_data_get(light);
vec3 L = P - light._position;
vec2 closest_point = vec2(dot(light._right, L), dot(light._up, L));
closest_point /= area.size;
vec3 lP = transform_point_inversed(light.object_to_world, P);
vec2 ls_closest_point = lP.xy;
if (light.type == LIGHT_ELLIPSE) {
closest_point /= max(1.0, length(closest_point));
ls_closest_point /= max(1.0, length(ls_closest_point / area.size));
}
else {
closest_point = clamp(closest_point, -1.0, 1.0);
ls_closest_point = clamp(ls_closest_point, -area.size, area.size);
}
closest_point *= area.size;
vec3 ws_closest_point = transform_point(light.object_to_world, vec3(ls_closest_point, 0.0));
vec3 L_prime = light._right * closest_point.x + light._up * closest_point.y;
L = L_prime - L;
vec3 L = ws_closest_point - P;
float inv_distance = inversesqrt(length_squared(L));
LightVector lv;
lv.L = L * inv_distance;
@ -70,19 +66,7 @@ LightVector light_shape_vector_get(LightData light, const bool is_directional, v
/* Rotate vector to light's local space. Does not translate. */
vec3 light_world_to_local(LightData light, vec3 L)
{
/* Avoid relying on compiler to optimize this.
* vec3 lL = transpose(mat3(light.object_mat)) * L; */
vec3 lL;
lL.x = dot(light.object_mat[0].xyz, L);
lL.y = dot(light.object_mat[1].xyz, L);
lL.z = dot(light.object_mat[2].xyz, L);
return lL;
}
/* Transform position from light's local space to world space. Does translation. */
vec3 light_local_position_to_world(LightData light, vec3 lP)
{
return mat3(light.object_mat) * lP + light._position;
return transform_direction_transposed(light.object_to_world, L);
}
/* From Frostbite PBR Course
@ -101,7 +85,7 @@ float light_spot_attenuation(LightData light, vec3 L)
vec3 lL = light_world_to_local(light, L);
float ellipse = inversesqrt(1.0 + length_squared(lL.xy * spot.spot_size_inv / lL.z));
float spotmask = smoothstep(0.0, 1.0, ellipse * spot.spot_mul + spot.spot_bias);
return spotmask * step(0.0, -dot(L, -light._back));
return (lL.z > 0.0) ? spotmask : 0.0;
}
float light_attenuation_common(LightData light, const bool is_directional, vec3 L)
@ -113,7 +97,7 @@ float light_attenuation_common(LightData light, const bool is_directional, vec3
return light_spot_attenuation(light, L);
}
if (is_area_light(light.type)) {
return step(0.0, -dot(L, -light._back));
return float(dot(L, light_z_axis(light)) > 0.0);
}
return 1.0;
}
@ -202,7 +186,7 @@ float light_point_light(LightData light, const bool is_directional, LightVector
if (is_area_light(light.type)) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(light._back, lv.L));
power *= saturate(dot(light_z_axis(light), lv.L));
}
return power;
}
@ -227,12 +211,15 @@ float light_ltc(
return 1.0;
}
vec3 Px = light_x_axis(light);
vec3 Py = light_y_axis(light);
if (light.type == LIGHT_RECT) {
LightAreaData area = light_area_data_get(light);
vec3 corners[4];
corners[0] = light._right * area.size.x + light._up * -area.size.y;
corners[1] = light._right * area.size.x + light._up * area.size.y;
corners[0] = Px * area.size.x + Py * -area.size.y;
corners[1] = Px * area.size.x + Py * area.size.y;
corners[2] = -corners[0];
corners[3] = -corners[1];
@ -247,9 +234,6 @@ float light_ltc(
return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0));
}
else {
vec3 Px = light._right;
vec3 Py = light._up;
if (!is_area_light(light.type)) {
make_orthonormal_basis(lv.L, Px, Py);
}

View File

@ -124,11 +124,6 @@ bool debug_tilemaps(vec3 P, LightData light)
out_color_add = vec4(debug_tile_state_color(light.type, tile), 0.0);
out_color_mul = vec4(0.0);
# ifdef DRW_DEBUG_PRINT
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
drw_print(light.object_mat);
}
# endif
return true;
}
#else
@ -144,11 +139,6 @@ bool debug_tilemaps(vec3 P, LightData light)
out_color_add = vec4(debug_tile_state_color(tile), 0.0);
out_color_mul = vec4(0.0);
# ifdef DRW_DEBUG_PRINT
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
drw_print(light.object_mat);
}
# endif
return true;
}
#endif
@ -186,7 +176,7 @@ void debug_random_tilemap_color(vec3 P, LightData light)
coord = shadow_directional_coordinates(light, lP);
}
else {
vec3 lP = light_world_to_local(light, P - light._position);
vec3 lP = light_world_to_local(light, P - light_position_get(light));
int face_id = shadow_punctual_face_index_get(lP);
lP = shadow_punctual_local_position_to_face_local(face_id, lP);
coord = shadow_punctual_coordinates(light, lP, face_id);

View File

@ -152,12 +152,12 @@ vec3 shadow_punctual_reconstruct_position(ShadowSampleParams params,
vec3 lP = project_point(wininv, clip_P);
int face_id = params.tilemap_index - light.tilemap_index;
lP = shadow_punctual_face_local_to_local_position(face_id, lP);
return mat3(light.object_mat) * lP + light._position;
return transform_point(light.object_to_world, lP);
}
ShadowSampleParams shadow_punctual_sample_params_get(LightData light, vec3 P)
{
vec3 lP = (P - light._position) * mat3(light.object_mat);
vec3 lP = transform_point_inversed(light.object_to_world, P);
int face_id = shadow_punctual_face_index_get(lP);
/* Local Light Space > Face Local (View) Space. */
@ -205,7 +205,7 @@ ShadowDirectionalSampleInfo shadow_directional_sample_info_get(LightData light,
info.clip_near = orderedIntBitsToFloat(light.clip_near);
info.clip_far = orderedIntBitsToFloat(light.clip_far);
int level = shadow_directional_level(light, lP - light._position);
int level = shadow_directional_level(light, lP - light_position_get(light));
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
info.level_relative = level - light_sun_data_get(light).clipmap_lod_min;
@ -234,14 +234,14 @@ vec3 shadow_directional_reconstruct_position(ShadowSampleParams params, LightDat
lP.xy = clipmap_pos + info.clipmap_origin;
lP.z = (params.uv.z + info.clip_near) * -1.0;
return mat3(light.object_mat) * lP;
return transform_direction_transposed(light.object_to_world, lP);
}
ShadowSampleParams shadow_directional_sample_params_get(usampler2D tilemaps_tx,
LightData light,
vec3 P)
{
vec3 lP = P * mat3(light.object_mat);
vec3 lP = transform_direction(light.object_to_world, P);
ShadowDirectionalSampleInfo info = shadow_directional_sample_info_get(light, lP);
ShadowCoordinates coord = shadow_directional_coordinates(light, lP);

View File

@ -55,15 +55,15 @@ void shadow_tag_usage_tilemap_directional(uint l_idx, vec3 P, vec3 V, float radi
/* TODO(Miguel Pozo): Implement lod_bias support. */
if (radius == 0.0) {
int level = shadow_directional_level(light, lP - light._position);
int level = shadow_directional_level(light, lP - light_position_get(light));
ShadowCoordinates coord = shadow_directional_coordinates_at_level(light, lP, level);
shadow_tag_usage_tile(light, coord.tile_coord, 0, coord.tilemap_index);
}
else {
vec3 start_lP = light_world_to_local(light, P - V * radius);
vec3 end_lP = light_world_to_local(light, P + V * radius);
int min_level = shadow_directional_level(light, start_lP - light._position);
int max_level = shadow_directional_level(light, end_lP - light._position);
int min_level = shadow_directional_level(light, start_lP - light_position_get(light));
int max_level = shadow_directional_level(light, end_lP - light_position_get(light));
for (int level = min_level; level <= max_level; level++) {
ShadowCoordinates coord_min = shadow_directional_coordinates_at_level(
@ -89,7 +89,7 @@ void shadow_tag_usage_tilemap_punctual(
return;
}
vec3 lP = light_world_to_local(light, P - light._position);
vec3 lP = light_world_to_local(light, P - light_position_get(light));
float dist_to_light = max(length(lP) - radius, 1e-5);
if (dist_to_light > light_local_data_get(light).influence_radius_max) {
return;

View File

@ -111,7 +111,9 @@ void main()
set_clipmap_data(light, 0, 2, 0.0, 0.0);
light.tilemap_index = light_sun_data_get(light).clipmap_lod_min;
light._position = vec3(0.0);
light.object_to_world.x = float4(1.0, 0.0, 0.0, 0.0);
light.object_to_world.y = float4(0.0, 1.0, 0.0, 0.0);
light.object_to_world.z = float4(0.0, 0.0, 1.0, 0.0);
light.lod_bias = 0;
float lod_min_tile_size = exp2(float(light_sun_data_get(light).clipmap_lod_min)) /
@ -226,7 +228,9 @@ void main()
// clipmap_lod_max = 2; /* 3 tile-maps. */
set_clipmap_data(light, 0, 2, 0.0, 0.0);
light.tilemap_index = 1;
light._position = vec3(0.0);
light.object_to_world.x = float4(1.0, 0.0, 0.0, 0.0);
light.object_to_world.y = float4(0.0, 1.0, 0.0, 0.0);
light.object_to_world.z = float4(0.0, 0.0, 1.0, 0.0);
light.lod_bias = light_sun_data_get(light).clipmap_lod_min - 1;
float lod_tile_size = exp2(float(light_sun_data_get(light).clipmap_lod_min)) /
float(SHADOW_TILEMAP_RES);

View File

@ -53,7 +53,7 @@ void main()
float local_min = FLT_MAX;
float local_max = -FLT_MAX;
for (int i = 0; i < 8; i++) {
float z = dot(box.corners[i].xyz, -light._back);
float z = dot(box.corners[i].xyz, -light_z_axis(light));
local_min = min(local_min, z);
local_max = max(local_max, z);
}

View File

@ -105,7 +105,7 @@ ShadowSamplingTile shadow_tile_load(usampler2D tilemaps_tx, ivec2 tile_co, int t
* This function should be the inverse of ShadowDirectional::coverage_get().
*
* \a lP shading point position in light space, relative to the to camera position snapped to
* the smallest clip-map level (`shadow_world_to_local(light, P) - light._position`).
* the smallest clip-map level (`shadow_world_to_local(light, P) - light_position_get(light)`).
*/
float shadow_directional_level_fractional(LightData light, vec3 lP)
@ -149,7 +149,7 @@ float shadow_punctual_footprint_ratio(LightData light,
* This gives a good approximation of what LOD to select to get a somewhat uniform shadow map
* resolution in screen space. */
float dist_to_light = distance(P, light._position);
float dist_to_light = distance(P, light_position_get(light));
float footprint_ratio = dist_to_light;
/* Project the radius to the screen. 1 unit away from the camera the same way
* pixel_world_radius_inv was computed. Not needed in orthographic mode. */
@ -225,7 +225,7 @@ ShadowCoordinates shadow_directional_coordinates_at_level(LightData light, vec3
*/
ShadowCoordinates shadow_directional_coordinates(LightData light, vec3 lP)
{
int level = shadow_directional_level(light, lP - light._position);
int level = shadow_directional_level(light, lP - light_position_get(light));
return shadow_directional_coordinates_at_level(light, lP, level);
}

View File

@ -247,7 +247,7 @@ ShadowTracingSample shadow_map_trace_sample(ShadowMapTracingState state,
/* Ray position is ray local position with origin at light origin. */
vec4 ray_pos = ray.origin + ray.direction * state.ray_time;
int level = shadow_directional_level(ray.light, ray_pos.xyz - ray.light._position);
int level = shadow_directional_level(ray.light, ray_pos.xyz - light_position_get(ray.light));
/* This difference needs to be less than 32 for the later shift to be valid.
* This is ensured by ShadowDirectional::clipmap_level_range(). */
int level_relative = level - light_sun_data_get(ray.light).clipmap_lod_min;
@ -460,7 +460,7 @@ vec3 shadow_pcf_offset(LightData light, const bool is_directional, vec3 P, vec3
/* Scale the offset based on shadow LOD. */
if (is_directional) {
vec3 lP = light_world_to_local(light, P);
float level = shadow_directional_level_fractional(light, lP - light._position);
float level = shadow_directional_level_fractional(light, lP - light_position_get(light));
float pcf_scale = mix(0.5, 1.0, fract(level));
pcf_offset *= pcf_scale;
}
@ -550,7 +550,7 @@ ShadowEvalResult shadow_eval(LightData light,
P += N_bias * normal_offset;
vec3 lP = is_directional ? light_world_to_local(light, P) :
light_world_to_local(light, P - light._position);
light_world_to_local(light, P - light_position_get(light));
vec3 lNg = light_world_to_local(light, Ng);
/* Invert horizon clipping. */
lNg = (is_transmission) ? -lNg : lNg;

View File

@ -26,10 +26,20 @@ void main()
{
float f_depth = gl_FragCoord.z + fwidth(gl_FragCoord.z);
#ifdef SHADOW_UPDATE_TBDR
/* We need to write to gl_FragDepth un-conditionnally. So we cannot early exit or use discard. */
# define discard_result f_depth = 1.0;
#else
# define discard_result \
discard; \
return;
#endif
/* Avoid values greater than 1. */
f_depth = saturate(f_depth);
/* Clip to light shape. */
if (length_squared(shadow_clip.vector) < 1.0) {
discard;
return;
discard_result;
}
#ifdef MAT_TRANSPARENT
@ -42,8 +52,7 @@ void main()
float transparency = average(g_transmittance);
if (transparency > random_threshold) {
discard;
return;
discard_result;
}
#endif
@ -78,8 +87,7 @@ void main()
#endif
#ifdef SHADOW_UPDATE_TBDR
/* Store output depth in tile memory using F32 attachment. NOTE: As depth testing is enabled,
* only the closest fragment will store the result. */
gl_FragDepth = f_depth;
out_depth = f_depth;
#endif
}

View File

@ -199,7 +199,7 @@ vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
if (light.type == LIGHT_RECT || light.type == LIGHT_ELLIPSE) {
/* Modulate by light plane orientation / solid angle. */
power *= saturate(dot(light._back, lv.L));
power *= saturate(dot(light_z_axis(light), lv.L));
}
}
return light.color * light.power[LIGHT_VOLUME] * power;

View File

@ -250,9 +250,6 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow_atomic)
.additional_info("eevee_surf_shadow")
.define("SHADOW_UPDATE_ATOMIC_RASTER")
.builtins(BuiltinBits::TEXTURE_ATOMIC)
/* Early fragment test for speeding up platforms that requires a depth buffer. */
/* NOTE: This removes the possibility of using gl_FragDepth. */
.early_fragment_test(true)
.vertex_out(eevee_surf_shadow_atomic_iface)
.storage_buf(SHADOW_RENDER_MAP_BUF_SLOT,
Qualifier::READ,
@ -268,6 +265,9 @@ GPU_SHADER_CREATE_INFO(eevee_surf_shadow_tbdr)
.additional_info("eevee_surf_shadow")
.define("SHADOW_UPDATE_TBDR")
.builtins(BuiltinBits::LAYER)
/* Use greater depth write to avoid loosing the early Z depth test but ensure correct fragment
ordering after slope bias. */
.depth_write(DepthWrite::GREATER)
/* F32 color attachment for on-tile depth accumulation without atomics. */
.fragment_out(0, Type::FLOAT, "out_depth", DualBlend::NONE, SHADOW_ROG_ID);

View File

@ -525,8 +525,7 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_PrivateData *pd,
const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
const bool is_in_front = (ob->dtx & OB_DRAW_IN_FRONT);
/* Grease Pencil 3 doesn't have this. */
const bool is_screenspace = false;
const bool override_vertcol = (pd->v3d_color_type != -1);
const bool is_vert_col_mode = (pd->v3d_color_type == V3D_SHADING_VERTEX_COLOR) ||
(ob->mode == OB_MODE_VERTEX_PAINT) || pd->is_render;
@ -541,9 +540,9 @@ GPENCIL_tLayer *grease_pencil_layer_cache_add(GPENCIL_PrivateData *pd,
const float vert_col_opacity = (override_vertcol) ?
(is_vert_col_mode ? pd->vertex_paint_opacity : 0.0f) :
(pd->is_render ? 1.0f : pd->vertex_paint_opacity);
/* Negate thickness sign to tag that strokes are in screen space.
* Convert to world units (by default, 1 meter = 1000 pixels). */
const float thickness_scale = (is_screenspace) ? -1.0f : 1.0f / 1000.0f;
/* Negate thickness sign to tag that strokes are in screen space (this is no longer used in
* GPv3). Convert to world units (by default, 1 meter = 1000 pixels). */
const float thickness_scale = blender::bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
/* If the layer is used as a mask (but is otherwise not visible in the render), render it with a
* opacity of 0 so that it can still mask other layers. */
const float layer_opacity = !is_used_as_mask ? grease_pencil_layer_final_opacity_get(

View File

@ -750,9 +750,9 @@ static void grease_pencil_geom_batch_ensure(Object &object,
const float3 pos = math::transform_point(layer_space_to_object_space, positions[point_i]);
copy_v3_v3(s_vert.pos, pos);
s_vert.radius = radii[point_i] * ((end_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
/* Convert to legacy "pixel" space. The shader expects the values to be in this space.
* Otherwise the values will get clamped. */
s_vert.radius *= 1000.0f;
/* Convert to legacy "pixel" space. We divide here, because the shader expects the values to
* be in the `px` space rather than world space. Otherwise the values will get clamped. */
s_vert.radius /= bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
s_vert.opacity = opacities[point_i] *
((start_cap == GP_STROKE_CAP_TYPE_ROUND) ? 1.0f : -1.0f);
s_vert.point_id = verts_range[idx];

View File

@ -383,7 +383,7 @@ PassType *drw_volume_object_mesh_init(PassType &ps,
volume_infos.grids_xform[grid_id++] = float4x4::identity();
}
}
else if (!fds->fluid) {
else if (fds->fluid) {
/* Smoke Simulation. */
DRW_smoke_ensure(fmd, fds->flags & FLUID_DOMAIN_USE_NOISE);

View File

@ -5142,7 +5142,7 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
cfra = BKE_nla_tweakedit_remap(adt, float(scene->r.cfra), NLATIME_CONVERT_UNMAP);
/* Get flags for keyframing. */
flag = ANIM_get_keyframing_flags(scene);
flag = blender::animrig::get_keyframing_flags(scene);
/* try to resolve the path stored in the F-Curve */
if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
@ -5205,7 +5205,7 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
key->adt, anim_eval_context.eval_time, NLATIME_CONVERT_UNMAP);
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene);
flag = blender::animrig::get_keyframing_flags(scene);
/* try to resolve the path stored in the F-Curve */
if (RNA_path_resolve_property(&id_ptr, rna_path ? rna_path->c_str() : nullptr, &ptr, &prop)) {
@ -5261,7 +5261,7 @@ static void achannel_setting_slider_nla_curve_cb(bContext *C, void * /*id_poin*/
cfra = float(scene->r.cfra);
/* get flags for keyframing */
flag = ANIM_get_keyframing_flags(scene);
flag = blender::animrig::get_keyframing_flags(scene);
/* Get pointer and property from the slider -
* this should all match up with the NlaStrip required. */

View File

@ -70,31 +70,6 @@ static KeyingSet *keyingset_get_from_op_with_error(wmOperator *op,
static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *ks);
/* ************************************************** */
/* Keyframing Setting Wrangling */
eInsertKeyFlags ANIM_get_keyframing_flags(Scene *scene)
{
using namespace blender::animrig;
eInsertKeyFlags flag = INSERTKEY_NOFLAGS;
/* Visual keying. */
if (is_keying_flag(scene, KEYING_FLAG_VISUALKEY)) {
flag |= INSERTKEY_MATRIX;
}
/* Cycle-aware keyframe insertion - preserve cycle period and flow. */
if (is_keying_flag(scene, KEYING_FLAG_CYCLEAWARE)) {
flag |= INSERTKEY_CYCLE_AWARE;
}
if (is_keying_flag(scene, MANUALKEY_FLAG_INSERTNEEDED)) {
flag |= INSERTKEY_NEEDED;
}
return flag;
}
/* ******************************************* */
/* Animation Data Validation */
@ -389,7 +364,7 @@ static int insert_key(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
const float scene_frame = BKE_scene_frame_get(scene);
const eInsertKeyFlags insert_key_flags = ANIM_get_keyframing_flags(scene);
const eInsertKeyFlags insert_key_flags = animrig::get_keyframing_flags(scene);
const eBezTriple_KeyframeType key_type = eBezTriple_KeyframeType(
scene->toolsettings->keyframe_type);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
@ -976,7 +951,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op)
const bool all = RNA_boolean_get(op->ptr, "all");
eInsertKeyFlags flag = INSERTKEY_NOFLAGS;
flag = ANIM_get_keyframing_flags(scene);
flag = get_keyframing_flags(scene);
if (!(but = UI_context_active_but_prop_get(C, &ptr, &prop, &index))) {
/* pass event on if no active button found */

View File

@ -101,7 +101,7 @@ static int add_default_keyingset_exec(bContext *C, wmOperator * /*op*/)
*/
const eKS_Settings flag = KEYINGSET_ABSOLUTE;
const eInsertKeyFlags keyingflag = ANIM_get_keyframing_flags(scene);
const eInsertKeyFlags keyingflag = blender::animrig::get_keyframing_flags(scene);
/* Call the API func, and set the active keyingset index. */
BKE_keyingset_add(&scene->keyingsets, nullptr, nullptr, flag, keyingflag);
@ -285,7 +285,7 @@ static int add_keyingset_button_exec(bContext *C, wmOperator *op)
*/
const eKS_Settings flag = KEYINGSET_ABSOLUTE;
const eInsertKeyFlags keyingflag = ANIM_get_keyframing_flags(scene);
const eInsertKeyFlags keyingflag = blender::animrig::get_keyframing_flags(scene);
/* Call the API func, and set the active keyingset index. */
keyingset = BKE_keyingset_add(
@ -1136,7 +1136,7 @@ int ANIM_apply_keyingset(bContext *C,
}
Scene *scene = CTX_data_scene(C);
const eInsertKeyFlags base_kflags = ANIM_get_keyframing_flags(scene);
const eInsertKeyFlags base_kflags = blender::animrig::get_keyframing_flags(scene);
eInsertKeyFlags kflag = INSERTKEY_NOFLAGS;
if (mode == MODIFYKEY_MODE_INSERT) {
/* use context settings as base */

View File

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

View File

@ -53,7 +53,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;
@ -70,13 +70,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();
@ -137,7 +137,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)
{
/* Note: Don't access #AssetShelf.type directly, use #asset_shelf_type_ensure(). */
@ -167,7 +167,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
}
/* Case 3: */
for (std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
for (const std::unique_ptr<AssetShelfType> &shelf_type : space_type.asset_shelf_types) {
if (asset_shelf_type_poll(C, space_type, shelf_type.get())) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);

View File

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

View File

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

View File

@ -394,6 +394,9 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *active_object = CTX_data_active_object(C);
/* Note: `region` and `rv3d` may be null when called from a script. */
const ARegion *region = CTX_wm_region(C);
const RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (!active_object) {
return OPERATOR_CANCELLED;
}
@ -470,6 +473,9 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
operator_eval_data.depsgraphs = &depsgraphs;
operator_eval_data.self_object_orig = object;
operator_eval_data.scene_orig = scene;
RNA_int_get_array(op->ptr, "mouse_position", operator_eval_data.mouse_position);
operator_eval_data.region_size = region ? int2(region->sizex, region->sizey) : int2(0);
operator_eval_data.rv3d = rv3d;
nodes::GeoNodesCallData call_data{};
call_data.operator_data = &operator_eval_data;
@ -504,13 +510,15 @@ static int run_node_group_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static int run_node_group_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
static int run_node_group_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
const bNodeTree *node_tree = get_node_group(*C, *op->ptr, op->reports);
if (!node_tree) {
return OPERATOR_CANCELLED;
}
RNA_int_set_array(op->ptr, "mouse_position", event->mval);
nodes::update_input_properties_from_node_tree(*node_tree, op->properties, *op->properties);
nodes::update_output_properties_from_node_tree(*node_tree, op->properties, *op->properties);
@ -695,8 +703,35 @@ static std::string run_node_group_get_name(wmOperatorType * /*ot*/, PointerRNA *
return ref.drop_prefix(ref.find_last_of(SEP_STR) + 1);
}
static bool run_node_group_depends_on_cursor(bContext &C, wmOperatorType & /*ot*/, PointerRNA *ptr)
{
if (!ptr) {
return false;
}
Main &bmain = *CTX_data_main(&C);
if (bNodeTree *group = reinterpret_cast<bNodeTree *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(&bmain, ptr, ID_NT)))
{
return group->geometry_node_asset_traits &&
(group->geometry_node_asset_traits->flag & GEO_NODE_ASSET_WAIT_FOR_CURSOR) != 0;
}
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(C, *ptr, nullptr);
if (!asset) {
return false;
}
const IDProperty *traits_flag = BKE_asset_metadata_idprop_find(
&asset->get_metadata(), "geometry_node_asset_traits_flag");
if (traits_flag == nullptr || !(IDP_Int(traits_flag) & GEO_NODE_ASSET_WAIT_FOR_CURSOR)) {
return false;
}
return true;
}
void GEOMETRY_OT_execute_node_group(wmOperatorType *ot)
{
PropertyRNA *prop;
ot->name = "Run Node Group";
ot->idname = __func__;
ot->description = "Execute a node group on geometry";
@ -708,11 +743,27 @@ void GEOMETRY_OT_execute_node_group(wmOperatorType *ot)
ot->ui = run_node_group_ui;
ot->ui_poll = run_node_ui_poll;
ot->get_name = run_node_group_get_name;
ot->depends_on_cursor = run_node_group_depends_on_cursor;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
asset::operator_asset_reference_props_register(*ot->srna);
WM_operator_properties_id_lookup(ot, true);
/* Store the mouse position in an RNA property rather than allocated operator custom data in
* order to support redoing the operator. Because redo uses `exec`, the mouse position will be in
* the same position in screen space. */
prop = RNA_def_int_array(ot->srna,
"mouse_position",
2,
nullptr,
INT_MIN,
INT_MAX,
"Mouse Position",
"Mouse coordinates in region space",
INT_MIN,
INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
/** \} */

View File

@ -261,16 +261,12 @@ static int grease_pencil_stroke_simplify_exec(bContext *C, wmOperator *op)
[positions, radii](int64_t first_index, int64_t last_index, int64_t index) {
const float dist_position = dist_to_line_v3(
positions[index], positions[first_index], positions[last_index]);
/* We divide the distance by 2000.0f to convert from "pixels" to an actual
* distance. For some reason, grease pencil strokes the thickness of strokes in
* pixels rather than object space distance. */
const float dist_radii = dist_to_interpolated(positions[index],
positions[first_index],
positions[last_index],
radii[index],
radii[first_index],
radii[last_index]) /
2000.0f;
radii[last_index]);
return math::max(dist_position, dist_radii);
};
@ -2193,18 +2189,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 +2215,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");
@ -2231,7 +2227,8 @@ static int grease_pencil_paste_strokes_exec(bContext *C, wmOperator *op)
}
/* Ensure active keyframe. */
if (!ensure_active_keyframe(scene, grease_pencil)) {
bool inserted_keyframe = false;
if (!ensure_active_keyframe(scene, grease_pencil, inserted_keyframe)) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}
@ -2251,63 +2248,15 @@ 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);
if (inserted_keyframe) {
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
}
return OPERATOR_FINISHED;
}
@ -2319,7 +2268,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 +2349,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 +2387,94 @@ static void GREASE_PENCIL_OT_copy(wmOperatorType *ot)
/** \} */
void clipboard_free()
{
if (grease_pencil_clipboard) {
MEM_delete(grease_pencil_clipboard);
grease_pencil_clipboard = nullptr;
}
}
const bke::CurvesGeometry &clipboard_curves()
{
using namespace blender::ed::greasepencil;
return ensure_grease_pencil_clipboard().curves;
}
static Array<int> clipboard_materials_remap(Main &bmain, Object &object)
{
using namespace blender::ed::greasepencil;
/* Get a list of all materials in the scene. */
Map<uint, Material *> scene_materials;
LISTBASE_FOREACH (Material *, material, &bmain.materials) {
scene_materials.add(material->id.session_uid, material);
}
Clipboard &clipboard = ensure_grease_pencil_clipboard();
Array<int> clipboard_material_remap(clipboard.materials_in_source_num, 0);
for (const int i : clipboard.materials.index_range()) {
/* Check if the material name exists in the scene. */
int target_index;
uint material_id = clipboard.materials[i].first;
Material *material = scene_materials.lookup_default(material_id, nullptr);
if (!material) {
/* Material is removed, so create a new material. */
BKE_grease_pencil_object_material_new(&bmain, &object, nullptr, &target_index);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
continue;
}
/* Find or add the material to the target object. */
target_index = BKE_object_material_ensure(&bmain, &object, material);
clipboard_material_remap[clipboard.materials[i].second] = target_index;
}
return clipboard_material_remap;
}
IndexRange clipboard_paste_strokes(Main &bmain,
Object &object,
bke::greasepencil::Drawing &drawing,
const bool paste_back)
{
const bke::CurvesGeometry &clipboard_curves = ed::greasepencil::clipboard_curves();
/* Get a list of all materials in the scene. */
const Array<int> clipboard_material_remap = ed::greasepencil::clipboard_materials_remap(bmain,
object);
/* Get the index range of the pasted curves in the target layer. */
const IndexRange pasted_curves_range = paste_back ?
IndexRange(0, clipboard_curves.curves_num()) :
IndexRange(drawing.strokes().curves_num(),
clipboard_curves.curves_num());
/* Append the geometry from the clipboard to the target layer. */
Curves *clipboard_id = bke::curves_new_nomain(clipboard_curves);
Curves *target_id = curves_new_nomain(std::move(drawing.strokes_for_write()));
const Array<bke::GeometrySet> geometry_sets = {
bke::GeometrySet::from_curves(paste_back ? clipboard_id : target_id),
bke::GeometrySet::from_curves(paste_back ? target_id : clipboard_id)};
bke::GeometrySet joined_curves = geometry::join_geometries(geometry_sets, {});
drawing.strokes_for_write() = std::move(joined_curves.get_curves_for_write()->geometry.wrap());
/* Remap the material indices of the pasted curves to the target object material indices. */
bke::MutableAttributeAccessor attributes = drawing.strokes_for_write().attributes_for_write();
bke::SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
"material_index", bke::AttrDomain::Curve);
if (material_indices) {
for (const int i : pasted_curves_range) {
material_indices.span[i] = clipboard_material_remap[material_indices.span[i]];
}
material_indices.finish();
}
drawing.tag_topology_changed();
return pasted_curves_range;
}
} // namespace blender::ed::greasepencil
void ED_operatortypes_grease_pencil_edit()

View File

@ -335,7 +335,9 @@ void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked,
}
}
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil)
bool ensure_active_keyframe(const Scene &scene,
GreasePencil &grease_pencil,
bool &r_inserted_keyframe)
{
const int current_frame = scene.r.cfra;
bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
@ -366,6 +368,7 @@ bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil)
/* Otherwise we just insert a blank keyframe at the current frame. */
grease_pencil.insert_blank_frame(active_layer, current_frame, 0, BEZT_KEYTYPE_KEYFRAME);
}
r_inserted_keyframe = true;
}
/* There should now always be a drawing at the current frame. */
BLI_assert(active_layer.has_drawing_at(current_frame));

View File

@ -563,6 +563,47 @@ Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer(
return editable_drawings;
}
Vector<MutableDrawingInfo> retrieve_editable_drawings_from_layer_with_falloff(
const Scene &scene,
GreasePencil &grease_pencil,
const blender::bke::greasepencil::Layer &layer)
{
using namespace blender::bke::greasepencil;
const int current_frame = scene.r.cfra;
const ToolSettings *toolsettings = scene.toolsettings;
const bool use_multi_frame_editing = (toolsettings->gpencil_flags &
GP_USE_MULTI_FRAME_EDITING) != 0;
const bool use_multi_frame_falloff = use_multi_frame_editing &&
(toolsettings->gp_sculpt.flag &
GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) != 0;
const int layer_index = *grease_pencil.get_layer_index(layer);
int center_frame;
std::pair<int, int> minmax_frame;
if (use_multi_frame_falloff) {
BKE_curvemapping_init(toolsettings->gp_sculpt.cur_falloff);
minmax_frame = get_minmax_selected_frame_numbers(grease_pencil, current_frame);
center_frame = math::clamp(current_frame, minmax_frame.first, minmax_frame.second);
}
Vector<MutableDrawingInfo> editable_drawings;
const Array<int> frame_numbers = get_editable_frames_for_layer(
layer, current_frame, use_multi_frame_editing);
for (const int frame_number : frame_numbers) {
if (Drawing *drawing = grease_pencil.get_editable_drawing_at(layer, frame_number)) {
const float falloff = use_multi_frame_falloff ?
get_multi_frame_falloff(frame_number,
center_frame,
minmax_frame.first,
minmax_frame.second,
toolsettings->gp_sculpt.cur_falloff) :
1.0f;
editable_drawings.append({*drawing, layer_index, frame_number, falloff});
}
}
return editable_drawings;
}
Vector<DrawingInfo> retrieve_visible_drawings(const Scene &scene,
const GreasePencil &grease_pencil,
const bool do_onion_skinning)
@ -1131,11 +1172,14 @@ int grease_pencil_draw_operator_invoke(bContext *C, wmOperator *op)
}
/* Ensure a drawing at the current keyframe. */
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil)) {
bool inserted_keyframe = false;
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil, inserted_keyframe)) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}
if (inserted_keyframe) {
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
}
return OPERATOR_RUNNING_MODAL;
}

View File

@ -34,7 +34,8 @@ struct ViewContext;
namespace blender {
namespace bke {
enum class AttrDomain : int8_t;
}
class CurvesGeometry;
} // namespace bke
} // namespace blender
enum {
@ -62,6 +63,7 @@ void ED_primitivetool_modal_keymap(wmKeyConfig *keyconf);
void GREASE_PENCIL_OT_stroke_cutter(wmOperatorType *ot);
void ED_undosys_type_grease_pencil(UndoType *undo_type);
/**
* Get the selection mode for Grease Pencil selection operators: point, stroke, segment.
*/
@ -207,7 +209,9 @@ bool has_any_frame_selected(const bke::greasepencil::Layer &layer);
* create one when auto-key is on (taking additive drawing setting into account).
* \return false when no keyframe could be found or created.
*/
bool ensure_active_keyframe(const Scene &scene, GreasePencil &grease_pencil);
bool ensure_active_keyframe(const Scene &scene,
GreasePencil &grease_pencil,
bool &r_inserted_keyframe);
void create_keyframe_edit_data_selected_frames_list(KeyframeEditData *ked,
const bke::greasepencil::Layer &layer);
@ -256,6 +260,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 +387,16 @@ void normalize_vertex_weights(MDeformVert &dvert,
Span<bool> vertex_group_is_locked,
Span<bool> vertex_group_is_bone_deformed);
void clipboard_free();
const bke::CurvesGeometry &clipboard_curves();
/**
* Paste curves from the clipboard into the drawing.
* \param paste_back Render behind existing curves by inserting curves at the front.
* \return Index range of the new curves in the drawing after pasting.
*/
IndexRange clipboard_paste_strokes(Main &bmain,
Object &object,
bke::greasepencil::Drawing &drawing,
bool paste_back);
} // namespace blender::ed::greasepencil

View File

@ -39,15 +39,6 @@ struct NlaKeyframingContext;
/** \name Key-Framing Management
* \{ */
/**
* Get the active settings for key-framing settings from context (specifically the given scene)
* \param use_autokey_mode: include settings from key-framing mode in the result
* (i.e. replace only).
*/
eInsertKeyFlags ANIM_get_keyframing_flags(Scene *scene);
/* -------- */
/**
* \brief Lesser Key-framing API call.
*

View File

@ -68,7 +68,9 @@ bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool
if (but->type == UI_BTYPE_LABEL) {
if (for_tooltip) {
/* It's important labels are considered interactive for the purpose of showing tooltip. */
if (!ui_but_drag_is_draggable(but) && but->tip_func == nullptr) {
if (!ui_but_drag_is_draggable(but) && but->tip_func == nullptr &&
(but->tip == nullptr || but->tip[0] == '\0'))
{
return false;
}
}

View File

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

View File

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

View File

@ -27,6 +27,7 @@
#include "WM_message.hh"
#include "WM_toolsystem.hh"
#include "curves_sculpt_intern.hh"
#include "grease_pencil_intern.hh"
#include "paint_intern.hh"
@ -208,30 +209,31 @@ static bool grease_pencil_sculpt_paint_poll(bContext *C)
return true;
}
static GreasePencilStrokeOperation *grease_pencil_sculpt_paint_operation(bContext &C)
static GreasePencilStrokeOperation *grease_pencil_sculpt_paint_operation(
bContext &C, const BrushStrokeMode stroke_mode)
{
const Scene &scene = *CTX_data_scene(&C);
const GpSculptPaint &gp_sculptpaint = *scene.toolsettings->gp_sculptpaint;
const Brush &brush = *BKE_paint_brush_for_read(&gp_sculptpaint.paint);
switch (eBrushGPSculptTool(brush.gpencil_sculpt_tool)) {
case GPSCULPT_TOOL_SMOOTH:
return nullptr;
return greasepencil::new_smooth_operation(stroke_mode).release();
case GPSCULPT_TOOL_THICKNESS:
return nullptr;
return greasepencil::new_thickness_operation(stroke_mode).release();
case GPSCULPT_TOOL_STRENGTH:
return nullptr;
return greasepencil::new_strength_operation(stroke_mode).release();
case GPSCULPT_TOOL_GRAB:
return nullptr;
return greasepencil::new_grab_operation(stroke_mode).release();
case GPSCULPT_TOOL_PUSH:
return nullptr;
return greasepencil::new_push_operation(stroke_mode).release();
case GPSCULPT_TOOL_TWIST:
return nullptr;
return greasepencil::new_twist_operation(stroke_mode).release();
case GPSCULPT_TOOL_PINCH:
return nullptr;
return greasepencil::new_pinch_operation(stroke_mode).release();
case GPSCULPT_TOOL_RANDOMIZE:
return nullptr;
return greasepencil::new_randomize_operation(stroke_mode).release();
case GPSCULPT_TOOL_CLONE:
return nullptr;
return greasepencil::new_clone_operation(stroke_mode).release();
}
return nullptr;
}
@ -240,7 +242,8 @@ static bool grease_pencil_sculpt_paint_test_start(bContext *C,
wmOperator *op,
const float mouse[2])
{
GreasePencilStrokeOperation *operation = grease_pencil_sculpt_paint_operation(*C);
const BrushStrokeMode stroke_mode = BrushStrokeMode(RNA_enum_get(op->ptr, "mode"));
GreasePencilStrokeOperation *operation = grease_pencil_sculpt_paint_operation(*C, stroke_mode);
if (operation) {
stroke_start(*C, *op, float2(mouse), *operation);
return true;
@ -276,10 +279,14 @@ static int grease_pencil_sculpt_paint_invoke(bContext *C, wmOperator *op, const
}
/* Ensure a drawing at the current keyframe. */
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil)) {
bool inserted_keyframe = false;
if (!ed::greasepencil::ensure_active_keyframe(*scene, grease_pencil, inserted_keyframe)) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
return OPERATOR_CANCELLED;
}
if (inserted_keyframe) {
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
}
op->customdata = paint_stroke_new(C,
op,

View File

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

View File

@ -331,13 +331,14 @@ struct PaintOperationExecutor {
* stable) fit. */
Array<float2> coords_pre_blur(smooth_window.size());
const int pre_blur_iterations = 3;
geometry::gaussian_blur_1D(coords_to_smooth,
pre_blur_iterations,
settings_->active_smooth,
true,
true,
false,
coords_pre_blur.as_mutable_span());
geometry::gaussian_blur_1D(
coords_to_smooth,
pre_blur_iterations,
VArray<float>::ForSingle(settings_->active_smooth, smooth_window.size()),
true,
true,
false,
coords_pre_blur.as_mutable_span());
/* Curve fitting. The output will be a set of handles (float2 triplets) in a flat array. */
const float max_error_threshold_px = 5.0f;
@ -708,7 +709,7 @@ void PaintOperation::on_stroke_done(const bContext &C)
drawing.tag_topology_changed();
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &grease_pencil.id);
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil.id);
}
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1215,7 +1215,6 @@ static bool paint_use_2d_cursor(PaintMode mode)
return false;
case PaintMode::Texture3D:
case PaintMode::Texture2D:
case PaintMode::SculptUV:
case PaintMode::VertexGPencil:
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:
@ -1686,8 +1685,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 +1699,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 +1812,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
/* Free the previous pose brush preview. */
if (ss->pose_ik_chain_preview) {
pose::ik_chain_free(ss->pose_ik_chain_preview);
ss->pose_ik_chain_preview.reset();
}
/* Generate a new pose brush preview from the current cursor location. */

View File

@ -320,7 +320,9 @@ void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache,
/* `sculpt_uv.cc` */
void SCULPT_OT_uv_sculpt_stroke(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_grab(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_relax(wmOperatorType *ot);
void SCULPT_OT_uv_sculpt_pinch(wmOperatorType *ot);
/* paint_utils.cc */

View File

@ -1517,7 +1517,9 @@ void ED_operatortypes_paint()
WM_operatortype_append(PAINT_OT_weight_sample_group);
/* uv */
WM_operatortype_append(SCULPT_OT_uv_sculpt_stroke);
WM_operatortype_append(SCULPT_OT_uv_sculpt_grab);
WM_operatortype_append(SCULPT_OT_uv_sculpt_relax);
WM_operatortype_append(SCULPT_OT_uv_sculpt_pinch);
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);

View File

@ -1699,8 +1699,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);
@ -1853,8 +1864,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,
@ -1865,40 +1875,26 @@ static void vpaint_do_symmetrical_brush_actions(bContext *C,
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
int i = 0;
/* initial stroke */
const ePaintSymmetryFlags initial_symm = ePaintSymmetryFlags(0);
cache->mirror_symmetry_pass = ePaintSymmetryFlags(0);
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, initial_symm, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'X');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'Y');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, initial_symm, 'Z');
cache->symmetry = symm;
/* symm is a bit combination of XYZ - 1 is mirror
* X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (i = 1; i <= symm; i++) {
if (symm & i && (symm != 5 || i != 3) && (symm != 6 || !ELEM(i, 3, 5))) {
const ePaintSymmetryFlags symm_pass = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm_pass;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
/* symm is a bit combination of XYZ -
* 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */
for (int i = 0; i <= symm; i++) {
if (i & (1 << 0)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'X');
}
if (i & (1 << 1)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y');
}
if (i & (1 << 2)) {
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z');
}
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
const ePaintSymmetryFlags symm_pass = ePaintSymmetryFlags(i);
cache->mirror_symmetry_pass = symm_pass;
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, symm_pass, 0, 0);
vpaint_do_paint(C, vp, vpd, ob, mesh, brush, symm_pass, 'X', 0, 0);
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'X');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Y');
vpaint_do_radial_symmetry(C, vp, vpd, ob, mesh, brush, symm_pass, 'Z');
}
copy_v3_v3(cache->true_last_location, cache->true_location);

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More