Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
3b4a9b2319
|
@ -1773,6 +1773,10 @@ elseif(WITH_CYCLES_STANDALONE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing
|
||||
add_subdirectory(tests)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Blender Application
|
||||
if(WITH_BLENDER)
|
||||
|
@ -1780,11 +1784,6 @@ if(WITH_BLENDER)
|
|||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Testing
|
||||
add_subdirectory(tests)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Define 'heavy' submodules (for Ninja builder when using pools).
|
||||
setup_heavy_lib_pool()
|
||||
|
|
|
@ -42,7 +42,7 @@ def get_cmake_options(builder):
|
|||
elif builder.platform == 'linux':
|
||||
config_file = "build_files/buildbot/config/blender_linux.cmake"
|
||||
|
||||
optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK')
|
||||
optix_sdk_dir = os.path.join(builder.blender_dir, '..', '..', 'NVIDIA-Optix-SDK-7.1')
|
||||
options.append('-DOPTIX_ROOT_DIR:PATH=' + optix_sdk_dir)
|
||||
|
||||
# Workaround to build sm_30 kernels with CUDA 10, since CUDA 11 no longer supports that architecture
|
||||
|
|
|
@ -330,6 +330,9 @@ function(gtest_add_tests)
|
|||
set(gtest_case_name_regex ".*\\( *([A-Za-z_0-9]+) *, *([A-Za-z_0-9]+) *\\).*")
|
||||
set(gtest_test_type_regex "(TYPED_TEST|TEST_?[FP]?)")
|
||||
|
||||
# This will get a filter for each test suite.
|
||||
set(test_filters "")
|
||||
|
||||
foreach(source IN LISTS ARGS_SOURCES)
|
||||
if(NOT ARGS_SKIP_DEPENDENCY)
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${source})
|
||||
|
@ -376,175 +379,32 @@ function(gtest_add_tests)
|
|||
list(APPEND testList ${ctest_test_name})
|
||||
endif()
|
||||
else()
|
||||
set(ctest_test_name ${ARGS_TEST_PREFIX}${gtest_test_name}${ARGS_TEST_SUFFIX})
|
||||
add_test(NAME ${ctest_test_name}
|
||||
${workDir}
|
||||
COMMAND ${ARGS_TARGET}
|
||||
--gtest_filter=${gtest_test_name}
|
||||
${ARGS_EXTRA_ARGS}
|
||||
)
|
||||
list(APPEND testList ${ctest_test_name})
|
||||
# BLENDER: collect tests named "suite.testcase" as list of "suite.*" filters.
|
||||
string(REGEX REPLACE "\\..*$" "" gtest_suite_name ${gtest_test_name})
|
||||
list(APPEND test_filters "${gtest_suite_name}.*")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Join all found GTest suite names into one big filter.
|
||||
list(REMOVE_DUPLICATES test_filters)
|
||||
list(JOIN test_filters ":" gtest_filter)
|
||||
add_test(NAME ${ARGS_TEST_PREFIX}
|
||||
${workDir}
|
||||
COMMAND ${ARGS_TARGET}
|
||||
--gtest_filter=${gtest_filter}
|
||||
${ARGS_EXTRA_ARGS}
|
||||
)
|
||||
list(APPEND testList ${ARGS_TEST_PREFIX})
|
||||
|
||||
if(ARGS_TEST_LIST)
|
||||
set(${ARGS_TEST_LIST} ${testList} PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
function(gtest_discover_tests TARGET)
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
"NO_PRETTY_TYPES;NO_PRETTY_VALUES"
|
||||
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
|
||||
"EXTRA_ARGS;PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
if(NOT _WORKING_DIRECTORY)
|
||||
set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT _TEST_LIST)
|
||||
set(_TEST_LIST ${TARGET}_TESTS)
|
||||
endif()
|
||||
if(NOT _DISCOVERY_TIMEOUT)
|
||||
set(_DISCOVERY_TIMEOUT 5)
|
||||
endif()
|
||||
if(NOT _DISCOVERY_MODE)
|
||||
if(NOT CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE)
|
||||
set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
|
||||
endif()
|
||||
set(_DISCOVERY_MODE ${CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE})
|
||||
endif()
|
||||
|
||||
get_property(
|
||||
has_counter
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
SET
|
||||
)
|
||||
if(has_counter)
|
||||
get_property(
|
||||
counter
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
)
|
||||
math(EXPR counter "${counter} + 1")
|
||||
else()
|
||||
set(counter 1)
|
||||
endif()
|
||||
set_property(
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CTEST_DISCOVERED_TEST_COUNTER
|
||||
${counter}
|
||||
)
|
||||
|
||||
# Define rule to generate test list for aforementioned test executable
|
||||
# Blender: use _ instead of [] to avoid problems with zsh regex.
|
||||
set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_${counter}_")
|
||||
set(ctest_include_file "${ctest_file_base}_include.cmake")
|
||||
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
|
||||
get_property(crosscompiling_emulator
|
||||
TARGET ${TARGET}
|
||||
PROPERTY CROSSCOMPILING_EMULATOR
|
||||
)
|
||||
|
||||
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
|
||||
add_custom_command(
|
||||
TARGET ${TARGET} POST_BUILD
|
||||
BYPRODUCTS "${ctest_tests_file}"
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-D "TEST_TARGET=${TARGET}"
|
||||
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
|
||||
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
|
||||
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
|
||||
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
|
||||
-D "TEST_PROPERTIES=${_PROPERTIES}"
|
||||
-D "TEST_PREFIX=${_TEST_PREFIX}"
|
||||
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
|
||||
-D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
|
||||
-D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
|
||||
-D "TEST_LIST=${_TEST_LIST}"
|
||||
-D "CTEST_FILE=${ctest_tests_file}"
|
||||
-D "TEST_DISCOVERY_TIMEOUT=${_DISCOVERY_TIMEOUT}"
|
||||
-D "TEST_XML_OUTPUT_DIR=${_XML_OUTPUT_DIR}"
|
||||
-P "${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE "${ctest_include_file}"
|
||||
"if(EXISTS \"${ctest_tests_file}\")\n"
|
||||
" include(\"${ctest_tests_file}\")\n"
|
||||
"else()\n"
|
||||
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)\n"
|
||||
"endif()\n"
|
||||
)
|
||||
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
|
||||
PROPERTY GENERATOR_IS_MULTI_CONFIG
|
||||
)
|
||||
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
|
||||
endif()
|
||||
|
||||
string(CONCAT ctest_include_content
|
||||
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
|
||||
" if(\"$<TARGET_FILE:${TARGET}>\" IS_NEWER_THAN \"${ctest_tests_file}\")" "\n"
|
||||
" include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")" "\n"
|
||||
" gtest_discover_tests_impl(" "\n"
|
||||
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
|
||||
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
|
||||
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
|
||||
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
|
||||
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
|
||||
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
|
||||
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
|
||||
" NO_PRETTY_TYPES" " [==[" "${_NO_PRETTY_TYPES}" "]==]" "\n"
|
||||
" NO_PRETTY_VALUES" " [==[" "${_NO_PRETTY_VALUES}" "]==]" "\n"
|
||||
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
|
||||
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
|
||||
" TEST_DISCOVERY_TIMEOUT" " [==[" "${_DISCOVERY_TIMEOUT}" "]==]" "\n"
|
||||
" TEST_XML_OUTPUT_DIR" " [==[" "${_XML_OUTPUT_DIR}" "]==]" "\n"
|
||||
" )" "\n"
|
||||
" endif()" "\n"
|
||||
" include(\"${ctest_tests_file}\")" "\n"
|
||||
"else()" "\n"
|
||||
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
|
||||
"endif()" "\n"
|
||||
)
|
||||
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
|
||||
file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
|
||||
endforeach()
|
||||
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")")
|
||||
else()
|
||||
file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
|
||||
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
|
||||
endif()
|
||||
|
||||
# Add discovered tests to directory TEST_INCLUDE_FILES
|
||||
set_property(DIRECTORY
|
||||
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
|
||||
)
|
||||
|
||||
endfunction()
|
||||
|
||||
###############################################################################
|
||||
|
||||
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/GTestAddTests.cmake
|
||||
)
|
||||
# BLENDER: remove the discovery function gtest_discover_tests(). It's not used,
|
||||
# as it generates too many test invocations.
|
||||
|
||||
# Restore project's policies
|
||||
cmake_policy(POP)
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
# Distributed under the OSI-approved BSD 3-Clause License,
|
||||
# see accompanying file BSD-3-Clause-license.txt for details.
|
||||
|
||||
# Changes made to this script have been marked with "BLENDER".
|
||||
|
||||
|
||||
# BLENDER: disable ASAN leak detection when trying to discover tests.
|
||||
set(ENV{ASAN_OPTIONS} "detect_leaks=0")
|
||||
|
||||
cmake_minimum_required(VERSION ${CMAKE_VERSION})
|
||||
|
||||
# Overwrite possibly existing ${_CTEST_FILE} with empty file
|
||||
set(flush_tests_MODE WRITE)
|
||||
|
||||
# Flushes script to ${_CTEST_FILE}
|
||||
macro(flush_script)
|
||||
file(${flush_tests_MODE} "${_CTEST_FILE}" "${script}")
|
||||
set(flush_tests_MODE APPEND)
|
||||
|
||||
set(script "")
|
||||
endmacro()
|
||||
|
||||
# Flushes tests_buffer to tests
|
||||
macro(flush_tests_buffer)
|
||||
list(APPEND tests "${tests_buffer}")
|
||||
set(tests_buffer "")
|
||||
endmacro()
|
||||
|
||||
macro(add_command NAME)
|
||||
set(_args "")
|
||||
foreach(_arg ${ARGN})
|
||||
if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
|
||||
string(APPEND _args " [==[${_arg}]==]")
|
||||
else()
|
||||
string(APPEND _args " ${_arg}")
|
||||
endif()
|
||||
endforeach()
|
||||
string(APPEND script "${NAME}(${_args})\n")
|
||||
string(LENGTH "${script}" _script_len)
|
||||
if(${_script_len} GREATER "50000")
|
||||
flush_script()
|
||||
endif()
|
||||
# Unsets macro local variables to prevent leakage outside of this macro.
|
||||
unset(_args)
|
||||
unset(_script_len)
|
||||
endmacro()
|
||||
|
||||
function(gtest_discover_tests_impl)
|
||||
|
||||
cmake_parse_arguments(
|
||||
""
|
||||
""
|
||||
"NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_EXECUTOR;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
|
||||
"TEST_EXTRA_ARGS;TEST_PROPERTIES"
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
set(prefix "${_TEST_PREFIX}")
|
||||
set(suffix "${_TEST_SUFFIX}")
|
||||
set(extra_args ${_TEST_EXTRA_ARGS})
|
||||
set(properties ${_TEST_PROPERTIES})
|
||||
set(script)
|
||||
set(suite)
|
||||
set(tests)
|
||||
set(tests_buffer)
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${_TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
"Specified test executable does not exist.\n"
|
||||
" Path: '${_TEST_EXECUTABLE}'"
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
if(NOT ${result} EQUAL 0)
|
||||
string(REPLACE "\n" "\n " output "${output}")
|
||||
message(FATAL_ERROR
|
||||
"Error running test executable.\n"
|
||||
" Path: '${_TEST_EXECUTABLE}'\n"
|
||||
" Result: ${result}\n"
|
||||
" Output:\n"
|
||||
" ${output}\n"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Preserve semicolon in test-parameters
|
||||
string(REPLACE [[;]] [[\;]] output "${output}")
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
# Skip header
|
||||
if(NOT line MATCHES "gtest_main\\.cc")
|
||||
# Do we have a module name or a test name?
|
||||
if(NOT line MATCHES "^ ")
|
||||
# Module; remove trailing '.' to get just the name...
|
||||
string(REGEX REPLACE "\\.( *#.*)?" "" suite "${line}")
|
||||
if(line MATCHES "#" AND NOT _NO_PRETTY_TYPES)
|
||||
string(REGEX REPLACE "/[0-9]\\.+ +#.*= +" "/" pretty_suite "${line}")
|
||||
else()
|
||||
set(pretty_suite "${suite}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^DISABLED_" "" pretty_suite "${pretty_suite}")
|
||||
else()
|
||||
# Test name; strip spaces and comments to get just the name...
|
||||
string(REGEX REPLACE " +" "" test "${line}")
|
||||
if(test MATCHES "#" AND NOT _NO_PRETTY_VALUES)
|
||||
string(REGEX REPLACE "/[0-9]+#GetParam..=" "/" pretty_test "${test}")
|
||||
else()
|
||||
string(REGEX REPLACE "#.*" "" pretty_test "${test}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^DISABLED_" "" pretty_test "${pretty_test}")
|
||||
string(REGEX REPLACE "#.*" "" test "${test}")
|
||||
if(NOT "${_TEST_XML_OUTPUT_DIR}" STREQUAL "")
|
||||
set(TEST_XML_OUTPUT_PARAM "--gtest_output=xml:${_TEST_XML_OUTPUT_DIR}/${prefix}${suite}.${test}${suffix}.xml")
|
||||
else()
|
||||
unset(TEST_XML_OUTPUT_PARAM)
|
||||
endif()
|
||||
|
||||
# sanitize test name for further processing downstream
|
||||
set(testname "${prefix}${pretty_suite}.${pretty_test}${suffix}")
|
||||
# escape \
|
||||
string(REPLACE [[\]] [[\\]] testname "${testname}")
|
||||
# escape ;
|
||||
string(REPLACE [[;]] [[\;]] testname "${testname}")
|
||||
# escape $
|
||||
string(REPLACE [[$]] [[\$]] testname "${testname}")
|
||||
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${testname}"
|
||||
${_TEST_EXECUTOR}
|
||||
"${_TEST_EXECUTABLE}"
|
||||
"--gtest_filter=${suite}.${test}"
|
||||
"--gtest_also_run_disabled_tests"
|
||||
${TEST_XML_OUTPUT_PARAM}
|
||||
${extra_args}
|
||||
)
|
||||
if(suite MATCHES "^DISABLED" OR test MATCHES "^DISABLED")
|
||||
add_command(set_tests_properties
|
||||
"${testname}"
|
||||
PROPERTIES DISABLED TRUE
|
||||
)
|
||||
endif()
|
||||
add_command(set_tests_properties
|
||||
"${testname}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
|
||||
SKIP_REGULAR_EXPRESSION "\\\\[ SKIPPED \\\\]"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests_buffer "${testname}")
|
||||
list(LENGTH tests_buffer tests_buffer_length)
|
||||
if(${tests_buffer_length} GREATER "250")
|
||||
flush_tests_buffer()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
# properties on the tests
|
||||
flush_tests_buffer()
|
||||
add_command(set ${_TEST_LIST} ${tests})
|
||||
|
||||
# Write CTest script
|
||||
flush_script()
|
||||
|
||||
endfunction()
|
||||
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
gtest_discover_tests_impl(
|
||||
NO_PRETTY_TYPES ${NO_PRETTY_TYPES}
|
||||
NO_PRETTY_VALUES ${NO_PRETTY_VALUES}
|
||||
TEST_EXECUTABLE ${TEST_EXECUTABLE}
|
||||
TEST_EXECUTOR ${TEST_EXECUTOR}
|
||||
TEST_WORKING_DIR ${TEST_WORKING_DIR}
|
||||
TEST_PREFIX ${TEST_PREFIX}
|
||||
TEST_SUFFIX ${TEST_SUFFIX}
|
||||
TEST_LIST ${TEST_LIST}
|
||||
CTEST_FILE ${CTEST_FILE}
|
||||
TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
|
||||
TEST_XML_OUTPUT_DIR ${TEST_XML_OUTPUT_DIR}
|
||||
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
|
||||
TEST_PROPERTIES ${TEST_PROPERTIES}
|
||||
)
|
||||
endif()
|
|
@ -8,6 +8,17 @@
|
|||
#
|
||||
#=============================================================================
|
||||
|
||||
function(GET_BLENDER_TEST_INSTALL_DIR VARIABLE_NAME)
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
string(REPLACE "\${BUILD_TYPE}" "$<CONFIG>" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
string(REPLACE "\${BUILD_TYPE}" "" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
set(${VARIABLE_NAME} "${TEST_INSTALL_DIR}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
macro(BLENDER_SRC_GTEST_EX)
|
||||
if(WITH_GTESTS)
|
||||
set(options SKIP_ADD_TEST)
|
||||
|
@ -75,13 +86,7 @@ macro(BLENDER_SRC_GTEST_EX)
|
|||
target_link_libraries(${TARGET_NAME} ${GMP_LIBRARIES})
|
||||
endif()
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(GENERATOR_IS_MULTI_CONFIG)
|
||||
string(REPLACE "\${BUILD_TYPE}" "$<CONFIG>" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
else()
|
||||
string(REPLACE "\${BUILD_TYPE}" "" TEST_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
endif()
|
||||
|
||||
GET_BLENDER_TEST_INSTALL_DIR(TEST_INSTALL_DIR)
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${TESTS_OUTPUT_DIR}"
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TESTS_OUTPUT_DIR}"
|
||||
|
|
|
@ -388,6 +388,43 @@ function(blender_add_lib
|
|||
set_property(GLOBAL APPEND PROPERTY BLENDER_LINK_LIBS ${name})
|
||||
endfunction()
|
||||
|
||||
function(blender_add_test_suite)
|
||||
if (ARGC LESS 1)
|
||||
message(FATAL_ERROR "No arguments supplied to blender_add_test_suite()")
|
||||
endif()
|
||||
|
||||
# Parse the arguments
|
||||
set(oneValueArgs TARGET SUITE_NAME)
|
||||
set(multiValueArgs SOURCES)
|
||||
cmake_parse_arguments(ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
# Figure out the release dir, as some tests need files from there.
|
||||
GET_BLENDER_TEST_INSTALL_DIR(TEST_INSTALL_DIR)
|
||||
if(APPLE)
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/Blender.app/Contents/Resources/${BLENDER_VERSION})
|
||||
else()
|
||||
if(WIN32 OR WITH_INSTALL_PORTABLE)
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/${BLENDER_VERSION})
|
||||
else()
|
||||
set(_test_release_dir ${TEST_INSTALL_DIR}/share/blender/${BLENDER_VERSION})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Define a test case with our custom gtest_add_tests() command.
|
||||
include(GTest)
|
||||
gtest_add_tests(
|
||||
TARGET ${ARGS_TARGET}
|
||||
SOURCES "${ARGS_SOURCES}"
|
||||
TEST_PREFIX ${ARGS_SUITE_NAME}
|
||||
WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
|
||||
EXTRA_ARGS
|
||||
--test-assets-dir "${CMAKE_SOURCE_DIR}/../lib/tests"
|
||||
--test-release-dir "${_test_release_dir}"
|
||||
)
|
||||
|
||||
unset(_test_release_dir)
|
||||
endfunction()
|
||||
|
||||
# Add tests for a Blender library, to be called in tandem with blender_add_lib().
|
||||
# The tests will be part of the blender_test executable (see tests/gtests/runner).
|
||||
function(blender_add_test_lib
|
||||
|
@ -421,6 +458,12 @@ function(blender_add_test_lib
|
|||
blender_add_lib__impl(${name} "${sources}" "${includes}" "${includes_sys}" "${library_deps}")
|
||||
|
||||
set_property(GLOBAL APPEND PROPERTY BLENDER_TEST_LIBS ${name})
|
||||
|
||||
blender_add_test_suite(
|
||||
TARGET blender_test
|
||||
SUITE_NAME ${name}
|
||||
SOURCES "${sources}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
||||
|
@ -454,14 +497,10 @@ function(blender_add_test_executable
|
|||
SKIP_ADD_TEST
|
||||
)
|
||||
|
||||
include(GTest)
|
||||
set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_SOURCE_DIR}/build_files/cmake/Modules/GTestAddTests.cmake
|
||||
)
|
||||
|
||||
gtest_discover_tests(${name}_test
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
WORKING_DIRECTORY "${TEST_INSTALL_DIR}"
|
||||
blender_add_test_suite(
|
||||
TARGET ${name}_test
|
||||
SUITE_NAME ${name}
|
||||
SOURCES "${sources}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
|
|
@ -423,13 +423,14 @@ int writeObjectsVDB(const string &filename,
|
|||
|
||||
if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
|
||||
|
||||
if (clipGrid) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size");
|
||||
}
|
||||
if (mantaGrid->getType() & GridBase::TypeInt) {
|
||||
debMsg("Writing int grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
|
||||
Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
|
||||
if (clipGrid && mantaIntGrid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<int, openvdb::Int32Grid>(mantaIntGrid, clip, vdbClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
|
@ -440,6 +441,11 @@ int writeObjectsVDB(const string &filename,
|
|||
Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
|
||||
// Only supply clip grid if real grid is not equal to the clip grid
|
||||
openvdb::FloatGrid::Ptr tmpClipGrid = (mantaRealGrid == clipGrid) ? nullptr : vdbClipGrid;
|
||||
if (clipGrid && mantaRealGrid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<Real, openvdb::FloatGrid>(mantaRealGrid, clip, tmpClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
|
@ -448,6 +454,11 @@ int writeObjectsVDB(const string &filename,
|
|||
gClass = (mantaGrid->getType() & GridBase::TypeMAC) ? openvdb::GRID_STAGGERED :
|
||||
openvdb::GRID_UNKNOWN;
|
||||
Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
|
||||
if (clipGrid && mantaVec3Grid->saveSparse()) {
|
||||
assertMsg(clipGrid->getSize() == mantaGrid->getSize(),
|
||||
"writeObjectsVDB: Clip grid and exported grid must have the same size "
|
||||
<< clipGrid->getSize() << " vs " << mantaGrid->getSize());
|
||||
}
|
||||
vdbGrid = exportVDB<Vec3, openvdb::Vec3SGrid>(mantaVec3Grid, clip, vdbClipGrid);
|
||||
gridsVDB.push_back(vdbGrid);
|
||||
}
|
||||
|
|
|
@ -282,7 +282,7 @@ def list_render_passes(scene, srl):
|
|||
yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR')
|
||||
|
||||
# Denoising passes.
|
||||
if crl.use_denoising or crl.denoising_store_passes:
|
||||
if (scene.cycles.use_denoising and crl.use_denoising) or crl.denoising_store_passes:
|
||||
yield ("Noisy Image", "RGBA", 'COLOR')
|
||||
if crl.denoising_store_passes:
|
||||
yield ("Denoising Normal", "XYZ", 'VECTOR')
|
||||
|
|
|
@ -1929,18 +1929,19 @@ void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector<WorkT
|
|||
}
|
||||
|
||||
uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h);
|
||||
if (task.adaptive_sampling.use) {
|
||||
step_samples = task.adaptive_sampling.align_static_samples(step_samples);
|
||||
}
|
||||
|
||||
/* Render all samples. */
|
||||
int start_sample = rtile.start_sample;
|
||||
int end_sample = rtile.start_sample + rtile.num_samples;
|
||||
|
||||
for (int sample = start_sample; sample < end_sample; sample += step_samples) {
|
||||
for (int sample = start_sample; sample < end_sample;) {
|
||||
/* Setup and copy work tile to device. */
|
||||
wtile->start_sample = sample;
|
||||
wtile->num_samples = min(step_samples, end_sample - sample);
|
||||
wtile->num_samples = step_samples;
|
||||
if (task.adaptive_sampling.use) {
|
||||
wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
|
||||
}
|
||||
wtile->num_samples = min(wtile->num_samples, end_sample - sample);
|
||||
work_tiles.copy_to_device();
|
||||
|
||||
CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
|
||||
|
@ -1962,7 +1963,8 @@ void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector<WorkT
|
|||
cuda_assert(cuCtxSynchronize());
|
||||
|
||||
/* Update progress. */
|
||||
rtile.sample = sample + wtile->num_samples;
|
||||
sample += wtile->num_samples;
|
||||
rtile.sample = sample;
|
||||
task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
|
||||
|
||||
if (task.get_cancel()) {
|
||||
|
|
|
@ -920,8 +920,7 @@ class CPUDevice : public Device {
|
|||
ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride;
|
||||
if (buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = tile.sample / max((float)tile.start_sample + 1.0f,
|
||||
buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = tile.sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if (sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(kg, buffer, sample_multiplier);
|
||||
}
|
||||
|
@ -997,7 +996,7 @@ class CPUDevice : public Device {
|
|||
coverage.finalize();
|
||||
}
|
||||
|
||||
if (task.adaptive_sampling.use) {
|
||||
if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) {
|
||||
adaptive_sampling_post(tile, kg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -760,9 +760,6 @@ class OptiXDevice : public CUDADevice {
|
|||
const int end_sample = rtile.start_sample + rtile.num_samples;
|
||||
// Keep this number reasonable to avoid running into TDRs
|
||||
int step_samples = (info.display_device ? 8 : 32);
|
||||
if (task.adaptive_sampling.use) {
|
||||
step_samples = task.adaptive_sampling.align_static_samples(step_samples);
|
||||
}
|
||||
|
||||
// Offset into launch params buffer so that streams use separate data
|
||||
device_ptr launch_params_ptr = launch_params.device_pointer +
|
||||
|
@ -770,10 +767,14 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
for (int sample = rtile.start_sample; sample < end_sample; sample += step_samples) {
|
||||
for (int sample = rtile.start_sample; sample < end_sample;) {
|
||||
// Copy work tile information to device
|
||||
wtile.num_samples = min(step_samples, end_sample - sample);
|
||||
wtile.start_sample = sample;
|
||||
wtile.num_samples = step_samples;
|
||||
if (task.adaptive_sampling.use) {
|
||||
wtile.num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
|
||||
}
|
||||
wtile.num_samples = min(wtile.num_samples, end_sample - sample);
|
||||
device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile);
|
||||
check_result_cuda(
|
||||
cuMemcpyHtoDAsync(d_wtile_ptr, &wtile, sizeof(wtile), cuda_stream[thread_index]));
|
||||
|
@ -815,7 +816,8 @@ class OptiXDevice : public CUDADevice {
|
|||
check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index]));
|
||||
|
||||
// Update current sample, so it is displayed correctly
|
||||
rtile.sample = wtile.start_sample + wtile.num_samples;
|
||||
sample += wtile.num_samples;
|
||||
rtile.sample = sample;
|
||||
// Update task progress after the kernel completed rendering
|
||||
task.update_progress(&rtile, wtile.w * wtile.h * wtile.num_samples);
|
||||
|
||||
|
|
|
@ -223,8 +223,8 @@ bool DeviceSplitKernel::path_trace(DeviceTask &task,
|
|||
subtile.num_samples = samples_per_second;
|
||||
|
||||
if (task.adaptive_sampling.use) {
|
||||
subtile.num_samples = task.adaptive_sampling.align_dynamic_samples(subtile.start_sample,
|
||||
subtile.num_samples);
|
||||
subtile.num_samples = task.adaptive_sampling.align_samples(subtile.start_sample,
|
||||
subtile.num_samples);
|
||||
}
|
||||
|
||||
/* Don't go beyond requested number of samples. */
|
||||
|
|
|
@ -144,41 +144,20 @@ AdaptiveSampling::AdaptiveSampling() : use(true), adaptive_step(0), min_samples(
|
|||
}
|
||||
|
||||
/* Render samples in steps that align with the adaptive filtering. */
|
||||
int AdaptiveSampling::align_static_samples(int samples) const
|
||||
int AdaptiveSampling::align_samples(int sample, int num_samples) const
|
||||
{
|
||||
if (samples > adaptive_step) {
|
||||
/* Make multiple of adaptive_step. */
|
||||
while (samples % adaptive_step != 0) {
|
||||
samples--;
|
||||
}
|
||||
int end_sample = sample + num_samples;
|
||||
|
||||
/* Round down end sample to the nearest sample that needs filtering. */
|
||||
end_sample &= ~(adaptive_step - 1);
|
||||
|
||||
if (end_sample <= sample) {
|
||||
/* In order to reach the next sample that needs filtering, we'd need
|
||||
* to increase num_samples. We don't do that in this function, so
|
||||
* just keep it as is and don't filter this time around. */
|
||||
return num_samples;
|
||||
}
|
||||
else if (samples < adaptive_step) {
|
||||
/* Make divisor of adaptive_step. */
|
||||
while (adaptive_step % samples != 0) {
|
||||
samples--;
|
||||
}
|
||||
}
|
||||
|
||||
return max(samples, 1);
|
||||
}
|
||||
|
||||
/* Render samples in steps that align with the adaptive filtering, with the
|
||||
* suggested number of samples dynamically changing. */
|
||||
int AdaptiveSampling::align_dynamic_samples(int offset, int samples) const
|
||||
{
|
||||
/* Round so that we end up on multiples of adaptive_samples. */
|
||||
samples += offset;
|
||||
|
||||
if (samples > adaptive_step) {
|
||||
/* Make multiple of adaptive_step. */
|
||||
while (samples % adaptive_step != 0) {
|
||||
samples--;
|
||||
}
|
||||
}
|
||||
|
||||
samples -= offset;
|
||||
|
||||
return max(samples, 1);
|
||||
return end_sample - sample;
|
||||
}
|
||||
|
||||
bool AdaptiveSampling::need_filter(int sample) const
|
||||
|
|
|
@ -117,8 +117,7 @@ class AdaptiveSampling {
|
|||
public:
|
||||
AdaptiveSampling();
|
||||
|
||||
int align_static_samples(int samples) const;
|
||||
int align_dynamic_samples(int offset, int samples) const;
|
||||
int align_samples(int sample, int num_samples) const;
|
||||
bool need_filter(int sample) const;
|
||||
|
||||
bool use;
|
||||
|
|
|
@ -139,7 +139,7 @@ kernel_cuda_adaptive_scale_samples(WorkTile *tile, int start_sample, int sample,
|
|||
ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride;
|
||||
if(buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = sample / max((float)start_sample + 1.0f, buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if(sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(&kg, buffer, sample_multiplier);
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@ ccl_device void kernel_adaptive_adjust_samples(KernelGlobals *kg)
|
|||
int sample = kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples;
|
||||
if (buffer[kernel_data.film.pass_sample_count] < 0.0f) {
|
||||
buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count];
|
||||
float sample_multiplier = sample / max((float)kernel_split_params.tile.start_sample + 1.0f,
|
||||
buffer[kernel_data.film.pass_sample_count]);
|
||||
float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count];
|
||||
if (sample_multiplier != 1.0f) {
|
||||
kernel_adaptive_post_adjust(kg, buffer, sample_multiplier);
|
||||
}
|
||||
|
|
|
@ -459,13 +459,17 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
|
|||
int device_num = device->device_number(tile_device);
|
||||
|
||||
while (!tile_manager.next_tile(tile, device_num, tile_types)) {
|
||||
if (steal_tile(rtile, tile_device, tile_lock)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Wait for denoising tiles to become available */
|
||||
if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) {
|
||||
denoising_cond.wait(tile_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
return steal_tile(rtile, tile_device, tile_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* fill render tile */
|
||||
|
@ -477,6 +481,7 @@ bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_typ
|
|||
rtile.num_samples = tile_manager.state.num_samples;
|
||||
rtile.resolution = tile_manager.state.resolution_divider;
|
||||
rtile.tile_index = tile->index;
|
||||
rtile.stealing_state = RenderTile::NO_STEALING;
|
||||
|
||||
if (tile->state == Tile::DENOISE) {
|
||||
rtile.task = RenderTile::DENOISE;
|
||||
|
|
|
@ -225,6 +225,9 @@ GHOST_SystemWin32::GHOST_SystemWin32()
|
|||
#ifdef WITH_INPUT_NDOF
|
||||
m_ndofManager = new GHOST_NDOFManagerWin32(*this);
|
||||
#endif
|
||||
|
||||
getCursorPosition(m_mousePosX, m_mousePosY);
|
||||
m_mouseTimestamp = ::GetTickCount();
|
||||
}
|
||||
|
||||
GHOST_SystemWin32::~GHOST_SystemWin32()
|
||||
|
@ -533,7 +536,20 @@ GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32
|
|||
{
|
||||
if (!::GetActiveWindow())
|
||||
return GHOST_kFailure;
|
||||
return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
|
||||
|
||||
INPUT input;
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.mouseData = 0;
|
||||
input.mi.time = ::GetTickCount();
|
||||
/* Map from virtual screen to 0-65536. */
|
||||
input.mi.dx = (x - GetSystemMetrics(SM_XVIRTUALSCREEN)) * 65536 /
|
||||
GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
input.mi.dy = (y - GetSystemMetrics(SM_YVIRTUALSCREEN)) * 65536 /
|
||||
GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK;
|
||||
SendInput(1, &input, sizeof(input));
|
||||
|
||||
return GHOST_kSuccess;
|
||||
}
|
||||
|
||||
GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
|
@ -942,8 +958,18 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
|
|||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
|
||||
/* Ensure button click occurs at its intended position. */
|
||||
processCursorEvent(window);
|
||||
/* Move mouse to button event position. */
|
||||
if (!window->m_tabletInRange) {
|
||||
processCursorEvent(window);
|
||||
}
|
||||
else {
|
||||
/* Tablet should be hadling inbetween mouse moves, only move to event position. */
|
||||
DWORD msgPos = ::GetMessagePos();
|
||||
int msgPosX = GET_X_LPARAM(msgPos);
|
||||
int msgPosY = GET_Y_LPARAM(msgPos);
|
||||
system->pushEvent(new GHOST_EventCursor(
|
||||
::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td));
|
||||
}
|
||||
|
||||
window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
|
||||
return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask, td);
|
||||
|
@ -1101,18 +1127,79 @@ void GHOST_SystemWin32::processPointerEvent(
|
|||
system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y);
|
||||
}
|
||||
|
||||
GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
void GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
|
||||
{
|
||||
/* Cursor moves handled by tablets while active. */
|
||||
if (window->m_tabletInRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
|
||||
|
||||
DWORD msgPos = ::GetMessagePos();
|
||||
GHOST_TInt32 x_screen = GET_X_LPARAM(msgPos);
|
||||
GHOST_TInt32 y_screen = GET_Y_LPARAM(msgPos);
|
||||
GHOST_TInt32 x_accum = 0, y_accum = 0;
|
||||
LONG msgTime = ::GetMessageTime();
|
||||
|
||||
if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
|
||||
GHOST_TInt32 x_new = x_screen;
|
||||
GHOST_TInt32 y_new = y_screen;
|
||||
/* GetMessagePointsEx processes points as 16 bit integers and can fail or return erroneous values
|
||||
* if negative input is not truncated. */
|
||||
int msgPosX = GET_X_LPARAM(msgPos) & 0x0000FFFF;
|
||||
int msgPosY = GET_Y_LPARAM(msgPos) & 0x0000FFFF;
|
||||
|
||||
const int maxPoints = 64;
|
||||
MOUSEMOVEPOINT currentPoint = {msgPosX, msgPosY, (DWORD)msgTime, 0};
|
||||
MOUSEMOVEPOINT points[maxPoints] = {0};
|
||||
/* GetMouseMovePointsEx returns the number of points returned that are less than or equal to the
|
||||
* requested point. If the requested point is the most recent, this returns up to 64 requested
|
||||
* points. */
|
||||
int numPoints = ::GetMouseMovePointsEx(
|
||||
sizeof(MOUSEMOVEPOINT), ¤tPoint, points, maxPoints, GMMP_USE_DISPLAY_POINTS);
|
||||
|
||||
if (numPoints == -1) {
|
||||
/* Points at edge of screen are often not in the queue, use the message's point instead. */
|
||||
numPoints = 1;
|
||||
points[0] = currentPoint;
|
||||
}
|
||||
|
||||
GHOST_TInt32 x_accum = 0, y_accum = 0;
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
|
||||
/* Points are in reverse chronological order. Find least recent, unprocessed mouse move. */
|
||||
int i;
|
||||
for (i = 0; i < numPoints; i++) {
|
||||
if (points[i].time < system->m_mouseTimestamp) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* GetMouseMovePointsEx returns 16 bit number as 32 bit. If negative, we need to sign extend.
|
||||
*/
|
||||
points[i].x = points[i].x > 32767 ? points[i].x | 0xFFFF0000 : points[i].x;
|
||||
points[i].y = points[i].y > 32767 ? points[i].y | 0xFFFF0000 : points[i].y;
|
||||
|
||||
if (points[i].time == system->m_mouseTimestamp && points[i].x == system->m_mousePosX &&
|
||||
points[i].y == system->m_mousePosY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (--i >= 0) {
|
||||
system->pushEvent(new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
points[i].x + x_accum,
|
||||
points[i].y + y_accum,
|
||||
GHOST_TABLET_DATA_NONE));
|
||||
}
|
||||
|
||||
DWORD lastTimestamp = points[0].time;
|
||||
|
||||
/* Check if we need to wrap the cursor. */
|
||||
if (window->getCursorGrabModeIsWarp()) {
|
||||
/* Wrap based on current cursor position in case Win32 mouse move queue is out of order due to
|
||||
* prior wrap. */
|
||||
POINT point;
|
||||
::GetCursorPos(&point);
|
||||
GHOST_TInt32 x_current = point.x;
|
||||
GHOST_TInt32 y_current = point.y;
|
||||
GHOST_TInt32 x_wrap = point.x;
|
||||
GHOST_TInt32 y_wrap = point.y;
|
||||
GHOST_Rect bounds;
|
||||
|
||||
/* Fallback to window bounds. */
|
||||
|
@ -1122,23 +1209,24 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
|
|||
|
||||
/* Could also clamp to screen bounds wrap with a window outside the view will fail atm.
|
||||
* Use offset of 8 in case the window is at screen bounds. */
|
||||
bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis());
|
||||
bounds.wrapPoint(x_wrap, y_wrap, 2, window->getCursorGrabAxis());
|
||||
|
||||
window->getCursorGrabAccum(x_accum, y_accum);
|
||||
if (x_new != x_screen || y_new != y_screen) {
|
||||
system->setCursorPosition(x_new, y_new); /* wrap */
|
||||
window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
|
||||
if (x_wrap != x_current || y_wrap != y_current) {
|
||||
system->setCursorPosition(x_wrap, y_wrap);
|
||||
window->setCursorGrabAccum(x_accum + (x_current - x_wrap), y_accum + (y_current - y_wrap));
|
||||
|
||||
/* First message after SendInput wrap is invalid for unknown reasons, skip events until one
|
||||
* tick after SendInput event time. */
|
||||
lastTimestamp = ::GetTickCount() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
GHOST_TabletData td = window->m_tabletInRange ? window->getLastTabletData() :
|
||||
GHOST_TABLET_DATA_NONE;
|
||||
return new GHOST_EventCursor(system->getMilliSeconds(),
|
||||
GHOST_kEventCursorMove,
|
||||
window,
|
||||
x_screen + x_accum,
|
||||
y_screen + y_accum,
|
||||
td);
|
||||
system->m_mousePosX = points[0].x;
|
||||
system->m_mousePosY = points[0].y;
|
||||
/* Use latest time, checking for overflow. */
|
||||
if (lastTimestamp > system->m_mouseTimestamp || ::GetTickCount() < system->m_mouseTimestamp) {
|
||||
system->m_mouseTimestamp = lastTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_SystemWin32::processWheelEvent(GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam)
|
||||
|
@ -1559,7 +1647,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|||
break;
|
||||
case WT_PROXIMITY: {
|
||||
if (window->useTabletAPI(GHOST_kTabletWintab)) {
|
||||
window->m_tabletInRange = LOWORD(lParam);
|
||||
if (LOWORD(lParam)) {
|
||||
window->m_tabletInRange = true;
|
||||
}
|
||||
else {
|
||||
window->processWintabLeave();
|
||||
}
|
||||
}
|
||||
eventHandled = true;
|
||||
break;
|
||||
|
@ -1626,7 +1719,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|||
}
|
||||
|
||||
if (!window->m_tabletInRange) {
|
||||
event = processCursorEvent(window);
|
||||
processCursorEvent(window);
|
||||
eventHandled = true;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL: {
|
||||
|
@ -1672,6 +1766,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
|||
case WM_MOUSELEAVE:
|
||||
window->m_mousePresent = false;
|
||||
window->setWintabOverlap(false);
|
||||
if (!window->m_tabletInRange) {
|
||||
processCursorEvent(window);
|
||||
}
|
||||
break;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Mouse events, ignored
|
||||
|
|
|
@ -340,9 +340,8 @@ class GHOST_SystemWin32 : public GHOST_System {
|
|||
/**
|
||||
* Creates cursor event.
|
||||
* \param window: The window receiving the event (the active window).
|
||||
* \return The event created.
|
||||
*/
|
||||
static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window);
|
||||
static void processCursorEvent(GHOST_WindowWin32 *window);
|
||||
|
||||
/**
|
||||
* Handles a mouse wheel event.
|
||||
|
@ -463,16 +462,23 @@ class GHOST_SystemWin32 : public GHOST_System {
|
|||
__int64 m_lfstart;
|
||||
/** AltGr on current keyboard layout. */
|
||||
bool m_hasAltGr;
|
||||
/** language identifier. */
|
||||
/** Language identifier. */
|
||||
WORD m_langId;
|
||||
/** stores keyboard layout. */
|
||||
/** Stores keyboard layout. */
|
||||
HKL m_keylayout;
|
||||
|
||||
/** Console status */
|
||||
/** Console status. */
|
||||
int m_consoleStatus;
|
||||
|
||||
/** Wheel delta accumulator */
|
||||
/** Wheel delta accumulator. */
|
||||
int m_wheelDeltaAccum;
|
||||
|
||||
/** Last mouse x position. */
|
||||
int m_mousePosX;
|
||||
/** Last mouse y position. */
|
||||
int m_mousePosY;
|
||||
/** Last mouse timestamp. */
|
||||
DWORD m_mouseTimestamp;
|
||||
};
|
||||
|
||||
inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys &keys) const
|
||||
|
|
|
@ -1155,16 +1155,21 @@ void GHOST_WindowWin32::setWintabOverlap(bool overlap)
|
|||
/* If context is disabled, Windows Ink may be active and managing m_tabletInRange. Don't
|
||||
* modify it. */
|
||||
if (!(context.lcStatus & CXS_DISABLED)) {
|
||||
/* Set tablet as not in range, proximity event may not occur. */
|
||||
m_tabletInRange = false;
|
||||
/* Clear the packet queue. */
|
||||
m_wintab.packetsGet(m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
processWintabLeave();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabLeave()
|
||||
{
|
||||
m_tabletInRange = false;
|
||||
m_wintab.buttons = 0;
|
||||
/* Clear the packet queue. */
|
||||
m_wintab.packetsGet(m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
|
||||
}
|
||||
|
||||
void GHOST_WindowWin32::processWintabDisplayChangeEvent()
|
||||
{
|
||||
LOGCONTEXT lc_sys = {0}, lc_curr = {0};
|
||||
|
@ -1327,17 +1332,32 @@ GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin3
|
|||
out.tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
|
||||
}
|
||||
|
||||
out.button = wintabMouseToGhost(pkt.pkCursor, LOWORD(pkt.pkButtons));
|
||||
switch (HIWORD(pkt.pkButtons)) {
|
||||
case TBN_NONE:
|
||||
out.type = GHOST_kEventCursorMove;
|
||||
break;
|
||||
case TBN_DOWN:
|
||||
out.type = GHOST_kEventButtonDown;
|
||||
break;
|
||||
case TBN_UP:
|
||||
out.type = GHOST_kEventButtonUp;
|
||||
break;
|
||||
/* Some Wintab libraries don't handle relative button input, so we track button presses
|
||||
* manually. */
|
||||
out.button = GHOST_kButtonMaskNone;
|
||||
out.type = GHOST_kEventCursorMove;
|
||||
|
||||
DWORD buttonsChanged = m_wintab.buttons ^ pkt.pkButtons;
|
||||
if (buttonsChanged) {
|
||||
/* Find the index for the changed button from the button map. */
|
||||
WORD physicalButton = 0;
|
||||
for (DWORD diff = (unsigned)buttonsChanged >> 1; diff > 0; diff = (unsigned)diff >> 1) {
|
||||
physicalButton++;
|
||||
}
|
||||
|
||||
out.button = wintabMouseToGhost(pkt.pkCursor, physicalButton);
|
||||
|
||||
if (out.button != GHOST_kButtonMaskNone) {
|
||||
if (buttonsChanged & pkt.pkButtons) {
|
||||
out.type = GHOST_kEventButtonDown;
|
||||
}
|
||||
else {
|
||||
out.type = GHOST_kEventButtonUp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only update handled button, in case multiple button events arrived simultaneously. */
|
||||
m_wintab.buttons ^= 1 << physicalButton;
|
||||
}
|
||||
|
||||
out.time = system->tickCountToMillis(pkt.pkTime);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
// PACKETDATA and PACKETMODE modify structs in pktdef.h, so make sure they come first
|
||||
#define PACKETDATA \
|
||||
(PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_X | PK_Y | PK_TIME)
|
||||
#define PACKETMODE PK_BUTTONS
|
||||
#define PACKETMODE 0
|
||||
#include <pktdef.h>
|
||||
|
||||
class GHOST_SystemWin32;
|
||||
|
@ -466,6 +466,11 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
|||
*/
|
||||
void setWintabOverlap(bool overlap);
|
||||
|
||||
/**
|
||||
* Resets Wintab state.
|
||||
*/
|
||||
void processWintabLeave();
|
||||
|
||||
/**
|
||||
* Handle Wintab coordinate changes when DisplayChange events occur.
|
||||
*/
|
||||
|
@ -614,6 +619,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
|
|||
HCTX context = NULL;
|
||||
/** Number of connected Wintab digitizers. */
|
||||
UINT numDevices = 0;
|
||||
/** Pressed button map. */
|
||||
GHOST_TUns8 buttons = 0;
|
||||
LONG maxPressure = 0;
|
||||
LONG maxAzimuth = 0, maxAltitude = 0;
|
||||
/** Reusable buffer to read in Wintab Packets. */
|
||||
|
|
|
@ -1394,15 +1394,14 @@ bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain)
|
|||
if (with_debug)
|
||||
cout << "MANTA::readGuiding()" << endl;
|
||||
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
if (!mUsingGuiding)
|
||||
return false;
|
||||
if (!fds)
|
||||
if (!fmd)
|
||||
return false;
|
||||
|
||||
ostringstream ss;
|
||||
vector<string> pythonCommands;
|
||||
FluidDomainSettings *fds = fmd->domain;
|
||||
|
||||
string directory = (sourceDomain) ? getDirectory(fmd, FLUID_DOMAIN_DIR_DATA) :
|
||||
getDirectory(fmd, FLUID_DOMAIN_DIR_GUIDE);
|
||||
|
|
|
@ -412,7 +412,9 @@ def fluid_post_step_$ID$():\n\
|
|||
mantaMsg('Fluid post step')\n\
|
||||
\n\
|
||||
# Copy vel grid to reals grids (which Blender internal will in turn use for vel access)\n\
|
||||
copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n";
|
||||
copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n\
|
||||
if using_guiding_s$ID$:\n\
|
||||
copyVec3ToReal(source=guidevel_sg$ID$, targetX=x_guidevel_s$ID$, targetY=y_guidevel_s$ID$, targetZ=z_guidevel_s$ID$)\n";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// DESTRUCTION
|
||||
|
@ -676,7 +678,9 @@ const std::string fluid_load_guiding =
|
|||
def fluid_load_guiding_$ID$(path, framenr, file_format):\n\
|
||||
mantaMsg('Fluid load guiding, frame ' + str(framenr))\n\
|
||||
guidevel_sg$ID$.setName('$NAME_VELOCITY_GUIDE$')\n\
|
||||
fluid_file_import_s$ID$(dict=fluid_guiding_dict_s$ID$, path=path, framenr=framenr, file_format=file_format, file_name=file_guiding_s$ID$)\n";
|
||||
fluid_file_import_s$ID$(dict=fluid_guiding_dict_s$ID$, path=path, framenr=framenr, file_format=file_format, file_name=file_guiding_s$ID$)\n\
|
||||
\n\
|
||||
copyVec3ToReal(source=guidevel_sg$ID$, targetX=x_guidevel_s$ID$, targetY=y_guidevel_s$ID$, targetZ=z_guidevel_s$ID$)\n";
|
||||
|
||||
const std::string fluid_load_vel =
|
||||
"\n\
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -345,6 +345,7 @@ class PHYSICS_PT_cloth_object_collision(PhysicButtonsPanel, Panel):
|
|||
|
||||
cloth = context.cloth.collision_settings
|
||||
md = context.cloth
|
||||
ob = context.object
|
||||
|
||||
layout.active = cloth.use_collision and cloth_panel_enabled(md)
|
||||
|
||||
|
@ -356,6 +357,9 @@ class PHYSICS_PT_cloth_object_collision(PhysicButtonsPanel, Panel):
|
|||
col = flow.column()
|
||||
col.prop(cloth, "impulse_clamp")
|
||||
|
||||
col = flow.column()
|
||||
col.prop_search(cloth, "vertex_group_object_collisions", ob, "vertex_groups", text="Vertex Group")
|
||||
|
||||
col = flow.column()
|
||||
col.prop(cloth, "collection")
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ def physics_add(layout, md, name, type, typeicon, toggles):
|
|||
if toggles:
|
||||
row.prop(md, "show_viewport", text="")
|
||||
row.prop(md, "show_render", text="")
|
||||
return row
|
||||
else:
|
||||
row.operator(
|
||||
"object.modifier_add",
|
||||
|
@ -89,7 +90,10 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
|
|||
col.operator("object.forcefield_toggle", text="Force Field", icon='X')
|
||||
|
||||
if obj.type == 'MESH':
|
||||
physics_add(col, context.collision, "Collision", 'COLLISION', 'MOD_PHYSICS', False)
|
||||
row = physics_add(col, context.collision, "Collision", 'COLLISION', 'MOD_PHYSICS', False)
|
||||
if row and obj.collision:
|
||||
row.prop(obj.collision, "use", text="", icon="HIDE_OFF" if obj.collision.use else "HIDE_ON")
|
||||
|
||||
physics_add(col, context.cloth, "Cloth", 'CLOTH', 'MOD_CLOTH', True)
|
||||
physics_add(col, context.dynamic_paint, "Dynamic Paint", 'DYNAMIC_PAINT', 'MOD_DYNAMICPAINT', True)
|
||||
|
||||
|
|
|
@ -141,16 +141,9 @@ class SEQUENCER_HT_header(Header):
|
|||
|
||||
SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
|
||||
|
||||
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
|
||||
layout.separator_spacer()
|
||||
row = layout.row(align=True)
|
||||
row.prop(sequencer_tool_settings, "fit_method", text="")
|
||||
layout.separator_spacer()
|
||||
layout.separator_spacer()
|
||||
|
||||
if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
|
||||
if st.view_type == 'PREVIEW':
|
||||
layout.separator_spacer()
|
||||
|
||||
layout.prop(st, "display_mode", text="", icon_only=True)
|
||||
layout.prop(st, "preview_channels", text="", icon_only=True)
|
||||
|
||||
|
|
|
@ -1004,7 +1004,7 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False):
|
|||
if item.draw_cursor is not None:
|
||||
def handle_fn(context, item, tool, xy):
|
||||
item.draw_cursor(context, tool, xy)
|
||||
handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type)
|
||||
handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type, 'WINDOW')
|
||||
handle_map[space_type] = handle
|
||||
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ compositor_node_categories = [
|
|||
NodeItem("CompositorNodeHueCorrect"),
|
||||
NodeItem("CompositorNodeBrightContrast"),
|
||||
NodeItem("CompositorNodeGamma"),
|
||||
NodeItem("CompositorNodeExposure"),
|
||||
NodeItem("CompositorNodeExposure"),
|
||||
NodeItem("CompositorNodeColorCorrection"),
|
||||
NodeItem("CompositorNodeTonemap"),
|
||||
NodeItem("CompositorNodeZcombine"),
|
||||
|
@ -488,6 +488,7 @@ geometry_node_categories = [
|
|||
NodeItem("GeometryNodeAttributeFill"),
|
||||
NodeItem("GeometryNodeAttributeMix"),
|
||||
NodeItem("GeometryNodeAttributeColorRamp"),
|
||||
NodeItem("GeometryNodeAttributeVectorMath"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
NodeItem("ShaderNodeValToRGB"),
|
||||
|
@ -515,6 +516,7 @@ geometry_node_categories = [
|
|||
NodeItem("GeometryNodePointInstance"),
|
||||
NodeItem("GeometryNodePointSeparate"),
|
||||
NodeItem("GeometryNodeRotatePoints"),
|
||||
NodeItem("GeometryNodeAlignRotationToVector"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
|
||||
NodeItem("ShaderNodeMapRange"),
|
||||
|
|
|
@ -169,8 +169,10 @@ class WriteAttribute {
|
|||
}
|
||||
|
||||
/* Get a span that new attribute values can be written into. When all values have been changed,
|
||||
* #apply_span has to be called. The span might not contain the original attribute values. */
|
||||
* #apply_span has to be called. */
|
||||
fn::GMutableSpan get_span();
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
fn::GMutableSpan get_span_for_write_only();
|
||||
/* Write the changes to the span into the actual attribute, if they aren't already. */
|
||||
void apply_span();
|
||||
|
||||
|
@ -178,7 +180,7 @@ class WriteAttribute {
|
|||
virtual void get_internal(const int64_t index, void *r_value) const = 0;
|
||||
virtual void set_internal(const int64_t index, const void *value) = 0;
|
||||
|
||||
virtual void initialize_span();
|
||||
virtual void initialize_span(const bool write_only);
|
||||
virtual void apply_span_if_necessary();
|
||||
};
|
||||
|
||||
|
@ -250,12 +252,16 @@ template<typename T> class TypedWriteAttribute {
|
|||
}
|
||||
|
||||
/* Get a span that new values can be written into. Once all values have been updated #apply_span
|
||||
* has to be called. The span might *not* contain the initial attribute values, so one should
|
||||
* generally only write to the span. */
|
||||
* has to be called. */
|
||||
MutableSpan<T> get_span()
|
||||
{
|
||||
return attribute_->get_span().typed<T>();
|
||||
}
|
||||
/* The span returned by this method might not contain the current attribute values. */
|
||||
MutableSpan<T> get_span_for_write_only()
|
||||
{
|
||||
return attribute_->get_span_for_write_only().typed<T>();
|
||||
}
|
||||
|
||||
/* Write back all changes to the actual attribute, if necessary. */
|
||||
void apply_span()
|
||||
|
|
|
@ -50,6 +50,7 @@ struct Scene;
|
|||
typedef enum eClothVertexFlag {
|
||||
CLOTH_VERT_FLAG_PINNED = (1 << 0),
|
||||
CLOTH_VERT_FLAG_NOSELFCOLL = (1 << 1), /* vertex NOT used for self collisions */
|
||||
CLOTH_VERT_FLAG_NOOBJCOLL = (1 << 2), /* vertex NOT used for object collisions */
|
||||
} eClothVertexFlag;
|
||||
|
||||
typedef struct ClothHairData {
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Format to a cryptomatte meta data key.
|
||||
*
|
||||
* Cryptomatte stores meta data. The keys are formatted containing a hash that
|
||||
* is generated from its layer name.
|
||||
*
|
||||
* The output of this function is:
|
||||
* 'cryptomatte/{hash of layer_name}/{key_name}'.
|
||||
*/
|
||||
std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name,
|
||||
const StringRefNull key_name);
|
||||
|
||||
/* Extract the cryptomatte layer name from the given `render_pass_name`.
|
||||
*
|
||||
* Cryptomatte passes are formatted with a trailing number for storing multiple samples that belong
|
||||
* to the same cryptomatte layer. This function would remove the trailing numbers to determine the
|
||||
* cryptomatte layer name.
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* A render_pass_name could be 'View Layer.CryptoMaterial02'. The cryptomatte layer would be 'View
|
||||
* Layer.CryptoMaterial'.
|
||||
*
|
||||
* NOTE: The return type is a substring of `render_pass_name` and therefore cannot outlive the
|
||||
* `render_pass_name` internal data.
|
||||
*/
|
||||
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name);
|
||||
|
||||
} // namespace blender
|
|
@ -80,6 +80,9 @@ float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
|
|||
int defbase_tot_sel,
|
||||
bool is_normalized);
|
||||
|
||||
/* This much unlocked weight is considered equivalent to none. */
|
||||
#define VERTEX_WEIGHT_LOCK_EPSILON 1e-6f
|
||||
|
||||
float BKE_defvert_calc_lock_relative_weight(float weight,
|
||||
float locked_weight,
|
||||
float unlocked_weight);
|
||||
|
|
|
@ -1357,6 +1357,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
|||
#define GEO_NODE_POINT_SEPARATE 1014
|
||||
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
|
||||
#define GEO_NODE_ROTATE_POINTS 1016
|
||||
#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
|
||||
#define GEO_NODE_ALIGN_ROTATION_TO_VECTOR 1018
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -288,7 +288,6 @@ void BKE_object_eval_uber_data(struct Depsgraph *depsgraph,
|
|||
struct Object *ob);
|
||||
void BKE_object_eval_assign_data(struct Object *object, struct ID *data, bool is_owned);
|
||||
|
||||
void BKE_object_eval_boundbox(struct Depsgraph *depsgraph, struct Object *object);
|
||||
void BKE_object_sync_to_original(struct Depsgraph *depsgraph, struct Object *object);
|
||||
|
||||
void BKE_object_eval_ptcache_reset(struct Depsgraph *depsgraph,
|
||||
|
|
|
@ -731,6 +731,7 @@ add_dependencies(bf_blenkernel bf_dna)
|
|||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
intern/armature_test.cc
|
||||
intern/cryptomatte_test.cc
|
||||
intern/fcurve_test.cc
|
||||
intern/lattice_deform_test.cc
|
||||
intern/layer_test.cc
|
||||
|
|
|
@ -830,18 +830,6 @@ void BKE_pose_splineik_evaluate(struct Depsgraph *depsgraph,
|
|||
BKE_splineik_execute_tree(depsgraph, scene, object, rootchan, ctime);
|
||||
}
|
||||
|
||||
/* Common part for both original and proxy armatrues. */
|
||||
static void pose_eval_done_common(struct Depsgraph *depsgraph, Object *object)
|
||||
{
|
||||
const bArmature *armature = (bArmature *)object->data;
|
||||
if (armature->edbo != NULL) {
|
||||
return;
|
||||
}
|
||||
bPose *pose = object->pose;
|
||||
UNUSED_VARS_NDEBUG(pose);
|
||||
BLI_assert(pose != NULL);
|
||||
BKE_object_eval_boundbox(depsgraph, object);
|
||||
}
|
||||
static void pose_eval_cleanup_common(Object *object)
|
||||
{
|
||||
bPose *pose = object->pose;
|
||||
|
@ -857,7 +845,6 @@ void BKE_pose_eval_done(struct Depsgraph *depsgraph, Object *object)
|
|||
UNUSED_VARS_NDEBUG(pose);
|
||||
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
|
||||
BLI_assert(object->type == OB_ARMATURE);
|
||||
pose_eval_done_common(depsgraph, object);
|
||||
}
|
||||
|
||||
void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *object)
|
||||
|
@ -885,7 +872,6 @@ void BKE_pose_eval_proxy_done(struct Depsgraph *depsgraph, Object *object)
|
|||
{
|
||||
BLI_assert(ID_IS_LINKED(object) && object->proxy_from != NULL);
|
||||
DEG_debug_print_eval(depsgraph, __func__, object->id.name, object);
|
||||
pose_eval_done_common(depsgraph, object);
|
||||
}
|
||||
|
||||
void BKE_pose_eval_proxy_cleanup(struct Depsgraph *depsgraph, Object *object)
|
||||
|
|
|
@ -97,26 +97,45 @@ WriteAttribute::~WriteAttribute()
|
|||
|
||||
/**
|
||||
* Get a mutable span that can be modified. When all modifications to the attribute are done,
|
||||
* #apply_span_if_necessary should be called.
|
||||
*/
|
||||
* #apply_span should be called. */
|
||||
fn::GMutableSpan WriteAttribute::get_span()
|
||||
{
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span();
|
||||
this->initialize_span(false);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void WriteAttribute::initialize_span()
|
||||
fn::GMutableSpan WriteAttribute::get_span_for_write_only()
|
||||
{
|
||||
array_buffer_ = MEM_mallocN_aligned(cpp_type_.size() * size_, cpp_type_.alignment(), __func__);
|
||||
if (size_ == 0) {
|
||||
return fn::GMutableSpan(cpp_type_);
|
||||
}
|
||||
if (array_buffer_ == nullptr) {
|
||||
this->initialize_span(true);
|
||||
}
|
||||
array_should_be_applied_ = true;
|
||||
return fn::GMutableSpan(cpp_type_, array_buffer_, size_);
|
||||
}
|
||||
|
||||
void WriteAttribute::initialize_span(const bool write_only)
|
||||
{
|
||||
const int element_size = cpp_type_.size();
|
||||
array_buffer_ = MEM_mallocN_aligned(element_size * size_, cpp_type_.alignment(), __func__);
|
||||
array_is_temporary_ = true;
|
||||
/* This does nothing for trivial types, but is necessary for general correctness. */
|
||||
cpp_type_.construct_default_n(array_buffer_, size_);
|
||||
if (write_only) {
|
||||
/* This does nothing for trivial types, but is necessary for general correctness. */
|
||||
cpp_type_.construct_default_n(array_buffer_, size_);
|
||||
}
|
||||
else {
|
||||
for (const int i : IndexRange(size_)) {
|
||||
this->get(i, POINTER_OFFSET(array_buffer_, i * element_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WriteAttribute::apply_span()
|
||||
|
@ -219,7 +238,7 @@ template<typename T> class ArrayWriteAttribute final : public WriteAttribute {
|
|||
data_[index] = *reinterpret_cast<const T *>(value);
|
||||
}
|
||||
|
||||
void initialize_span() override
|
||||
void initialize_span(const bool UNUSED(write_only)) override
|
||||
{
|
||||
array_buffer_ = data_.data();
|
||||
array_is_temporary_ = false;
|
||||
|
|
|
@ -611,6 +611,8 @@ int cloth_uses_vgroup(ClothModifierData *clmd)
|
|||
{
|
||||
return (((clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) &&
|
||||
(clmd->coll_parms->vgroup_selfcol > 0)) ||
|
||||
((clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) &&
|
||||
(clmd->coll_parms->vgroup_objcol > 0)) ||
|
||||
(clmd->sim_parms->vgroup_pressure > 0) || (clmd->sim_parms->vgroup_struct > 0) ||
|
||||
(clmd->sim_parms->vgroup_bend > 0) || (clmd->sim_parms->vgroup_shrink > 0) ||
|
||||
(clmd->sim_parms->vgroup_intern > 0) || (clmd->sim_parms->vgroup_mass > 0));
|
||||
|
@ -644,8 +646,8 @@ static void cloth_apply_vgroup(ClothModifierData *clmd, Mesh *mesh)
|
|||
verts->shrink_factor = 0.0f;
|
||||
|
||||
/* Reset vertex flags */
|
||||
verts->flags &= ~CLOTH_VERT_FLAG_PINNED;
|
||||
verts->flags &= ~CLOTH_VERT_FLAG_NOSELFCOLL;
|
||||
verts->flags &= ~(CLOTH_VERT_FLAG_PINNED | CLOTH_VERT_FLAG_NOSELFCOLL |
|
||||
CLOTH_VERT_FLAG_NOOBJCOLL);
|
||||
|
||||
MDeformVert *dvert = CustomData_get(&mesh->vdata, i, CD_MDEFORMVERT);
|
||||
if (dvert) {
|
||||
|
@ -682,6 +684,12 @@ static void cloth_apply_vgroup(ClothModifierData *clmd, Mesh *mesh)
|
|||
}
|
||||
}
|
||||
|
||||
if (dvert->dw[j].def_nr == (clmd->coll_parms->vgroup_objcol - 1)) {
|
||||
if (dvert->dw[j].weight > 0.0f) {
|
||||
verts->flags |= CLOTH_VERT_FLAG_NOOBJCOLL;
|
||||
}
|
||||
}
|
||||
|
||||
if (dvert->dw[j].def_nr == (clmd->sim_parms->vgroup_shrink - 1)) {
|
||||
/* Used for linear interpolation between min and max
|
||||
* shrink factor based on weight. */
|
||||
|
|
|
@ -829,8 +829,8 @@ Base *BKE_collection_or_layer_objects(const ViewLayer *view_layer, Collection *c
|
|||
Collection *BKE_collection_master_add()
|
||||
{
|
||||
/* Not an actual datablock, but owned by scene. */
|
||||
Collection *master_collection = MEM_callocN(sizeof(Collection), "Master Collection");
|
||||
STRNCPY(master_collection->id.name, "GRMaster Collection");
|
||||
Collection *master_collection = BKE_libblock_alloc(
|
||||
NULL, ID_GR, "Master Collection", LIB_ID_CREATE_NO_MAIN);
|
||||
master_collection->id.flag |= LIB_EMBEDDED_DATA;
|
||||
master_collection->flag |= COLLECTION_IS_MASTER;
|
||||
master_collection->color_tag = COLLECTION_COLOR_NONE;
|
||||
|
|
|
@ -1012,7 +1012,7 @@ static bool cloth_bvh_collision_is_active(const ClothModifierData *UNUSED(clmd),
|
|||
const int flags_a = verts[tri_a->tri[0]].flags & verts[tri_a->tri[1]].flags &
|
||||
verts[tri_a->tri[2]].flags;
|
||||
|
||||
if (flags_a & CLOTH_VERT_FLAG_PINNED) {
|
||||
if (flags_a & (CLOTH_VERT_FLAG_PINNED | CLOTH_VERT_FLAG_NOOBJCOLL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1238,13 +1238,8 @@ static void add_collision_object(ListBase *relations,
|
|||
int level,
|
||||
unsigned int modifier_type)
|
||||
{
|
||||
CollisionModifierData *cmd = NULL;
|
||||
|
||||
/* only get objects with collision modifier */
|
||||
if (((modifier_type == eModifierType_Collision) && ob->pd && ob->pd->deflect) ||
|
||||
(modifier_type != eModifierType_Collision)) {
|
||||
cmd = (CollisionModifierData *)BKE_modifiers_findby_type(ob, modifier_type);
|
||||
}
|
||||
ModifierData *cmd = BKE_modifiers_findby_type(ob, modifier_type);
|
||||
|
||||
if (cmd) {
|
||||
CollisionRelation *relation = MEM_callocN(sizeof(CollisionRelation), "CollisionRelation");
|
||||
|
@ -1320,6 +1315,10 @@ Object **BKE_collision_objects_create(Depsgraph *depsgraph,
|
|||
/* Get evaluated object. */
|
||||
Object *ob = (Object *)DEG_get_evaluated_id(depsgraph, &relation->ob->id);
|
||||
|
||||
if (modifier_type == eModifierType_Collision && !(ob->pd && ob->pd->deflect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ob != self) {
|
||||
objects[num] = ob;
|
||||
num++;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
|
||||
#include "BKE_cryptomatte.h"
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
|
@ -43,6 +44,7 @@
|
|||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
enum CryptomatteLayerState {
|
||||
EMPTY,
|
||||
|
@ -299,15 +301,15 @@ static uint32_t cryptomatte_determine_identifier(const std::string name)
|
|||
return BLI_hash_mm3(reinterpret_cast<const unsigned char *>(name.c_str()), name.length(), 0);
|
||||
}
|
||||
|
||||
static std::string cryptomatte_determine_prefix(const std::string name)
|
||||
static void add_render_result_meta_data(RenderResult *render_result,
|
||||
const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key_name,
|
||||
const blender::StringRefNull value)
|
||||
{
|
||||
std::stringstream stream;
|
||||
const uint32_t render_pass_identifier = cryptomatte_determine_identifier(name);
|
||||
stream << "cryptomatte/";
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex
|
||||
<< render_pass_identifier;
|
||||
stream << "/";
|
||||
return stream.str();
|
||||
BKE_render_result_stamp_data(
|
||||
render_result,
|
||||
blender::BKE_cryptomatte_meta_data_key(layer_name, key_name).c_str(),
|
||||
value.data());
|
||||
}
|
||||
|
||||
void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session,
|
||||
|
@ -335,12 +337,47 @@ void BKE_cryptomatte_store_metadata(struct CryptomatteSession *session,
|
|||
|
||||
const std::string manifest = layer->manifest_get_string();
|
||||
const std::string name = cryptomatte_determine_name(view_layer, cryptomatte_layer_name);
|
||||
const std::string prefix = cryptomatte_determine_prefix(name);
|
||||
|
||||
/* Store the meta data into the render result. */
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "name").c_str(), name.c_str());
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "hash").c_str(), "MurmurHash3_32");
|
||||
BKE_render_result_stamp_data(
|
||||
render_result, (prefix + "conversion").c_str(), "uint32_to_float32");
|
||||
BKE_render_result_stamp_data(render_result, (prefix + "manifest").c_str(), manifest.c_str());
|
||||
add_render_result_meta_data(render_result, name, "name", name);
|
||||
add_render_result_meta_data(render_result, name, "hash", "MurmurHash3_32");
|
||||
add_render_result_meta_data(render_result, name, "conversion", "uint32_to_float32");
|
||||
add_render_result_meta_data(render_result, name, "manifest", manifest);
|
||||
}
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Return the hash of the given cryptomatte layer name.
|
||||
*
|
||||
* The cryptomatte specification limits the hash to 7 characters.
|
||||
* The 7 position limitation solves issues when using cryptomatte together with OpenEXR.
|
||||
* The specification suggests to use the first 7 chars of the hashed layer_name.
|
||||
*/
|
||||
static std::string cryptomatte_layer_name_hash(const StringRef layer_name)
|
||||
{
|
||||
std::stringstream stream;
|
||||
const uint32_t render_pass_identifier = cryptomatte_determine_identifier(layer_name);
|
||||
stream << std::setfill('0') << std::setw(sizeof(uint32_t) * 2) << std::hex
|
||||
<< render_pass_identifier;
|
||||
return stream.str().substr(0, 7);
|
||||
}
|
||||
|
||||
std::string BKE_cryptomatte_meta_data_key(const StringRef layer_name, const StringRefNull key_name)
|
||||
{
|
||||
return "cryptomatte/" + cryptomatte_layer_name_hash(layer_name) + "/" + key_name;
|
||||
}
|
||||
|
||||
/* Extracts the cryptomatte name from a render pass name.
|
||||
*
|
||||
* Example: A render pass could be named `CryptoObject00`. This
|
||||
* function would remove the trailing digits and return `CryptoObject`. */
|
||||
StringRef BKE_cryptomatte_extract_layer_name(const StringRef render_pass_name)
|
||||
{
|
||||
int64_t last_token = render_pass_name.size();
|
||||
while (last_token > 0 && std::isdigit(render_pass_name[last_token - 1])) {
|
||||
last_token -= 1;
|
||||
}
|
||||
return render_pass_name.substr(0, last_token);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2021 by Blender Foundation.
|
||||
*/
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
|
||||
namespace blender::bke::tests {
|
||||
|
||||
TEST(cryptomatte, meta_data_key)
|
||||
{
|
||||
ASSERT_EQ("cryptomatte/c7dbf5e/key",
|
||||
BKE_cryptomatte_meta_data_key("ViewLayer.CryptoMaterial", "key"));
|
||||
ASSERT_EQ("cryptomatte/b990b65/𝓴𝓮𝔂",
|
||||
BKE_cryptomatte_meta_data_key("𝖚𝖓𝖎𝖈𝖔𝖉𝖊.CryptoMaterial", "𝓴𝓮𝔂"));
|
||||
}
|
||||
|
||||
TEST(cryptomatte, extract_layer_name)
|
||||
{
|
||||
ASSERT_EQ("ViewLayer.CryptoMaterial",
|
||||
BKE_cryptomatte_extract_layer_name("ViewLayer.CryptoMaterial00"));
|
||||
ASSERT_EQ("𝖚𝖓𝖎𝖈𝖔𝖉𝖊", BKE_cryptomatte_extract_layer_name("𝖚𝖓𝖎𝖈𝖔𝖉𝖊13"));
|
||||
ASSERT_EQ("NoTrailingSampleNumber",
|
||||
BKE_cryptomatte_extract_layer_name("NoTrailingSampleNumber"));
|
||||
ASSERT_EQ("W1thM1dd13Numb3rs", BKE_cryptomatte_extract_layer_name("W1thM1dd13Numb3rs09"));
|
||||
ASSERT_EQ("", BKE_cryptomatte_extract_layer_name("0123"));
|
||||
ASSERT_EQ("", BKE_cryptomatte_extract_layer_name(""));
|
||||
}
|
||||
|
||||
} // namespace blender::bke::tests
|
|
@ -899,7 +899,7 @@ float BKE_defvert_calc_lock_relative_weight(float weight,
|
|||
}
|
||||
|
||||
/* handle division by zero */
|
||||
if (locked_weight >= 1.0f) {
|
||||
if (locked_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
if (weight != 0.0f) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
|
|
@ -3921,7 +3921,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
|
|||
prev_guide = manta_has_guiding(fds->fluid, fmd, prev_frame, guide_parent);
|
||||
|
||||
/* Unused for now. */
|
||||
UNUSED_VARS(has_guide, prev_guide, next_mesh, next_guide);
|
||||
UNUSED_VARS(next_mesh, next_guide);
|
||||
|
||||
bool with_gdomain;
|
||||
with_gdomain = (fds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN);
|
||||
|
@ -4072,6 +4072,9 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
|
|||
break;
|
||||
case FLUID_DOMAIN_CACHE_REPLAY:
|
||||
default:
|
||||
if (with_guide) {
|
||||
baking_guide = !has_guide && (is_startframe || prev_guide);
|
||||
}
|
||||
baking_data = !has_data && (is_startframe || prev_data);
|
||||
if (with_smoke && with_noise) {
|
||||
baking_noise = !has_noise && (is_startframe || prev_noise);
|
||||
|
|
|
@ -39,6 +39,7 @@ TEST(view_layer, aov_unique_names)
|
|||
{
|
||||
/* Set Up */
|
||||
CLG_init();
|
||||
BKE_idtype_init();
|
||||
BKE_appdir_init();
|
||||
IMB_init();
|
||||
RE_engines_init();
|
||||
|
|
|
@ -1087,8 +1087,6 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
|
|||
/* alphabetic insertion: is in new_id */
|
||||
BKE_main_unlock(bmain);
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
|
||||
/* TODO to be removed from here! */
|
||||
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) {
|
||||
DEG_id_type_tag(bmain, type);
|
||||
|
@ -1097,6 +1095,13 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
|
|||
else {
|
||||
BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2);
|
||||
}
|
||||
|
||||
/* We also need to ensure a valid `session_uuid` for some non-main data (like embedded IDs).
|
||||
* IDs not allocated however should not need those (this would e.g. avoid generating session
|
||||
* uuids for depsgraph CoW IDs, if it was using this function). */
|
||||
if ((flag & LIB_ID_CREATE_NO_ALLOCATE) == 0) {
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
|
@ -296,11 +296,16 @@ NlaTrack *BKE_nlatrack_add(AnimData *adt, NlaTrack *prev, const bool is_liboverr
|
|||
nlt->flag = NLATRACK_SELECTED | NLATRACK_OVERRIDELIBRARY_LOCAL;
|
||||
nlt->index = BLI_listbase_count(&adt->nla_tracks);
|
||||
|
||||
/* add track to stack, and make it the active one */
|
||||
if (is_liboverride) {
|
||||
for (; prev != NULL && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0; prev = prev->next) {
|
||||
/* In liboverride case, we only add local tracks after all those comming from the linked data, so
|
||||
* we need to find the first local track. */
|
||||
if (is_liboverride && prev != NULL && (prev->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
|
||||
NlaTrack *first_local = prev->next;
|
||||
for (; first_local != NULL && (first_local->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0;
|
||||
first_local = first_local->next) {
|
||||
}
|
||||
prev = first_local != NULL ? first_local->prev : NULL;
|
||||
}
|
||||
/* Add track to stack, and make it the active one. */
|
||||
if (prev != NULL) {
|
||||
BLI_insertlinkafter(&adt->nla_tracks, prev, nlt);
|
||||
}
|
||||
|
|
|
@ -2322,14 +2322,14 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
|
|||
/* trees are created as local trees for compositor, material or texture nodes,
|
||||
* node groups and other tree types are created as library data.
|
||||
*/
|
||||
if (bmain) {
|
||||
ntree = BKE_libblock_alloc(bmain, ID_NT, name, 0);
|
||||
const bool is_embedded = (bmain == NULL);
|
||||
int flag = 0;
|
||||
if (is_embedded) {
|
||||
flag |= LIB_ID_CREATE_NO_MAIN;
|
||||
}
|
||||
else {
|
||||
ntree = MEM_callocN(sizeof(bNodeTree), "new node tree");
|
||||
ntree = BKE_libblock_alloc(bmain, ID_NT, name, flag);
|
||||
if (is_embedded) {
|
||||
ntree->id.flag |= LIB_EMBEDDED_DATA;
|
||||
*((short *)ntree->id.name) = ID_NT;
|
||||
BLI_strncpy(ntree->id.name + 2, name, sizeof(ntree->id.name));
|
||||
}
|
||||
|
||||
/* Types are fully initialized at this point,
|
||||
|
@ -4730,6 +4730,7 @@ static void registerGeometryNodes(void)
|
|||
|
||||
register_node_type_geo_attribute_compare();
|
||||
register_node_type_geo_attribute_fill();
|
||||
register_node_type_geo_attribute_vector_math();
|
||||
register_node_type_geo_triangulate();
|
||||
register_node_type_geo_edge_split();
|
||||
register_node_type_geo_transform();
|
||||
|
@ -4745,6 +4746,7 @@ static void registerGeometryNodes(void)
|
|||
register_node_type_geo_attribute_mix();
|
||||
register_node_type_geo_attribute_color_ramp();
|
||||
register_node_type_geo_rotate_points();
|
||||
register_node_type_geo_align_rotation_to_vector();
|
||||
}
|
||||
|
||||
static void registerFunctionNodes(void)
|
||||
|
|
|
@ -268,25 +268,17 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
|
|||
}
|
||||
}
|
||||
}
|
||||
BKE_object_eval_boundbox(depsgraph, ob);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(sergey): Ensure that bounding box is already calculated, and move this
|
||||
* into #BKE_object_sync_to_original().
|
||||
*/
|
||||
void BKE_object_eval_boundbox(Depsgraph *depsgraph, Object *object)
|
||||
/** Bounding box from evaluated geometry. */
|
||||
static void object_sync_boundbox_to_original(Object *object_orig, Object *object_eval)
|
||||
{
|
||||
if (!DEG_is_active(depsgraph)) {
|
||||
return;
|
||||
}
|
||||
Object *ob_orig = DEG_get_original_object(object);
|
||||
BoundBox *bb = BKE_object_boundbox_get(object);
|
||||
BoundBox *bb = BKE_object_boundbox_get(object_eval);
|
||||
if (bb != NULL) {
|
||||
if (ob_orig->runtime.bb == NULL) {
|
||||
ob_orig->runtime.bb = MEM_mallocN(sizeof(*ob_orig->runtime.bb), __func__);
|
||||
if (object_orig->runtime.bb == NULL) {
|
||||
object_orig->runtime.bb = MEM_mallocN(sizeof(*object_orig->runtime.bb), __func__);
|
||||
}
|
||||
*ob_orig->runtime.bb = *bb;
|
||||
*object_orig->runtime.bb = *bb;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,6 +307,8 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object)
|
|||
md_orig->error = BLI_strdup(md->error);
|
||||
}
|
||||
}
|
||||
|
||||
object_sync_boundbox_to_original(object_orig, object);
|
||||
}
|
||||
|
||||
bool BKE_object_eval_proxy_copy(Depsgraph *depsgraph, Object *object)
|
||||
|
|
|
@ -251,10 +251,42 @@ static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
|
|||
BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether `us_item` is before (-1), after (1) or same as (0) `us_anchor` step. */
|
||||
static int undosys_stack_order(const UndoStack *ustack,
|
||||
const UndoStep *us_anchor,
|
||||
const UndoStep *us_item)
|
||||
{
|
||||
const int index_anchor = BLI_findindex(&ustack->steps, us_anchor);
|
||||
const int index_item = BLI_findindex(&ustack->steps, us_item);
|
||||
BLI_assert(index_anchor >= 0);
|
||||
BLI_assert(index_item >= 0);
|
||||
|
||||
return (index_item == index_anchor) ? 0 : (index_item < index_anchor) ? -1 : 1;
|
||||
}
|
||||
|
||||
# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo) \
|
||||
{ \
|
||||
const UndoStep *_us_anchor = (_ustack)->step_active; \
|
||||
BLI_assert(_us_anchor == NULL || \
|
||||
(undosys_stack_order((_ustack), _us_anchor, (_us_undo)) <= 0)); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo) \
|
||||
{ \
|
||||
const UndoStep *_us_anchor = (_ustack)->step_active; \
|
||||
BLI_assert(_us_anchor == NULL || \
|
||||
(undosys_stack_order((_ustack), _us_anchor, (_us_redo)) >= 0)); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
#else
|
||||
static void undosys_stack_validate(UndoStack *UNUSED(ustack), bool UNUSED(expect_non_empty))
|
||||
{
|
||||
}
|
||||
# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo)
|
||||
# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo)
|
||||
#endif
|
||||
|
||||
UndoStack *BKE_undosys_stack_create(void)
|
||||
|
@ -676,62 +708,91 @@ bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack,
|
|||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (us == NULL) {
|
||||
CLOG_ERROR(&LOG, "called with a NULL step");
|
||||
return false;
|
||||
}
|
||||
undosys_stack_validate(ustack, true);
|
||||
|
||||
UndoStep *us_prev = us ? us->prev : NULL;
|
||||
if (us) {
|
||||
/* The current state is a copy, we need to load the previous state. */
|
||||
us = us_prev;
|
||||
/* We expect to get next-from-actual-target step here (i.e. active step in case we only undo
|
||||
* once)?
|
||||
* FIXME: this is very confusing now that we may have to undo several steps anyway, this function
|
||||
* should just get the target final step, not assume that it is getting the active one by default
|
||||
* (or the step after the target one when undoing more than one step). */
|
||||
UndoStep *us_target = us->prev;
|
||||
if (us_target == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid target step");
|
||||
return false;
|
||||
}
|
||||
ASSERT_VALID_UNDO_STEP(ustack, us_target);
|
||||
|
||||
/* This will be active once complete. */
|
||||
UndoStep *us_active = us_prev;
|
||||
UndoStep *us_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_active && us_active->skip) {
|
||||
us_active = us_active->prev;
|
||||
}
|
||||
}
|
||||
|
||||
if ((us != NULL) && (us_active != NULL)) {
|
||||
CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
|
||||
|
||||
/* Handle accumulate steps. */
|
||||
if (ustack->step_active) {
|
||||
UndoStep *us_iter = ustack->step_active;
|
||||
while (us_iter != us) {
|
||||
/* TODO:
|
||||
* - skip successive steps that store the same data, eg: memfile steps.
|
||||
* - or steps that include another steps data, eg: a memfile step includes text undo data.
|
||||
*/
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, false);
|
||||
|
||||
us_iter = us_iter->prev;
|
||||
}
|
||||
/* This will be the active step once the undo process is complete.
|
||||
*
|
||||
* In case we do skip 'skipped' steps, the final active step may be several steps backward from
|
||||
* the one passed as parameter. */
|
||||
UndoStep *us_target_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_target_active != NULL && us_target_active->skip) {
|
||||
us_target_active = us_target_active->prev;
|
||||
}
|
||||
|
||||
{
|
||||
UndoStep *us_iter = us_prev;
|
||||
do {
|
||||
const bool is_final = (us_iter == us_active);
|
||||
if (is_final == false) {
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"undo continue with skip %p '%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
} while ((us_active != us_iter) && (us_iter = us_iter->prev));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
if (us_target_active == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid final active target step");
|
||||
return false;
|
||||
}
|
||||
|
||||
CLOG_INFO(
|
||||
&LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name);
|
||||
|
||||
/* Undo steps until we reach original given target, if we do have a current active step.
|
||||
*
|
||||
* NOTE: Unlike with redo case, where we can expect current active step to fully reflect current
|
||||
* data status, in undo case we also do reload the active step.
|
||||
* FIXME: this feels weak, and should probably not be actually needed? Or should also be done in
|
||||
* redo case? */
|
||||
if (ustack->step_active != NULL) {
|
||||
for (UndoStep *us_iter = ustack->step_active; us_iter != us_target; us_iter = us_iter->prev) {
|
||||
BLI_assert(us_iter != NULL);
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, false);
|
||||
ustack->step_active = us_iter;
|
||||
}
|
||||
}
|
||||
|
||||
/* Undo target step, and all potential extra ones if some steps have to be 'skipped'. */
|
||||
for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->prev) {
|
||||
const bool is_final = (us_iter == us_target_active);
|
||||
|
||||
if (!is_final) {
|
||||
BLI_assert(us_iter->skip == true);
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"undo continue with skip addr=%p, name='%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
|
||||
if (is_final) {
|
||||
/* Undo process is finished and successful. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert(
|
||||
!"This should never be reached, either undo stack is corrupted, or code above is buggy");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true);
|
||||
|
@ -756,54 +817,79 @@ bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack,
|
|||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (us == NULL) {
|
||||
CLOG_ERROR(&LOG, "called with a NULL step");
|
||||
return false;
|
||||
}
|
||||
undosys_stack_validate(ustack, true);
|
||||
|
||||
UndoStep *us_next = us ? us->next : NULL;
|
||||
/* Unlike undo accumulate, we always use the next. */
|
||||
us = us_next;
|
||||
/* We expect to get previous-from-actual-target step here (i.e. active step in case we only redo
|
||||
* once)?
|
||||
* FIXME: this is very confusing now that we may have to redo several steps anyway, this function
|
||||
* should just get the target final step, not assume that it is getting the active one by default
|
||||
* (or the step before the target one when redoing more than one step). */
|
||||
UndoStep *us_target = us->next;
|
||||
if (us_target == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid target step");
|
||||
return false;
|
||||
}
|
||||
ASSERT_VALID_REDO_STEP(ustack, us_target);
|
||||
|
||||
/* This will be active once complete. */
|
||||
UndoStep *us_active = us_next;
|
||||
/* This will be the active step once the redo process is complete.
|
||||
*
|
||||
* In case we do skip 'skipped' steps, the final active step may be several steps forward the one
|
||||
* passed as parameter. */
|
||||
UndoStep *us_target_active = us_target;
|
||||
if (use_skip) {
|
||||
while (us_active && us_active->skip) {
|
||||
us_active = us_active->next;
|
||||
while (us_target_active != NULL && us_target_active->skip) {
|
||||
us_target_active = us_target_active->next;
|
||||
}
|
||||
}
|
||||
if (us_target_active == NULL) {
|
||||
CLOG_ERROR(&LOG, "could not find a valid final active target step");
|
||||
return false;
|
||||
}
|
||||
|
||||
CLOG_INFO(
|
||||
&LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name);
|
||||
|
||||
/* Redo steps until we reach original given target, if we do have a current active step. */
|
||||
if (ustack->step_active != NULL) {
|
||||
for (UndoStep *us_iter = ustack->step_active->next; us_iter != us_target;
|
||||
us_iter = us_iter->next) {
|
||||
BLI_assert(us_iter != NULL);
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, false);
|
||||
ustack->step_active = us_iter;
|
||||
}
|
||||
}
|
||||
|
||||
if ((us != NULL) && (us_active != NULL)) {
|
||||
CLOG_INFO(&LOG, 1, "addr=%p, name='%s', type='%s'", us, us->name, us->type->name);
|
||||
/* Redo target step, and all potential extra ones if some steps have to be 'skipped'. */
|
||||
for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->next) {
|
||||
const bool is_final = (us_iter == us_target_active);
|
||||
|
||||
/* Handle accumulate steps. */
|
||||
if (ustack->step_active && ustack->step_active->next) {
|
||||
UndoStep *us_iter = ustack->step_active->next;
|
||||
while (us_iter != us) {
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, false);
|
||||
us_iter = us_iter->next;
|
||||
}
|
||||
if (!is_final) {
|
||||
BLI_assert(us_iter->skip == true);
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"redo continue with skip addr=%p, name='%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
|
||||
{
|
||||
UndoStep *us_iter = us_next;
|
||||
do {
|
||||
const bool is_final = (us_iter == us_active);
|
||||
if (is_final == false) {
|
||||
CLOG_INFO(&LOG,
|
||||
2,
|
||||
"redo continue with skip %p '%s', type='%s'",
|
||||
us_iter,
|
||||
us_iter->name,
|
||||
us_iter->type->name);
|
||||
}
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
} while ((us_active != us_iter) && (us_iter = us_iter->next));
|
||||
undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, is_final);
|
||||
ustack->step_active = us_iter;
|
||||
|
||||
if (is_final) {
|
||||
/* Redo process is finished and successful. */
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BLI_assert(
|
||||
!"This should never be reached, either undo stack is corrupted, or code above is buggy");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true);
|
||||
|
|
|
@ -120,7 +120,8 @@ struct double2 {
|
|||
|
||||
static double distance_squared(const double2 &a, const double2 &b)
|
||||
{
|
||||
return double2::dot(a, b);
|
||||
double2 diff = a - b;
|
||||
return double2::dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
|
|
|
@ -218,7 +218,8 @@ struct double3 {
|
|||
|
||||
static double distance_squared(const double3 &a, const double3 &b)
|
||||
{
|
||||
return double3::dot(a, b);
|
||||
double3 diff = a - b;
|
||||
return double3::dot(diff, diff);
|
||||
}
|
||||
|
||||
static double3 interpolate(const double3 &a, const double3 &b, double t)
|
||||
|
|
|
@ -141,7 +141,8 @@ struct float2 {
|
|||
|
||||
static float distance_squared(const float2 &a, const float2 &b)
|
||||
{
|
||||
return float2::dot(a, b);
|
||||
float2 diff = a - b;
|
||||
return float2::dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
|
|
|
@ -236,7 +236,8 @@ struct float3 {
|
|||
|
||||
static float distance_squared(const float3 &a, const float3 &b)
|
||||
{
|
||||
return float3::dot(a, b);
|
||||
float3 diff = a - b;
|
||||
return float3::dot(diff, diff);
|
||||
}
|
||||
|
||||
static float3 interpolate(const float3 &a, const float3 &b, float t)
|
||||
|
|
|
@ -156,7 +156,8 @@ struct mpq2 {
|
|||
|
||||
static mpq_class distance_squared(const mpq2 &a, const mpq2 &b)
|
||||
{
|
||||
return dot(a, b);
|
||||
mpq2 diff = a - b;
|
||||
return dot(diff, diff);
|
||||
}
|
||||
|
||||
struct isect_result {
|
||||
|
|
|
@ -1508,15 +1508,15 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Editing *ed = SEQ_editing_get(scene, false);
|
||||
if (ed == NULL) {
|
||||
continue;
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
Editing *ed = SEQ_editing_get(scene, false);
|
||||
if (ed == NULL) {
|
||||
continue;
|
||||
}
|
||||
ed->cache_flag = (SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_FINAL_OUT);
|
||||
do_versions_strip_cache_settings_recursive(&ed->seqbase);
|
||||
}
|
||||
ed->cache_flag = (SEQ_CACHE_STORE_RAW | SEQ_CACHE_STORE_FINAL_OUT);
|
||||
do_versions_strip_cache_settings_recursive(&ed->seqbase);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,6 +71,8 @@ set(SRC
|
|||
intern/COM_MemoryBuffer.h
|
||||
intern/COM_MemoryProxy.cpp
|
||||
intern/COM_MemoryProxy.h
|
||||
intern/COM_MetaData.cpp
|
||||
intern/COM_MetaData.h
|
||||
intern/COM_Node.cpp
|
||||
intern/COM_Node.h
|
||||
intern/COM_NodeConverter.cpp
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
void MetaData::add(const blender::StringRef key, const blender::StringRef value)
|
||||
{
|
||||
entries_.add(key, value);
|
||||
}
|
||||
|
||||
void MetaData::addCryptomatteEntry(const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key,
|
||||
const blender::StringRef value)
|
||||
{
|
||||
add(blender::BKE_cryptomatte_meta_data_key(layer_name, key), value);
|
||||
}
|
||||
|
||||
/* Replace the hash neutral cryptomatte keys with hashed versions.
|
||||
*
|
||||
* When a conversion happens it will also add the cryptomatte name key with the given
|
||||
* `layer_name`.*/
|
||||
void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name)
|
||||
{
|
||||
std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, "");
|
||||
std::string cryptomatte_conversion = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_CONVERSION,
|
||||
"");
|
||||
std::string cryptomatte_manifest = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_MANIFEST, "");
|
||||
|
||||
if (cryptomatte_hash.length() || cryptomatte_conversion.length() ||
|
||||
cryptomatte_manifest.length()) {
|
||||
addCryptomatteEntry(layer_name, "name", layer_name);
|
||||
}
|
||||
if (cryptomatte_hash.length()) {
|
||||
addCryptomatteEntry(layer_name, "hash", cryptomatte_hash);
|
||||
}
|
||||
if (cryptomatte_conversion.length()) {
|
||||
addCryptomatteEntry(layer_name, "conversion", cryptomatte_conversion);
|
||||
}
|
||||
if (cryptomatte_manifest.length()) {
|
||||
addCryptomatteEntry(layer_name, "manifest", cryptomatte_manifest);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaData::addToRenderResult(RenderResult *render_result) const
|
||||
{
|
||||
for (blender::Map<std::string, std::string>::Item entry : entries_.items()) {
|
||||
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Copyright 2021, Blender Foundation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
/* Forward declarations. */
|
||||
struct StampData;
|
||||
struct RenderResult;
|
||||
|
||||
/* Cryptomatte includes hash in its meta data keys. The hash is generated from the render
|
||||
* layer/pass name. Compositing happens without the knowledge of the original layer and pass. The
|
||||
* next keys are used to transfer the cryptomatte meta data in a neutral way. The file output node
|
||||
* will generate a hash based on the layer name configured by the user.
|
||||
*
|
||||
* The `{hash}` has no special meaning except to make sure that the meta data stays unique. */
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_HASH("cryptomatte/{hash}/hash");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_CONVERSION("cryptomatte/{hash}/conversion");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_MANIFEST("cryptomatte/{hash}/manifest");
|
||||
constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/name");
|
||||
|
||||
class MetaData {
|
||||
private:
|
||||
blender::Map<std::string, std::string> entries_;
|
||||
void addCryptomatteEntry(const blender::StringRef layer_name,
|
||||
const blender::StringRefNull key,
|
||||
const blender::StringRef value);
|
||||
|
||||
public:
|
||||
void add(const blender::StringRef key, const blender::StringRef value);
|
||||
void replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name);
|
||||
void addToRenderResult(RenderResult *render_result) const;
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData")
|
||||
#endif
|
||||
};
|
|
@ -19,8 +19,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "BLI_rect.h"
|
||||
#include "COM_MetaData.h"
|
||||
#include "COM_defines.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
# include "MEM_guardedalloc.h"
|
||||
#endif
|
||||
|
@ -32,6 +36,7 @@ typedef enum PixelSampler {
|
|||
} PixelSampler;
|
||||
|
||||
class MemoryBuffer;
|
||||
|
||||
/**
|
||||
* \brief Helper class for reading socket data.
|
||||
* Only use this class for dispatching (un-ary and n-ary) executions.
|
||||
|
@ -134,6 +139,14 @@ class SocketReader {
|
|||
return this->m_height;
|
||||
}
|
||||
|
||||
/* Return the meta data associated with this branch.
|
||||
*
|
||||
* The return parameter holds an instance or is an nullptr. */
|
||||
virtual std::unique_ptr<MetaData> getMetaData() const
|
||||
{
|
||||
return std::unique_ptr<MetaData>();
|
||||
}
|
||||
|
||||
#ifdef WITH_CXX_GUARDEDALLOC
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("COM:SocketReader")
|
||||
#endif
|
||||
|
|
|
@ -50,7 +50,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
|
|||
OutputOpenExrMultiLayerOperation *outputOperation;
|
||||
|
||||
if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) {
|
||||
outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getRenderData(),
|
||||
outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getScene(),
|
||||
context.getRenderData(),
|
||||
context.getbNodeTree(),
|
||||
storage->base_path,
|
||||
storage->format.exr_codec,
|
||||
|
@ -58,7 +59,8 @@ void OutputFileNode::convertToOperations(NodeConverter &converter,
|
|||
context.getViewName());
|
||||
}
|
||||
else {
|
||||
outputOperation = new OutputOpenExrMultiLayerOperation(context.getRenderData(),
|
||||
outputOperation = new OutputOpenExrMultiLayerOperation(context.getScene(),
|
||||
context.getRenderData(),
|
||||
context.getbNodeTree(),
|
||||
storage->base_path,
|
||||
storage->format.exr_codec,
|
||||
|
|
|
@ -143,13 +143,14 @@ void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution()
|
|||
/************************************ OpenEXR Multilayer Multiview *******************************/
|
||||
|
||||
OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation(
|
||||
const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
bool exr_half_float,
|
||||
const char *viewName)
|
||||
: OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, exr_half_float, viewName)
|
||||
: OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, viewName)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -195,12 +196,16 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename
|
|||
BLI_make_existing_file(filename);
|
||||
|
||||
/* prepare the file with all the channels for the header */
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr) == 0) {
|
||||
StampData *stamp_data = createStampData();
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) ==
|
||||
0) {
|
||||
printf("Error Writing Multilayer Multiview Openexr\n");
|
||||
IMB_exr_close(exrhandle);
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
}
|
||||
else {
|
||||
IMB_exr_clear_channels(exrhandle);
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
return exrhandle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,8 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera
|
|||
class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation {
|
||||
private:
|
||||
public:
|
||||
OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerMultiViewOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
|
|
|
@ -18,12 +18,15 @@
|
|||
|
||||
#include "COM_OutputFileOperation.h"
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
|
@ -36,6 +39,8 @@
|
|||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
void add_exr_channels(void *exrhandle,
|
||||
const char *layerName,
|
||||
const DataType datatype,
|
||||
|
@ -299,13 +304,15 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo
|
|||
this->imageInput = nullptr;
|
||||
}
|
||||
|
||||
OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
bool exr_half_float,
|
||||
const char *viewName)
|
||||
{
|
||||
this->m_scene = scene;
|
||||
this->m_rd = rd;
|
||||
this->m_tree = tree;
|
||||
|
||||
|
@ -323,6 +330,26 @@ void OutputOpenExrMultiLayerOperation::add_layer(const char *name,
|
|||
this->m_layers.push_back(OutputOpenExrLayer(name, datatype, use_layer));
|
||||
}
|
||||
|
||||
StampData *OutputOpenExrMultiLayerOperation::createStampData() const
|
||||
{
|
||||
/* StampData API doesn't provide functions to modify an instance without having a RenderResult.
|
||||
*/
|
||||
RenderResult render_result;
|
||||
StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene);
|
||||
render_result.stamp_data = stamp_data;
|
||||
for (int i = 0; i < this->m_layers.size(); i++) {
|
||||
const OutputOpenExrLayer *layer = &this->m_layers[i];
|
||||
std::unique_ptr<MetaData> meta_data = layer->imageInput->getMetaData();
|
||||
if (meta_data) {
|
||||
blender::StringRef layer_name = blender::BKE_cryptomatte_extract_layer_name(
|
||||
blender::StringRef(layer->name, BLI_strnlen(layer->name, sizeof(layer->name))));
|
||||
meta_data->replaceHashNeutralCryptomatteKeys(layer_name);
|
||||
meta_data->addToRenderResult(&render_result);
|
||||
}
|
||||
}
|
||||
return stamp_data;
|
||||
}
|
||||
|
||||
void OutputOpenExrMultiLayerOperation::initExecution()
|
||||
{
|
||||
for (unsigned int i = 0; i < this->m_layers.size(); i++) {
|
||||
|
@ -386,7 +413,8 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
|
|||
}
|
||||
|
||||
/* when the filename has no permissions, this can fail */
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, nullptr)) {
|
||||
StampData *stamp_data = createStampData();
|
||||
if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) {
|
||||
IMB_exr_write_channels(exrhandle);
|
||||
}
|
||||
else {
|
||||
|
@ -404,5 +432,6 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
|
|||
|
||||
this->m_layers[i].imageInput = nullptr;
|
||||
}
|
||||
BKE_stamp_data_free(stamp_data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
|
|||
protected:
|
||||
typedef std::vector<OutputOpenExrLayer> LayerList;
|
||||
|
||||
const Scene *m_scene;
|
||||
const RenderData *m_rd;
|
||||
const bNodeTree *m_tree;
|
||||
|
||||
|
@ -100,8 +101,11 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
|
|||
LayerList m_layers;
|
||||
const char *m_viewName;
|
||||
|
||||
StampData *createStampData() const;
|
||||
|
||||
public:
|
||||
OutputOpenExrMultiLayerOperation(const RenderData *rd,
|
||||
OutputOpenExrMultiLayerOperation(const Scene *scene,
|
||||
const RenderData *rd,
|
||||
const bNodeTree *tree,
|
||||
const char *path,
|
||||
char exr_codec,
|
||||
|
|
|
@ -18,8 +18,16 @@
|
|||
|
||||
#include "COM_RenderLayersProg.h"
|
||||
|
||||
#include "COM_MetaData.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
@ -209,6 +217,82 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2],
|
|||
}
|
||||
}
|
||||
|
||||
struct CallbackData {
|
||||
std::unique_ptr<MetaData> meta_data;
|
||||
std::string hash_key;
|
||||
std::string conversion_key;
|
||||
std::string manifest_key;
|
||||
|
||||
void addMetaData(blender::StringRef key, blender::StringRefNull value)
|
||||
{
|
||||
if (!meta_data) {
|
||||
meta_data = std::make_unique<MetaData>();
|
||||
}
|
||||
meta_data->add(key, value);
|
||||
}
|
||||
|
||||
void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name)
|
||||
{
|
||||
manifest_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "manifest");
|
||||
hash_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "hash");
|
||||
conversion_key = blender::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "conversion");
|
||||
}
|
||||
};
|
||||
|
||||
/* C type callback function (StampCallback). */
|
||||
static void extract_cryptomatte_meta_data(void *_data,
|
||||
const char *propname,
|
||||
char *propvalue,
|
||||
int UNUSED(len))
|
||||
{
|
||||
CallbackData *data = static_cast<CallbackData *>(_data);
|
||||
blender::StringRefNull key(propname);
|
||||
if (key == data->hash_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue);
|
||||
}
|
||||
else if (key == data->conversion_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue);
|
||||
}
|
||||
else if (key == data->manifest_key) {
|
||||
data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<MetaData> RenderLayersProg::getMetaData() const
|
||||
{
|
||||
Scene *scene = this->getScene();
|
||||
Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr;
|
||||
RenderResult *rr = nullptr;
|
||||
CallbackData callback_data = {nullptr};
|
||||
|
||||
if (re) {
|
||||
rr = RE_AcquireResultRead(re);
|
||||
}
|
||||
|
||||
if (rr && rr->stamp_data) {
|
||||
ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId());
|
||||
if (view_layer) {
|
||||
std::string full_layer_name = std::string(
|
||||
view_layer->name,
|
||||
BLI_strnlen(view_layer->name, sizeof(view_layer->name))) +
|
||||
"." + m_passName;
|
||||
blender::StringRef cryptomatte_layer_name = blender::BKE_cryptomatte_extract_layer_name(
|
||||
full_layer_name);
|
||||
callback_data.setCryptomatteKeys(cryptomatte_layer_name);
|
||||
|
||||
BKE_stamp_info_callback(
|
||||
&callback_data, rr->stamp_data, extract_cryptomatte_meta_data, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (re) {
|
||||
RE_ReleaseResult(re);
|
||||
re = nullptr;
|
||||
}
|
||||
|
||||
return std::move(callback_data.meta_data);
|
||||
}
|
||||
|
||||
/* ******** Render Layers AO Operation ******** */
|
||||
void RenderLayersAOOperation::executePixelSampled(float output[4],
|
||||
float x,
|
||||
|
|
|
@ -94,7 +94,7 @@ class RenderLayersProg : public NodeOperation {
|
|||
{
|
||||
this->m_scene = scene;
|
||||
}
|
||||
Scene *getScene()
|
||||
Scene *getScene() const
|
||||
{
|
||||
return this->m_scene;
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class RenderLayersProg : public NodeOperation {
|
|||
{
|
||||
this->m_layerId = layerId;
|
||||
}
|
||||
short getLayerId()
|
||||
short getLayerId() const
|
||||
{
|
||||
return this->m_layerId;
|
||||
}
|
||||
|
@ -121,6 +121,8 @@ class RenderLayersProg : public NodeOperation {
|
|||
void initExecution();
|
||||
void deinitExecution();
|
||||
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
|
||||
|
||||
std::unique_ptr<MetaData> getMetaData() const override;
|
||||
};
|
||||
|
||||
class RenderLayersAOOperation : public RenderLayersProg {
|
||||
|
|
|
@ -24,3 +24,8 @@ SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion)
|
|||
this->addInputSocket(type);
|
||||
this->addOutputSocket(type);
|
||||
}
|
||||
|
||||
std::unique_ptr<MetaData> SocketProxyOperation::getMetaData() const
|
||||
{
|
||||
return this->getInputSocket(0)->getReader()->getMetaData();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ class SocketProxyOperation : public NodeOperation {
|
|||
{
|
||||
m_use_conversion = use_conversion;
|
||||
}
|
||||
std::unique_ptr<MetaData> getMetaData() const override;
|
||||
|
||||
private:
|
||||
bool m_use_conversion;
|
||||
|
|
|
@ -83,6 +83,7 @@
|
|||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_light.h"
|
||||
#include "BKE_mask.h"
|
||||
#include "BKE_material.h"
|
||||
|
@ -152,12 +153,14 @@ DepsgraphNodeBuilder::~DepsgraphNodeBuilder()
|
|||
|
||||
IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
|
||||
{
|
||||
BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET);
|
||||
|
||||
IDNode *id_node = nullptr;
|
||||
ID *id_cow = nullptr;
|
||||
IDComponentsMask previously_visible_components_mask = 0;
|
||||
uint32_t previous_eval_flags = 0;
|
||||
DEGCustomDataMeshMasks previous_customdata_masks;
|
||||
IDInfo *id_info = id_info_hash_.lookup_default(id, nullptr);
|
||||
IDInfo *id_info = id_info_hash_.lookup_default(id->session_uuid, nullptr);
|
||||
if (id_info != nullptr) {
|
||||
id_cow = id_info->id_cow;
|
||||
previously_visible_components_mask = id_info->previously_visible_components_mask;
|
||||
|
@ -334,7 +337,8 @@ void DepsgraphNodeBuilder::begin_build()
|
|||
id_info->previously_visible_components_mask = id_node->visible_components_mask;
|
||||
id_info->previous_eval_flags = id_node->eval_flags;
|
||||
id_info->previous_customdata_masks = id_node->customdata_masks;
|
||||
id_info_hash_.add_new(id_node->id_orig, id_info);
|
||||
BLI_assert(!id_info_hash_.contains(id_node->id_orig_session_uuid));
|
||||
id_info_hash_.add_new(id_node->id_orig_session_uuid, id_info);
|
||||
id_node->id_cow = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -285,8 +285,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
|
|||
* very root is visible (aka not restricted.). */
|
||||
bool is_parent_collection_visible_;
|
||||
|
||||
/* Indexed by original ID, values are IDInfo. */
|
||||
Map<const ID *, IDInfo *> id_info_hash_;
|
||||
/* Indexed by original ID.session_uuid, values are IDInfo. */
|
||||
Map<uint, IDInfo *> id_info_hash_;
|
||||
|
||||
/* Set of IDs which were already build. Makes it easier to keep track of
|
||||
* what was already built and what was not. */
|
||||
|
|
|
@ -80,6 +80,7 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
|
|||
/* Store ID-pointer. */
|
||||
id_type = GS(id->name);
|
||||
id_orig = (ID *)id;
|
||||
id_orig_session_uuid = id->session_uuid;
|
||||
eval_flags = 0;
|
||||
previous_eval_flags = 0;
|
||||
customdata_masks = DEGCustomDataMeshMasks();
|
||||
|
|
|
@ -74,12 +74,22 @@ struct IDNode : public Node {
|
|||
|
||||
IDComponentsMask get_visible_components_mask() const;
|
||||
|
||||
/* ID Block referenced. */
|
||||
/* Type of the ID stored separately, so it's possible to perform check whether CoW is needed
|
||||
* without de-referencing the id_cow (which is not safe when ID is NOT covered by CoW and has
|
||||
* been deleted from the main database.) */
|
||||
ID_Type id_type;
|
||||
|
||||
/* ID Block referenced. */
|
||||
ID *id_orig;
|
||||
|
||||
/* Session-wide UUID of the id_orig.
|
||||
* Is used on relations update to map evaluated state from old nodes to the new ones, without
|
||||
* relying on pointers (which are not guaranteed to be unique) and without dereferencing id_orig
|
||||
* which could be "stale" pointer. */
|
||||
uint id_orig_session_uuid;
|
||||
|
||||
/* Evaluated datablock.
|
||||
* Will be covered by the copy-on-write system if the ID Type needs it. */
|
||||
ID *id_cow;
|
||||
|
||||
/* Hash to make it faster to look up components. */
|
||||
|
|
|
@ -1907,7 +1907,7 @@ typedef struct PosNorLoop {
|
|||
|
||||
typedef struct MeshExtract_PosNor_Data {
|
||||
PosNorLoop *vbo_data;
|
||||
GPUPackedNormal packed_nor[];
|
||||
GPUNormal normals[];
|
||||
} MeshExtract_PosNor_Data;
|
||||
|
||||
static void *extract_pos_nor_init(const MeshRenderData *mr,
|
||||
|
@ -1926,7 +1926,7 @@ static void *extract_pos_nor_init(const MeshRenderData *mr,
|
|||
GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
|
||||
|
||||
/* Pack normals per vert, reduce amount of computation. */
|
||||
size_t packed_nor_len = sizeof(GPUPackedNormal) * mr->vert_len;
|
||||
size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
|
||||
MeshExtract_PosNor_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
|
||||
data->vbo_data = (PosNorLoop *)GPU_vertbuf_get_data(vbo);
|
||||
|
||||
|
@ -1936,13 +1936,13 @@ static void *extract_pos_nor_init(const MeshRenderData *mr,
|
|||
BMVert *eve;
|
||||
int v;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
|
||||
data->packed_nor[v] = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
|
||||
data->normals[v].low = GPU_normal_convert_i10_v3(bm_vert_no_get(mr, eve));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const MVert *mv = mr->mvert;
|
||||
for (int v = 0; v < mr->vert_len; v++, mv++) {
|
||||
data->packed_nor[v] = GPU_normal_convert_i10_s3(mv->no);
|
||||
data->normals[v].low = GPU_normal_convert_i10_s3(mv->no);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
|
@ -1957,7 +1957,7 @@ static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
|
|||
{
|
||||
PosNorLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
|
||||
vert->nor = data->packed_nor[BM_elem_index_get(l->v)];
|
||||
vert->nor = data->normals[BM_elem_index_get(l->v)].low;
|
||||
BMFace *efa = l->f;
|
||||
vert->nor.w = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
|
||||
}
|
||||
|
@ -1974,7 +1974,7 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
|
|||
PosNorLoop *vert = &data->vbo_data[ml_index];
|
||||
const MVert *mv = &mr->mvert[ml->v];
|
||||
copy_v3_v3(vert->pos, mv->co);
|
||||
vert->nor = data->packed_nor[ml->v];
|
||||
vert->nor = data->normals[ml->v].low;
|
||||
/* Flag for paint mode overlay. */
|
||||
if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
|
||||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
|
||||
|
@ -2002,8 +2002,8 @@ static void extract_pos_nor_iter_ledge_bm(const MeshRenderData *mr,
|
|||
PosNorLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
|
||||
copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
|
||||
vert[0].nor = data->packed_nor[BM_elem_index_get(eed->v1)];
|
||||
vert[1].nor = data->packed_nor[BM_elem_index_get(eed->v2)];
|
||||
vert[0].nor = data->normals[BM_elem_index_get(eed->v1)].low;
|
||||
vert[1].nor = data->normals[BM_elem_index_get(eed->v2)].low;
|
||||
}
|
||||
EXTRACT_LEDGE_FOREACH_BM_END;
|
||||
}
|
||||
|
@ -2019,8 +2019,8 @@ static void extract_pos_nor_iter_ledge_mesh(const MeshRenderData *mr,
|
|||
PosNorLoop *vert = &data->vbo_data[ml_index];
|
||||
copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
|
||||
copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
|
||||
vert[0].nor = data->packed_nor[med->v1];
|
||||
vert[1].nor = data->packed_nor[med->v2];
|
||||
vert[0].nor = data->normals[med->v1].low;
|
||||
vert[1].nor = data->normals[med->v2].low;
|
||||
}
|
||||
EXTRACT_LEDGE_FOREACH_MESH_END;
|
||||
}
|
||||
|
@ -2036,7 +2036,7 @@ static void extract_pos_nor_iter_lvert_bm(const MeshRenderData *mr,
|
|||
const int l_index = offset + lvert_index;
|
||||
PosNorLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
|
||||
vert->nor = data->packed_nor[BM_elem_index_get(eve)];
|
||||
vert->nor = data->normals[BM_elem_index_get(eve)].low;
|
||||
}
|
||||
EXTRACT_LVERT_FOREACH_BM_END;
|
||||
}
|
||||
|
@ -2053,7 +2053,7 @@ static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr,
|
|||
const int v_index = mr->lverts[lvert_index];
|
||||
PosNorLoop *vert = &data->vbo_data[ml_index];
|
||||
copy_v3_v3(vert->pos, mv->co);
|
||||
vert->nor = data->packed_nor[v_index];
|
||||
vert->nor = data->normals[v_index].low;
|
||||
}
|
||||
EXTRACT_LVERT_FOREACH_MESH_END;
|
||||
}
|
||||
|
@ -2080,6 +2080,198 @@ static const MeshExtract extract_pos_nor = {
|
|||
};
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Position and High Quality Vertex Normal
|
||||
* \{ */
|
||||
|
||||
typedef struct PosNorHQLoop {
|
||||
float pos[3];
|
||||
short nor[4];
|
||||
} PosNorHQLoop;
|
||||
|
||||
typedef struct MeshExtract_PosNorHQ_Data {
|
||||
PosNorHQLoop *vbo_data;
|
||||
GPUNormal normals[];
|
||||
} MeshExtract_PosNorHQ_Data;
|
||||
|
||||
static void *extract_pos_nor_hq_init(const MeshRenderData *mr,
|
||||
struct MeshBatchCache *UNUSED(cache),
|
||||
void *buf)
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
/* WARNING Adjust #PosNorHQLoop struct accordingly. */
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
|
||||
GPU_vertformat_alias_add(&format, "vnor");
|
||||
}
|
||||
GPUVertBuf *vbo = buf;
|
||||
GPU_vertbuf_init_with_format(vbo, &format);
|
||||
GPU_vertbuf_data_alloc(vbo, mr->loop_len + mr->loop_loose_len);
|
||||
|
||||
/* Pack normals per vert, reduce amount of computation. */
|
||||
size_t packed_nor_len = sizeof(GPUNormal) * mr->vert_len;
|
||||
MeshExtract_PosNorHQ_Data *data = MEM_mallocN(sizeof(*data) + packed_nor_len, __func__);
|
||||
data->vbo_data = (PosNorHQLoop *)GPU_vertbuf_get_data(vbo);
|
||||
|
||||
/* Quicker than doing it for each loop. */
|
||||
if (mr->extract_type == MR_EXTRACT_BMESH) {
|
||||
BMIter iter;
|
||||
BMVert *eve;
|
||||
int v;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, mr->bm, BM_VERTS_OF_MESH, v) {
|
||||
normal_float_to_short_v3(data->normals[v].high, bm_vert_no_get(mr, eve));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const MVert *mv = mr->mvert;
|
||||
for (int v = 0; v < mr->vert_len; v++, mv++) {
|
||||
copy_v3_v3_short(data->normals[v].high, mv->no);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_poly_bm(const MeshRenderData *mr,
|
||||
const ExtractPolyBMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr)
|
||||
{
|
||||
PosNorHQLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert->pos, bm_vert_co_get(mr, l->v));
|
||||
copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(l->v)].high);
|
||||
|
||||
BMFace *efa = l->f;
|
||||
vert->nor[3] = BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ? -1 : 0;
|
||||
}
|
||||
EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l);
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
|
||||
const ExtractPolyMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr)
|
||||
{
|
||||
PosNorHQLoop *vert = &data->vbo_data[ml_index];
|
||||
const MVert *mv = &mr->mvert[ml->v];
|
||||
copy_v3_v3(vert->pos, mv->co);
|
||||
copy_v3_v3_short(vert->nor, data->normals[ml->v].high);
|
||||
|
||||
/* Flag for paint mode overlay. */
|
||||
if (mp->flag & ME_HIDE || mv->flag & ME_HIDE ||
|
||||
((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) &&
|
||||
(mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
|
||||
vert->nor[3] = -1;
|
||||
}
|
||||
else if (mv->flag & SELECT) {
|
||||
vert->nor[3] = 1;
|
||||
}
|
||||
else {
|
||||
vert->nor[3] = 0;
|
||||
}
|
||||
}
|
||||
EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_ledge_bm(const MeshRenderData *mr,
|
||||
const ExtractLEdgeBMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
EXTRACT_LEDGE_FOREACH_BM_BEGIN(eed, ledge_index, params)
|
||||
{
|
||||
int l_index = mr->loop_len + ledge_index * 2;
|
||||
PosNorHQLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert[0].pos, bm_vert_co_get(mr, eed->v1));
|
||||
copy_v3_v3(vert[1].pos, bm_vert_co_get(mr, eed->v2));
|
||||
copy_v3_v3_short(vert[0].nor, data->normals[BM_elem_index_get(eed->v1)].high);
|
||||
vert[0].nor[3] = 0;
|
||||
copy_v3_v3_short(vert[1].nor, data->normals[BM_elem_index_get(eed->v2)].high);
|
||||
vert[1].nor[3] = 0;
|
||||
}
|
||||
EXTRACT_LEDGE_FOREACH_BM_END;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_ledge_mesh(const MeshRenderData *mr,
|
||||
const ExtractLEdgeMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
EXTRACT_LEDGE_FOREACH_MESH_BEGIN(med, ledge_index, params, mr)
|
||||
{
|
||||
const int ml_index = mr->loop_len + ledge_index * 2;
|
||||
PosNorHQLoop *vert = &data->vbo_data[ml_index];
|
||||
copy_v3_v3(vert[0].pos, mr->mvert[med->v1].co);
|
||||
copy_v3_v3(vert[1].pos, mr->mvert[med->v2].co);
|
||||
copy_v3_v3_short(vert[0].nor, data->normals[med->v1].high);
|
||||
vert[0].nor[3] = 0;
|
||||
copy_v3_v3_short(vert[1].nor, data->normals[med->v2].high);
|
||||
vert[1].nor[3] = 0;
|
||||
}
|
||||
EXTRACT_LEDGE_FOREACH_MESH_END;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_lvert_bm(const MeshRenderData *mr,
|
||||
const ExtractLVertBMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
const int offset = mr->loop_len + (mr->edge_loose_len * 2);
|
||||
EXTRACT_LVERT_FOREACH_BM_BEGIN(eve, lvert_index, params)
|
||||
{
|
||||
const int l_index = offset + lvert_index;
|
||||
PosNorHQLoop *vert = &data->vbo_data[l_index];
|
||||
copy_v3_v3(vert->pos, bm_vert_co_get(mr, eve));
|
||||
copy_v3_v3_short(vert->nor, data->normals[BM_elem_index_get(eve)].high);
|
||||
vert->nor[3] = 0;
|
||||
}
|
||||
EXTRACT_LVERT_FOREACH_BM_END;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_iter_lvert_mesh(const MeshRenderData *mr,
|
||||
const ExtractLVertMesh_Params *params,
|
||||
void *_data)
|
||||
{
|
||||
MeshExtract_PosNorHQ_Data *data = _data;
|
||||
const int offset = mr->loop_len + (mr->edge_loose_len * 2);
|
||||
EXTRACT_LVERT_FOREACH_MESH_BEGIN(mv, lvert_index, params, mr)
|
||||
{
|
||||
const int ml_index = offset + lvert_index;
|
||||
const int v_index = mr->lverts[lvert_index];
|
||||
PosNorHQLoop *vert = &data->vbo_data[ml_index];
|
||||
copy_v3_v3(vert->pos, mv->co);
|
||||
copy_v3_v3_short(vert->nor, data->normals[v_index].high);
|
||||
vert->nor[3] = 0;
|
||||
}
|
||||
EXTRACT_LVERT_FOREACH_MESH_END;
|
||||
}
|
||||
|
||||
static void extract_pos_nor_hq_finish(const MeshRenderData *UNUSED(mr),
|
||||
struct MeshBatchCache *UNUSED(cache),
|
||||
void *UNUSED(vbo),
|
||||
void *data)
|
||||
{
|
||||
MEM_freeN(data);
|
||||
}
|
||||
|
||||
static const MeshExtract extract_pos_nor_hq = {
|
||||
.init = extract_pos_nor_hq_init,
|
||||
.iter_poly_bm = extract_pos_nor_hq_iter_poly_bm,
|
||||
.iter_poly_mesh = extract_pos_nor_hq_iter_poly_mesh,
|
||||
.iter_ledge_bm = extract_pos_nor_hq_iter_ledge_bm,
|
||||
.iter_ledge_mesh = extract_pos_nor_hq_iter_ledge_mesh,
|
||||
.iter_lvert_bm = extract_pos_nor_hq_iter_lvert_bm,
|
||||
.iter_lvert_mesh = extract_pos_nor_hq_iter_lvert_mesh,
|
||||
.finish = extract_pos_nor_hq_finish,
|
||||
.data_flag = 0,
|
||||
.use_threading = true,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract HQ Loop Normal
|
||||
* \{ */
|
||||
|
@ -4747,7 +4939,83 @@ static const MeshExtract extract_fdots_nor = {
|
|||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Facedots Normal and edit flag
|
||||
/** \name Extract Facedots High Quality Normal and edit flag
|
||||
* \{ */
|
||||
static void *extract_fdots_nor_hq_init(const MeshRenderData *mr,
|
||||
struct MeshBatchCache *UNUSED(cache),
|
||||
void *buf)
|
||||
{
|
||||
static GPUVertFormat format = {0};
|
||||
if (format.attr_len == 0) {
|
||||
GPU_vertformat_attr_add(&format, "norAndFlag", GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
|
||||
}
|
||||
GPUVertBuf *vbo = buf;
|
||||
GPU_vertbuf_init_with_format(vbo, &format);
|
||||
GPU_vertbuf_data_alloc(vbo, mr->poly_len);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
|
||||
struct MeshBatchCache *UNUSED(cache),
|
||||
void *buf,
|
||||
void *UNUSED(data))
|
||||
{
|
||||
static float invalid_normal[3] = {0.0f, 0.0f, 0.0f};
|
||||
GPUVertBuf *vbo = buf;
|
||||
short *nor = (short *)GPU_vertbuf_get_data(vbo);
|
||||
BMFace *efa;
|
||||
|
||||
/* Quicker than doing it for each loop. */
|
||||
if (mr->extract_type == MR_EXTRACT_BMESH) {
|
||||
for (int f = 0; f < mr->poly_len; f++) {
|
||||
efa = BM_face_at_index(mr->bm, f);
|
||||
const bool is_face_hidden = BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
|
||||
if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
|
||||
mr->p_origindex[f] == ORIGINDEX_NONE)) {
|
||||
normal_float_to_short_v3(&nor[f * 4], invalid_normal);
|
||||
nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
|
||||
}
|
||||
else {
|
||||
normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
|
||||
/* Select / Active Flag. */
|
||||
nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
|
||||
((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
|
||||
NOR_AND_FLAG_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int f = 0; f < mr->poly_len; f++) {
|
||||
efa = bm_original_face_get(mr, f);
|
||||
const bool is_face_hidden = efa && BM_elem_flag_test(efa, BM_ELEM_HIDDEN);
|
||||
if (is_face_hidden || (mr->extract_type == MR_EXTRACT_MAPPED && mr->p_origindex &&
|
||||
mr->p_origindex[f] == ORIGINDEX_NONE)) {
|
||||
normal_float_to_short_v3(&nor[f * 4], invalid_normal);
|
||||
nor[f * 4 + 3] = NOR_AND_FLAG_HIDDEN;
|
||||
}
|
||||
else {
|
||||
normal_float_to_short_v3(&nor[f * 4], bm_face_no_get(mr, efa));
|
||||
/* Select / Active Flag. */
|
||||
nor[f * 4 + 3] = (BM_elem_flag_test(efa, BM_ELEM_SELECT) ?
|
||||
((efa == mr->efa_act) ? NOR_AND_FLAG_ACTIVE : NOR_AND_FLAG_SELECT) :
|
||||
NOR_AND_FLAG_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const MeshExtract extract_fdots_nor_hq = {
|
||||
.init = extract_fdots_nor_hq_init,
|
||||
.finish = extract_fdots_nor_hq_finish,
|
||||
.data_flag = MR_DATA_POLY_NOR,
|
||||
.use_threading = false,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/** \name Extract Facedots UV
|
||||
* \{ */
|
||||
|
||||
typedef struct MeshExtract_FdotUV_Data {
|
||||
|
@ -5580,11 +5848,19 @@ static void extract_task_create(struct TaskGraph *task_graph,
|
|||
BLI_assert(scene != NULL);
|
||||
const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
|
||||
GPU_use_hq_normals_workaround();
|
||||
if (do_hq_normals && (extract == &extract_lnor)) {
|
||||
extract = &extract_lnor_hq;
|
||||
}
|
||||
if (do_hq_normals && (extract == &extract_tan)) {
|
||||
extract = &extract_tan_hq;
|
||||
if (do_hq_normals) {
|
||||
if (extract == &extract_lnor) {
|
||||
extract = &extract_lnor_hq;
|
||||
}
|
||||
else if (extract == &extract_pos_nor) {
|
||||
extract = &extract_pos_nor_hq;
|
||||
}
|
||||
else if (extract == &extract_tan) {
|
||||
extract = &extract_tan_hq;
|
||||
}
|
||||
else if (extract == &extract_fdots_nor) {
|
||||
extract = &extract_fdots_nor_hq;
|
||||
}
|
||||
}
|
||||
|
||||
/* Divide extraction of the VBO/IBO into sensible chunks of works. */
|
||||
|
|
|
@ -892,7 +892,7 @@ static void acf_group_name(bAnimListElem *ale, char *name)
|
|||
/* name property for group entries */
|
||||
static bool acf_group_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop)
|
||||
{
|
||||
RNA_pointer_create(ale->id, &RNA_ActionGroup, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_ActionGroup, ale->data, ptr);
|
||||
*prop = RNA_struct_name_property(ptr->type);
|
||||
|
||||
return (*prop != NULL);
|
||||
|
@ -1013,7 +1013,7 @@ static bool acf_fcurve_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRN
|
|||
* as our "name" so that user can perform quick fixes
|
||||
*/
|
||||
if (fcu->flag & FCURVE_DISABLED) {
|
||||
RNA_pointer_create(ale->id, &RNA_FCurve, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_FCurve, ale->data, ptr);
|
||||
*prop = RNA_struct_find_property(ptr, "data_path");
|
||||
}
|
||||
else {
|
||||
|
@ -3965,7 +3965,7 @@ static void acf_nlaaction_name(bAnimListElem *ale, char *name)
|
|||
static bool acf_nlaaction_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop)
|
||||
{
|
||||
if (ale->data) {
|
||||
RNA_pointer_create(ale->id, &RNA_Action, ale->data, ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_Action, ale->data, ptr);
|
||||
*prop = RNA_struct_name_property(ptr->type);
|
||||
|
||||
return (*prop != NULL);
|
||||
|
|
|
@ -5698,7 +5698,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
/* First test: curve? */
|
||||
if (obedit->type != OB_CURVE) {
|
||||
LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
|
||||
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) == 1)) {
|
||||
if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) {
|
||||
as_curve = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -645,6 +645,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
|
|||
brush.sculpt.cloth
|
||||
brush.sculpt.crease
|
||||
brush.sculpt.displacement_eraser
|
||||
brush.sculpt.displacement_smear
|
||||
brush.sculpt.draw
|
||||
brush.sculpt.draw_face_sets
|
||||
brush.sculpt.draw_sharp
|
||||
|
|
|
@ -148,10 +148,18 @@ static bool object_materials_supported_poll_ex(bContext *C, const Object *ob)
|
|||
if (!ED_operator_object_active_local_editable_ex(C, ob)) {
|
||||
return false;
|
||||
}
|
||||
if (!OB_TYPE_SUPPORT_MATERIAL(ob->type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Material linked to object. */
|
||||
if (ob->matbits && ob->actcol && ob->matbits[ob->actcol - 1]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Material linked to obdata. */
|
||||
const ID *data = ob->data;
|
||||
return (OB_TYPE_SUPPORT_MATERIAL(ob->type) &&
|
||||
/* Object data checks. */
|
||||
data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
|
||||
return (data && !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data));
|
||||
}
|
||||
|
||||
static bool object_materials_supported_poll(bContext *C)
|
||||
|
|
|
@ -415,7 +415,12 @@ static float wpaint_undo_lock_relative(
|
|||
/* In auto-normalize mode, or when there is no unlocked weight,
|
||||
* compute based on locked weight. */
|
||||
if (auto_normalize || free_weight <= 0.0f) {
|
||||
weight *= (1.0f - locked_weight);
|
||||
if (locked_weight < 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
weight *= (1.0f - locked_weight);
|
||||
}
|
||||
else {
|
||||
weight = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* When dealing with full unlocked weight, don't paint, as it is always displayed as 1. */
|
||||
|
@ -518,7 +523,7 @@ static bool do_weight_paint_normalize_all_locked(MDeformVert *dvert,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (lock_weight >= 1.0f) {
|
||||
if (lock_weight >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
/* locked groups make it impossible to fully normalize,
|
||||
* zero out what we can and return false */
|
||||
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
|
||||
|
@ -779,7 +784,25 @@ static void do_weight_paint_vertex_single(
|
|||
index_mirr = vgroup_mirr = -1;
|
||||
}
|
||||
|
||||
if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
|
||||
/* Check if painting should create new deform weight entries. */
|
||||
bool restrict_to_existing = (wp->flag & VP_FLAG_VGROUP_RESTRICT) != 0;
|
||||
|
||||
if (wpi->do_lock_relative || wpi->do_auto_normalize) {
|
||||
/* Without do_lock_relative only dw_rel_locked is reliable, while dw_rel_free may be fake 0. */
|
||||
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
|
||||
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
|
||||
CLAMP(dw_rel_locked, 0.0f, 1.0f);
|
||||
|
||||
/* Do not create entries if there is not enough free weight to paint.
|
||||
* This logic is the same as in wpaint_undo_lock_relative and auto-normalize. */
|
||||
if (wpi->do_auto_normalize || dw_rel_free <= 0.0f) {
|
||||
if (dw_rel_locked >= 1.0f - VERTEX_WEIGHT_LOCK_EPSILON) {
|
||||
restrict_to_existing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (restrict_to_existing) {
|
||||
dw = BKE_defvert_find_index(dv, wpi->active.index);
|
||||
}
|
||||
else {
|
||||
|
@ -827,10 +850,6 @@ static void do_weight_paint_vertex_single(
|
|||
|
||||
/* Handle weight caught up in locked defgroups for Lock Relative. */
|
||||
if (wpi->do_lock_relative) {
|
||||
dw_rel_free = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_unlocked);
|
||||
dw_rel_locked = BKE_defvert_total_selected_weight(dv, wpi->defbase_tot, wpi->vgroup_locked);
|
||||
CLAMP(dw_rel_locked, 0.0f, 1.0f);
|
||||
|
||||
weight_cur = BKE_defvert_calc_lock_relative_weight(weight_cur, dw_rel_locked, dw_rel_free);
|
||||
}
|
||||
|
||||
|
@ -1658,6 +1677,10 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
|||
wpd->lock_flags, wpd->vgroup_validmap, wpd->active.index) &&
|
||||
(!wpd->do_multipaint || BKE_object_defgroup_check_lock_relative_multi(
|
||||
defbase_tot, wpd->lock_flags, defbase_sel, defbase_tot_sel))) {
|
||||
wpd->do_lock_relative = true;
|
||||
}
|
||||
|
||||
if (wpd->do_lock_relative || (ts->auto_normalize && wpd->lock_flags && !wpd->do_multipaint)) {
|
||||
bool *unlocked = MEM_dupallocN(wpd->vgroup_validmap);
|
||||
|
||||
if (wpd->lock_flags) {
|
||||
|
@ -1668,7 +1691,6 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo
|
|||
}
|
||||
|
||||
wpd->vgroup_unlocked = unlocked;
|
||||
wpd->do_lock_relative = true;
|
||||
}
|
||||
|
||||
if (wpd->do_multipaint && ts->auto_normalize) {
|
||||
|
@ -2383,7 +2405,8 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
|
|||
wpi.vgroup_unlocked = wpd->vgroup_unlocked;
|
||||
wpi.do_flip = RNA_boolean_get(itemptr, "pen_flip");
|
||||
wpi.do_multipaint = wpd->do_multipaint;
|
||||
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL));
|
||||
wpi.do_auto_normalize = ((ts->auto_normalize != 0) && (wpi.vgroup_validmap != NULL) &&
|
||||
(wpi.do_multipaint || wpi.vgroup_validmap[wpi.active.index]));
|
||||
wpi.do_lock_relative = wpd->do_lock_relative;
|
||||
wpi.is_normalized = wpi.do_auto_normalize || wpi.do_lock_relative;
|
||||
wpi.brush_alpha_value = brush_alpha_value;
|
||||
|
|
|
@ -145,7 +145,7 @@ static void solve_camera_freejob(void *scv)
|
|||
BKE_reportf(scj->reports,
|
||||
RPT_INFO,
|
||||
"Average re-projection error: %.2f px",
|
||||
tracking->reconstruction.error);
|
||||
BKE_tracking_get_active_reconstruction(tracking)->error);
|
||||
}
|
||||
|
||||
/* Set currently solved clip as active for scene. */
|
||||
|
|
|
@ -181,7 +181,7 @@ static void graph_panel_properties(const bContext *C, Panel *panel)
|
|||
}
|
||||
|
||||
/* F-Curve pointer */
|
||||
RNA_pointer_create(ale->id, &RNA_FCurve, fcu, &fcu_ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_FCurve, fcu, &fcu_ptr);
|
||||
|
||||
/* user-friendly 'name' for F-Curve */
|
||||
col = uiLayoutColumn(layout, false);
|
||||
|
@ -366,7 +366,7 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel)
|
|||
int unit = B_UNIT_NONE;
|
||||
|
||||
/* RNA pointer to keyframe, to allow editing */
|
||||
RNA_pointer_create(ale->id, &RNA_Keyframe, bezt, &bezt_ptr);
|
||||
RNA_pointer_create(ale->fcurve_owner_id, &RNA_Keyframe, bezt, &bezt_ptr);
|
||||
|
||||
/* get property that F-Curve affects, for some unit-conversion magic */
|
||||
RNA_id_pointer_create(ale->id, &id_ptr);
|
||||
|
|
|
@ -3228,6 +3228,35 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
|
|||
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_buts_attribute_vector_math(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
bNode *node = (bNode *)ptr->data;
|
||||
NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
|
||||
|
||||
uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
|
||||
uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
|
||||
|
||||
/* These "use input b / c" checks are copied from the node's code. They could be deduplicated if
|
||||
* the drawing code was moved to the node's file. */
|
||||
if (!ELEM(node_storage->operation,
|
||||
NODE_VECTOR_MATH_NORMALIZE,
|
||||
NODE_VECTOR_MATH_FLOOR,
|
||||
NODE_VECTOR_MATH_CEIL,
|
||||
NODE_VECTOR_MATH_FRACTION,
|
||||
NODE_VECTOR_MATH_ABSOLUTE,
|
||||
NODE_VECTOR_MATH_SINE,
|
||||
NODE_VECTOR_MATH_COSINE,
|
||||
NODE_VECTOR_MATH_TANGENT,
|
||||
NODE_VECTOR_MATH_LENGTH)) {
|
||||
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
|
||||
}
|
||||
if (ELEM(node_storage->operation, NODE_VECTOR_MATH_WRAP)) {
|
||||
uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void node_geometry_buts_point_instance(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
|
@ -3290,6 +3319,16 @@ static void node_geometry_buts_rotate_points(uiLayout *layout,
|
|||
}
|
||||
}
|
||||
|
||||
static void node_geometry_buts_align_rotation_to_vector(uiLayout *layout,
|
||||
bContext *UNUSED(C),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
uiItemR(layout, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE);
|
||||
uiItemR(col, ptr, "input_type_vector", DEFAULT_FLAGS, IFACE_("Vector"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void node_geometry_set_butfunc(bNodeType *ntype)
|
||||
{
|
||||
switch (ntype->type) {
|
||||
|
@ -3320,6 +3359,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
|||
case GEO_NODE_ATTRIBUTE_MIX:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_mix;
|
||||
break;
|
||||
case GEO_NODE_ATTRIBUTE_VECTOR_MATH:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_vector_math;
|
||||
break;
|
||||
case GEO_NODE_POINT_DISTRIBUTE:
|
||||
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
|
||||
break;
|
||||
|
@ -3329,6 +3371,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
|
|||
case GEO_NODE_ROTATE_POINTS:
|
||||
ntype->draw_buttons = node_geometry_buts_rotate_points;
|
||||
break;
|
||||
case GEO_NODE_ALIGN_ROTATION_TO_VECTOR:
|
||||
ntype->draw_buttons = node_geometry_buts_align_rotation_to_vector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3665,37 +3710,35 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
|
|||
{
|
||||
bNodeSocket *sock = ptr->data;
|
||||
int type = sock->typeinfo->type;
|
||||
/*int subtype = sock->typeinfo->subtype;*/
|
||||
|
||||
uiLayout *col = uiLayoutColumn(layout, false);
|
||||
|
||||
switch (type) {
|
||||
case SOCK_FLOAT: {
|
||||
uiLayout *row;
|
||||
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0);
|
||||
row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0);
|
||||
uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0);
|
||||
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
|
||||
uiLayout *sub = uiLayoutColumn(col, true);
|
||||
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
|
||||
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
|
||||
break;
|
||||
}
|
||||
case SOCK_INT: {
|
||||
uiLayout *row;
|
||||
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0);
|
||||
row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0);
|
||||
uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0);
|
||||
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE);
|
||||
uiLayout *sub = uiLayoutColumn(col, true);
|
||||
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
|
||||
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
uiLayout *row;
|
||||
uiItemR(layout, ptr, "default_value", UI_ITEM_R_EXPAND, NULL, DEFAULT_FLAGS);
|
||||
row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0);
|
||||
uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0);
|
||||
uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE);
|
||||
uiLayout *sub = uiLayoutColumn(col, true);
|
||||
uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE);
|
||||
uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE);
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN:
|
||||
case SOCK_RGBA:
|
||||
case SOCK_STRING: {
|
||||
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0);
|
||||
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,59 +93,35 @@ static bool node_tree_interface_poll(const bContext *C, PanelType *UNUSED(pt))
|
|||
(snode->edittree->inputs.first || snode->edittree->outputs.first));
|
||||
}
|
||||
|
||||
static bool node_tree_find_active_socket(bNodeTree *ntree,
|
||||
bNodeSocket **r_sock,
|
||||
eNodeSocketInOut *r_in_out)
|
||||
static bNodeSocket *node_tree_find_active_socket(bNodeTree *ntree, const eNodeSocketInOut in_out)
|
||||
{
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
|
||||
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
|
||||
if (socket->flag & SELECT) {
|
||||
*r_sock = socket;
|
||||
*r_in_out = SOCK_IN;
|
||||
return true;
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
|
||||
if (socket->flag & SELECT) {
|
||||
*r_sock = socket;
|
||||
*r_in_out = SOCK_OUT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*r_sock = NULL;
|
||||
*r_in_out = 0;
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void node_tree_interface_panel(const bContext *C, Panel *panel)
|
||||
static void draw_socket_list(const bContext *C,
|
||||
uiLayout *layout,
|
||||
bNodeTree *ntree,
|
||||
const eNodeSocketInOut in_out)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C); /* NULL checked in poll function. */
|
||||
bNodeTree *ntree = snode->edittree; /* NULL checked in poll function. */
|
||||
uiLayout *layout = panel->layout;
|
||||
PointerRNA tree_ptr;
|
||||
RNA_id_pointer_create((ID *)ntree, &tree_ptr);
|
||||
|
||||
PointerRNA ptr;
|
||||
RNA_id_pointer_create((ID *)ntree, &ptr);
|
||||
|
||||
bNodeSocket *socket;
|
||||
eNodeSocketInOut in_out;
|
||||
node_tree_find_active_socket(ntree, &socket, &in_out);
|
||||
PointerRNA sockptr;
|
||||
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, socket, &sockptr);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, false);
|
||||
|
||||
uiLayout *split = uiLayoutRow(row, true);
|
||||
uiLayout *col = uiLayoutColumn(split, true);
|
||||
wmOperatorType *ot = WM_operatortype_find("NODE_OT_tree_socket_add", false);
|
||||
uiItemL(col, IFACE_("Inputs:"), ICON_NONE);
|
||||
uiTemplateList(col,
|
||||
uiLayout *split = uiLayoutRow(layout, false);
|
||||
uiLayout *list_col = uiLayoutColumn(split, true);
|
||||
uiTemplateList(list_col,
|
||||
(bContext *)C,
|
||||
"NODE_UL_interface_sockets",
|
||||
"inputs",
|
||||
&ptr,
|
||||
"inputs",
|
||||
&ptr,
|
||||
"active_input",
|
||||
(in_out == SOCK_IN) ? "inputs" : "outputs",
|
||||
&tree_ptr,
|
||||
(in_out == SOCK_IN) ? "inputs" : "outputs",
|
||||
&tree_ptr,
|
||||
(in_out == SOCK_IN) ? "active_input" : "active_output",
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
|
@ -154,70 +130,90 @@ static void node_tree_interface_panel(const bContext *C, Panel *panel)
|
|||
false,
|
||||
false);
|
||||
PointerRNA opptr;
|
||||
uiItemFullO_ptr(col, ot, "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "in_out", SOCK_IN);
|
||||
uiLayout *ops_col = uiLayoutColumn(split, false);
|
||||
uiLayout *add_remove_col = uiLayoutColumn(ops_col, true);
|
||||
wmOperatorType *ot = WM_operatortype_find("NODE_OT_tree_socket_add", false);
|
||||
uiItemFullO_ptr(add_remove_col, ot, "", ICON_ADD, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "in_out", in_out);
|
||||
ot = WM_operatortype_find("NODE_OT_tree_socket_remove", false);
|
||||
uiItemFullO_ptr(add_remove_col, ot, "", ICON_REMOVE, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "in_out", in_out);
|
||||
|
||||
col = uiLayoutColumn(split, true);
|
||||
uiItemL(col, IFACE_("Outputs:"), ICON_NONE);
|
||||
uiTemplateList(col,
|
||||
(bContext *)C,
|
||||
"NODE_UL_interface_sockets",
|
||||
"outputs",
|
||||
&ptr,
|
||||
"outputs",
|
||||
&ptr,
|
||||
"active_output",
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false);
|
||||
uiItemFullO_ptr(col, ot, "", ICON_PLUS, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "in_out", SOCK_OUT);
|
||||
uiItemS(ops_col);
|
||||
|
||||
uiLayout *up_down_col = uiLayoutColumn(ops_col, true);
|
||||
ot = WM_operatortype_find("NODE_OT_tree_socket_move", false);
|
||||
col = uiLayoutColumn(row, true);
|
||||
uiItemFullO_ptr(col, ot, "", ICON_TRIA_UP, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
uiItemFullO_ptr(up_down_col, ot, "", ICON_TRIA_UP, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "direction", 1);
|
||||
uiItemFullO_ptr(col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "in_out", in_out);
|
||||
uiItemFullO_ptr(up_down_col, ot, "", ICON_TRIA_DOWN, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
|
||||
RNA_enum_set(&opptr, "direction", 2);
|
||||
RNA_enum_set(&opptr, "in_out", in_out);
|
||||
|
||||
if (socket) {
|
||||
row = uiLayoutRow(layout, true);
|
||||
uiItemR(row, &sockptr, "name", 0, NULL, ICON_NONE);
|
||||
uiItemO(row, "", ICON_X, "NODE_OT_tree_socket_remove");
|
||||
bNodeSocket *socket = node_tree_find_active_socket(ntree, in_out);
|
||||
if (socket != NULL) {
|
||||
uiLayoutSetPropSep(layout, true);
|
||||
uiLayoutSetPropDecorate(layout, false);
|
||||
PointerRNA socket_ptr;
|
||||
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, socket, &socket_ptr);
|
||||
uiItemR(layout, &socket_ptr, "name", 0, NULL, ICON_NONE);
|
||||
|
||||
if (socket->typeinfo->interface_draw) {
|
||||
uiItemS(layout);
|
||||
socket->typeinfo->interface_draw((bContext *)C, layout, &sockptr);
|
||||
socket->typeinfo->interface_draw((bContext *)C, layout, &socket_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void node_tree_interface_inputs_panel(const bContext *C, Panel *panel)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C); /* NULL checked in poll function. */
|
||||
bNodeTree *ntree = snode->edittree; /* NULL checked in poll function. */
|
||||
|
||||
draw_socket_list(C, panel->layout, ntree, SOCK_IN);
|
||||
}
|
||||
|
||||
static void node_tree_interface_outputs_panel(const bContext *C, Panel *panel)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C); /* NULL checked in poll function. */
|
||||
bNodeTree *ntree = snode->edittree; /* NULL checked in poll function. */
|
||||
|
||||
draw_socket_list(C, panel->layout, ntree, SOCK_OUT);
|
||||
}
|
||||
|
||||
/* ******************* node buttons registration ************** */
|
||||
|
||||
void node_buttons_register(ARegionType *art)
|
||||
{
|
||||
PanelType *pt;
|
||||
{
|
||||
PanelType *pt = MEM_callocN(sizeof(PanelType), __func__);
|
||||
strcpy(pt->idname, "NODE_PT_sockets");
|
||||
strcpy(pt->category, N_("Node"));
|
||||
strcpy(pt->label, N_("Sockets"));
|
||||
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->draw = node_sockets_panel;
|
||||
pt->poll = node_sockets_poll;
|
||||
pt->flag |= PANEL_TYPE_DEFAULT_CLOSED;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
}
|
||||
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype node panel node sockets");
|
||||
strcpy(pt->idname, "NODE_PT_sockets");
|
||||
strcpy(pt->category, N_("Node"));
|
||||
strcpy(pt->label, N_("Sockets"));
|
||||
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->draw = node_sockets_panel;
|
||||
pt->poll = node_sockets_poll;
|
||||
pt->flag |= PANEL_TYPE_DEFAULT_CLOSED;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
|
||||
pt = MEM_callocN(sizeof(PanelType), "spacetype node panel tree interface");
|
||||
strcpy(pt->idname, "NODE_PT_node_tree_interface");
|
||||
strcpy(pt->category, N_("Node"));
|
||||
strcpy(pt->label, N_("Interface"));
|
||||
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->draw = node_tree_interface_panel;
|
||||
pt->poll = node_tree_interface_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
{
|
||||
PanelType *pt = MEM_callocN(sizeof(PanelType), __func__);
|
||||
strcpy(pt->idname, "NODE_PT_node_tree_interface_inputs");
|
||||
strcpy(pt->category, N_("Node"));
|
||||
strcpy(pt->label, N_("Inputs"));
|
||||
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->draw = node_tree_interface_inputs_panel;
|
||||
pt->poll = node_tree_interface_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
}
|
||||
{
|
||||
PanelType *pt = MEM_callocN(sizeof(PanelType), __func__);
|
||||
strcpy(pt->idname, "NODE_PT_node_tree_interface_outputs");
|
||||
strcpy(pt->category, N_("Node"));
|
||||
strcpy(pt->label, N_("Outputs"));
|
||||
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->draw = node_tree_interface_outputs_panel;
|
||||
pt->poll = node_tree_interface_poll;
|
||||
BLI_addtail(&art->paneltypes, pt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2227,21 +2227,15 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
int in_out = RNA_enum_get(op->ptr, "in_out");
|
||||
|
||||
PointerRNA ntree_ptr;
|
||||
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
|
||||
|
||||
const char *default_name;
|
||||
bNodeSocket *active_sock;
|
||||
if (in_out == SOCK_IN) {
|
||||
active_sock = ntree_get_active_interface_socket(&ntree->inputs);
|
||||
default_name = "Input";
|
||||
}
|
||||
else {
|
||||
active_sock = ntree_get_active_interface_socket(&ntree->outputs);
|
||||
default_name = "Output";
|
||||
}
|
||||
const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
|
||||
ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs;
|
||||
|
||||
const char *default_name = (in_out == SOCK_IN) ? "Input" : "Output";
|
||||
bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets);
|
||||
|
||||
bNodeSocket *sock;
|
||||
if (active_sock) {
|
||||
|
@ -2256,11 +2250,8 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
|
|||
sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name);
|
||||
}
|
||||
|
||||
/* deactivate sockets (has to check both lists) */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->inputs) {
|
||||
socket_iter->flag &= ~SELECT;
|
||||
}
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->outputs) {
|
||||
/* Deactivate sockets. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) {
|
||||
socket_iter->flag &= ~SELECT;
|
||||
}
|
||||
/* make the new socket active */
|
||||
|
@ -2295,16 +2286,15 @@ void NODE_OT_tree_socket_add(wmOperatorType *ot)
|
|||
|
||||
/********************** Remove interface socket operator *********************/
|
||||
|
||||
static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
SpaceNode *snode = CTX_wm_space_node(C);
|
||||
bNodeTree *ntree = snode->edittree;
|
||||
const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
|
||||
|
||||
bNodeSocket *iosock = ntree_get_active_interface_socket(&ntree->inputs);
|
||||
if (!iosock) {
|
||||
iosock = ntree_get_active_interface_socket(&ntree->outputs);
|
||||
}
|
||||
if (!iosock) {
|
||||
bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs :
|
||||
&ntree->outputs);
|
||||
if (iosock == NULL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
|
@ -2340,6 +2330,7 @@ void NODE_OT_tree_socket_remove(wmOperatorType *ot)
|
|||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
|
||||
}
|
||||
|
||||
/********************** Move interface socket operator *********************/
|
||||
|
@ -2356,36 +2347,35 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
|
|||
bNodeTree *ntree = snode->edittree;
|
||||
int direction = RNA_enum_get(op->ptr, "direction");
|
||||
|
||||
ListBase *lb = &ntree->inputs;
|
||||
bNodeSocket *iosock = ntree_get_active_interface_socket(lb);
|
||||
if (!iosock) {
|
||||
lb = &ntree->outputs;
|
||||
iosock = ntree_get_active_interface_socket(lb);
|
||||
}
|
||||
if (!iosock) {
|
||||
const eNodeSocketInOut in_out = RNA_enum_get(op->ptr, "in_out");
|
||||
ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs;
|
||||
|
||||
bNodeSocket *iosock = ntree_get_active_interface_socket(sockets);
|
||||
|
||||
if (iosock == NULL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case 1: { /* up */
|
||||
bNodeSocket *before = iosock->prev;
|
||||
BLI_remlink(lb, iosock);
|
||||
BLI_remlink(sockets, iosock);
|
||||
if (before) {
|
||||
BLI_insertlinkbefore(lb, before, iosock);
|
||||
BLI_insertlinkbefore(sockets, before, iosock);
|
||||
}
|
||||
else {
|
||||
BLI_addhead(lb, iosock);
|
||||
BLI_addhead(sockets, iosock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: { /* down */
|
||||
bNodeSocket *after = iosock->next;
|
||||
BLI_remlink(lb, iosock);
|
||||
BLI_remlink(sockets, iosock);
|
||||
if (after) {
|
||||
BLI_insertlinkafter(lb, after, iosock);
|
||||
BLI_insertlinkafter(sockets, after, iosock);
|
||||
}
|
||||
else {
|
||||
BLI_addtail(lb, iosock);
|
||||
BLI_addtail(sockets, iosock);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2417,6 +2407,7 @@ void NODE_OT_tree_socket_move(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", "");
|
||||
RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", "");
|
||||
}
|
||||
|
||||
/* ********************** Shader Script Update ******************/
|
||||
|
|
|
@ -772,9 +772,7 @@ static int outliner_id_copy_tag(SpaceOutliner *space_outliner, ListBase *tree)
|
|||
}
|
||||
|
||||
/* go over sub-tree */
|
||||
if (TSELEM_OPEN(tselem, space_outliner)) {
|
||||
num_ids += outliner_id_copy_tag(space_outliner, &te->subtree);
|
||||
}
|
||||
num_ids += outliner_id_copy_tag(space_outliner, &te->subtree);
|
||||
}
|
||||
|
||||
return num_ids;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -623,42 +624,57 @@ static const char *draw_seq_text_get_name(Sequence *seq)
|
|||
|
||||
static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t source_len)
|
||||
{
|
||||
*r_source = '\0';
|
||||
|
||||
/* Set source for the most common types. */
|
||||
if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) {
|
||||
BLI_snprintf(r_source, source_len, "%s%s", seq->strip->dir, seq->strip->stripdata->name);
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_SOUND_RAM) {
|
||||
if (seq->sound) {
|
||||
BLI_snprintf(r_source, source_len, "%s", seq->sound->filepath);
|
||||
switch (seq->type) {
|
||||
case SEQ_TYPE_IMAGE:
|
||||
case SEQ_TYPE_MOVIE: {
|
||||
BLI_join_dirfile(r_source, source_len, seq->strip->dir, seq->strip->stripdata->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_MULTICAM) {
|
||||
BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source);
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_TEXT) {
|
||||
TextVars *textdata = seq->effectdata;
|
||||
BLI_snprintf(r_source, source_len, "%s", textdata->text);
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_SCENE) {
|
||||
if (seq->scene_camera) {
|
||||
BLI_snprintf(r_source,
|
||||
source_len,
|
||||
"%s (%s)",
|
||||
seq->scene->id.name + 2,
|
||||
((ID *)seq->scene_camera)->name + 2);
|
||||
case SEQ_TYPE_SOUND_RAM: {
|
||||
if (seq->sound != NULL) {
|
||||
BLI_strncpy(r_source, seq->sound->filepath, source_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
BLI_snprintf(r_source, source_len, "%s", seq->scene->id.name + 2);
|
||||
case SEQ_TYPE_MULTICAM: {
|
||||
BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source);
|
||||
break;
|
||||
}
|
||||
case SEQ_TYPE_TEXT: {
|
||||
const TextVars *textdata = seq->effectdata;
|
||||
BLI_strncpy(r_source, textdata->text, source_len);
|
||||
break;
|
||||
}
|
||||
case SEQ_TYPE_SCENE: {
|
||||
if (seq->scene != NULL) {
|
||||
if (seq->scene_camera != NULL) {
|
||||
BLI_snprintf(r_source,
|
||||
source_len,
|
||||
"%s (%s)",
|
||||
seq->scene->id.name + 2,
|
||||
seq->scene_camera->id.name + 2);
|
||||
}
|
||||
else {
|
||||
BLI_strncpy(r_source, seq->scene->id.name + 2, source_len);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEQ_TYPE_MOVIECLIP: {
|
||||
if (seq->clip != NULL) {
|
||||
BLI_strncpy(r_source, seq->clip->id.name + 2, source_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SEQ_TYPE_MASK: {
|
||||
if (seq->mask != NULL) {
|
||||
BLI_strncpy(r_source, seq->mask->id.name + 2, source_len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_MOVIECLIP) {
|
||||
BLI_snprintf(r_source, source_len, "%s", seq->clip->id.name + 2);
|
||||
}
|
||||
else if (seq->type == SEQ_TYPE_MASK) {
|
||||
BLI_snprintf(r_source, source_len, "%s", seq->mask->id.name + 2);
|
||||
}
|
||||
else {
|
||||
*r_source = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -667,33 +683,39 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq,
|
|||
char *r_overlay_string,
|
||||
size_t overlay_string_len)
|
||||
{
|
||||
const char *name = draw_seq_text_get_name(seq);
|
||||
const char *text_sep = " | ";
|
||||
const char *text_array[5];
|
||||
int i = 0;
|
||||
|
||||
if (sseq->flag & SEQ_SHOW_STRIP_NAME) {
|
||||
text_array[i++] = draw_seq_text_get_name(seq);
|
||||
}
|
||||
|
||||
char source[FILE_MAX];
|
||||
int strip_duration = seq->enddisp - seq->startdisp;
|
||||
draw_seq_text_get_source(seq, source, sizeof(source));
|
||||
|
||||
bool show_name = sseq->flag & SEQ_SHOW_STRIP_NAME;
|
||||
bool show_source = (sseq->flag & (SEQ_SHOW_STRIP_SOURCE)) && source[0] != '\0';
|
||||
bool show_duration = sseq->flag & SEQ_SHOW_STRIP_DURATION;
|
||||
|
||||
size_t string_len = 0;
|
||||
if (show_name) {
|
||||
string_len = BLI_snprintf(r_overlay_string, overlay_string_len, "%s", name);
|
||||
if (show_source || show_duration) {
|
||||
string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
|
||||
if (sseq->flag & SEQ_SHOW_STRIP_SOURCE) {
|
||||
draw_seq_text_get_source(seq, source, sizeof(source));
|
||||
if (source[0] != '\0') {
|
||||
if (i != 0) {
|
||||
text_array[i++] = text_sep;
|
||||
}
|
||||
text_array[i++] = source;
|
||||
}
|
||||
}
|
||||
if (show_source) {
|
||||
string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, "%s", source);
|
||||
if (show_duration) {
|
||||
string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
|
||||
|
||||
char strip_duration_text[16];
|
||||
if (sseq->flag & SEQ_SHOW_STRIP_DURATION) {
|
||||
const int strip_duration = seq->enddisp - seq->startdisp;
|
||||
SNPRINTF(strip_duration_text, "%d", strip_duration);
|
||||
if (i != 0) {
|
||||
text_array[i++] = text_sep;
|
||||
}
|
||||
text_array[i++] = strip_duration_text;
|
||||
}
|
||||
if (show_duration) {
|
||||
string_len += BLI_snprintf(
|
||||
r_overlay_string + string_len, overlay_string_len, "%d", strip_duration);
|
||||
}
|
||||
return string_len;
|
||||
|
||||
BLI_assert(i <= ARRAY_SIZE(text_array));
|
||||
|
||||
return BLI_string_join_array(r_overlay_string, overlay_string_len, text_array, i) -
|
||||
r_overlay_string;
|
||||
}
|
||||
|
||||
/* Draw info text on a sequence strip. */
|
||||
|
|
|
@ -708,8 +708,8 @@ static void walkEvent(bContext *C, WalkInfo *walk, const wmEvent *event)
|
|||
walk->is_cursor_absolute = true;
|
||||
copy_v2_v2_int(walk->prev_mval, event->mval);
|
||||
copy_v2_v2_int(walk->center_mval, event->mval);
|
||||
/* without this we can't turn 180d */
|
||||
CLAMP_MIN(walk->mouse_speed, 4.0f);
|
||||
/* Without this we can't turn 180d with the default speed of 1.0. */
|
||||
walk->mouse_speed *= 4.0f;
|
||||
}
|
||||
#endif /* USE_TABLET_SUPPORT */
|
||||
|
||||
|
|
|
@ -484,7 +484,7 @@ void recalcData_nla(TransInfo *t)
|
|||
for (track = tdn->nlt->next, n = 0; (track) && (n < delta); track = track->next, n++) {
|
||||
/* check if space in this track for the strip */
|
||||
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
|
||||
/* move strip to this track */
|
||||
BLI_remlink(&tdn->nlt->strips, strip);
|
||||
BKE_nlatrack_add_strip(track, strip, is_liboverride);
|
||||
|
@ -504,7 +504,7 @@ void recalcData_nla(TransInfo *t)
|
|||
for (track = tdn->nlt->prev, n = 0; (track) && (n < delta); track = track->prev, n++) {
|
||||
/* check if space in this track for the strip */
|
||||
if (BKE_nlatrack_has_space(track, strip->start, strip->end) &&
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, tdn->nlt)) {
|
||||
!BKE_nlatrack_is_nonlocal_in_liboverride(tdn->id, track)) {
|
||||
/* move strip to this track */
|
||||
BLI_remlink(&tdn->nlt->strips, strip);
|
||||
BKE_nlatrack_add_strip(track, strip, is_liboverride);
|
||||
|
|
|
@ -395,9 +395,9 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
|
|||
}
|
||||
|
||||
float incr_dir[3];
|
||||
mul_v3_m3v3(incr_dir, t->spacemtx_inv, global_dir);
|
||||
if (!(activeSnap(t) && validSnap(t)) && transform_snap_increment(t, incr_dir)) {
|
||||
mul_v3_m3v3(incr_dir, t->spacemtx, incr_dir);
|
||||
copy_v3_v3(incr_dir, global_dir);
|
||||
if (!(activeSnap(t) && validSnap(t)) &&
|
||||
transform_snap_increment_ex(t, (t->con.mode & CON_APPLY) != 0, incr_dir)) {
|
||||
|
||||
/* Test for mixed snap with grid. */
|
||||
float snap_dist_sq = FLT_MAX;
|
||||
|
@ -410,7 +410,6 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
|
|||
}
|
||||
}
|
||||
|
||||
headerTranslation(t, global_dir, str);
|
||||
applyTranslationValue(t, global_dir);
|
||||
|
||||
/* evil hack - redo translation if clipping needed */
|
||||
|
@ -428,6 +427,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
|
|||
|
||||
/* Set the redo value. */
|
||||
mul_v3_m3v3(t->values_final, t->spacemtx_inv, global_dir);
|
||||
headerTranslation(t, (t->con.mode & CON_APPLY) ? t->values_final : global_dir, str);
|
||||
|
||||
recalcData(t);
|
||||
ED_area_status_text(t->area, str);
|
||||
|
|
|
@ -1536,7 +1536,7 @@ static void snap_increment_apply(TransInfo *t,
|
|||
snap_increment_apply_ex(t, max_index, increment_dist, asp, r_val, r_val);
|
||||
}
|
||||
|
||||
bool transform_snap_increment(TransInfo *t, float *val)
|
||||
bool transform_snap_increment_ex(TransInfo *t, bool use_local_space, float *r_val)
|
||||
{
|
||||
if (!activeSnap(t)) {
|
||||
return false;
|
||||
|
@ -1552,12 +1552,26 @@ bool transform_snap_increment(TransInfo *t, float *val)
|
|||
return false;
|
||||
}
|
||||
|
||||
float increment_dist = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
|
||||
if (use_local_space) {
|
||||
BLI_assert(t->idx_max == 2);
|
||||
mul_m3_v3(t->spacemtx_inv, r_val);
|
||||
}
|
||||
|
||||
float increment_dist = (t->modifiers & MOD_PRECISION) ? t->snap[1] : t->snap[0];
|
||||
snap_increment_apply(t, t->idx_max, increment_dist, r_val);
|
||||
|
||||
if (use_local_space) {
|
||||
mul_m3_v3(t->spacemtx, r_val);
|
||||
}
|
||||
|
||||
snap_increment_apply(t, t->idx_max, increment_dist, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool transform_snap_increment(TransInfo *t, float *r_val)
|
||||
{
|
||||
return transform_snap_increment_ex(t, false, r_val);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -54,6 +54,7 @@ void snapFrameTransform(struct TransInfo *t,
|
|||
|
||||
bool transformModeUseSnap(const TransInfo *t);
|
||||
|
||||
bool transform_snap_increment_ex(TransInfo *t, bool use_local_space, float *r_val);
|
||||
bool transform_snap_increment(TransInfo *t, float *val);
|
||||
bool transform_snap_grid(TransInfo *t, float *val);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue