Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-01-12 19:43:30 +01:00
commit 3b4a9b2319
139 changed files with 3137 additions and 1283 deletions

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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}"

View File

@ -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()

View File

@ -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);
}

View File

@ -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')

View File

@ -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()) {

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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. */

View File

@ -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

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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), &currentPoint, 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

View File

@ -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

View File

@ -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);

View File

@ -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. */

View File

@ -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);

View File

@ -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\

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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"),

View File

@ -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()

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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
/** \} */

View File

@ -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,

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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. */

View File

@ -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;

View File

@ -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++;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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);
}

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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());
}
}

View File

@ -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
};

View File

@ -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

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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. */

View File

@ -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();

View File

@ -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. */

View File

@ -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. */

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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. */

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 ******************/

View File

@ -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;

View File

@ -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. */

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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