diff options
author | Jeroen Bakker <j.bakker@atmind.nl> | 2022-03-02 18:03:01 +0300 |
---|---|---|
committer | Jeroen Bakker <j.bakker@atmind.nl> | 2022-03-02 18:03:01 +0300 |
commit | a41c2a513761e8884e92526b069ff6eed8168676 (patch) | |
tree | e624093127815a09d2807dccddaabea35510e154 | |
parent | a23b4429915ca8597510b57353c4df331487c620 (diff) | |
parent | c23ec04b4e30f300a670f1cb1dc882e0608d09ad (diff) |
Merge branch 'master' into temp-image-buffer-rasterizertemp-image-buffer-rasterizer
686 files changed, 18349 insertions, 8026 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 75f5795db68..9f8800fe303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -866,7 +866,7 @@ if(WITH_CYCLES_DEVICE_HIP) endif() #----------------------------------------------------------------------------- -# Check check if submodules are cloned +# Check if submodules are cloned. if(WITH_INTERNATIONAL) file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/datafiles/locale") @@ -889,8 +889,8 @@ if(WITH_PYTHON) # Do this before main 'platform_*' checks, # because UNIX will search for the old Python paths which may not exist. # giving errors about missing paths before this case is met. - if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.9") - message(FATAL_ERROR "At least Python 3.9 is required to build, but found Python ${PYTHON_VERSION}") + if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.10") + message(FATAL_ERROR "At least Python 3.10 is required to build, but found Python ${PYTHON_VERSION}") endif() file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/scripts/addons") @@ -1886,7 +1886,6 @@ elseif(WITH_CYCLES_STANDALONE) add_subdirectory(intern/glew-mx) add_subdirectory(intern/guardedalloc) add_subdirectory(intern/libc_compat) - add_subdirectory(intern/numaapi) add_subdirectory(intern/sky) add_subdirectory(intern/cycles) diff --git a/GNUmakefile b/GNUmakefile index b100bf9290e..575f3e904df 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -51,20 +51,13 @@ Testing Targets * test: Run automated tests with ctest. - * test_cmake: - Runs our own cmake file checker - which detects errors in the cmake file list definitions - * test_pep8: - Checks all python script are pep8 - which are tagged to use the stricter formatting - * test_deprecated: - Checks for deprecation tags in our code which may need to be removed Static Source Code Checking Not associated with building Blender. * check_cppcheck: Run blender source through cppcheck (C & C++). * check_clang_array: Run blender source through clang array checking script (C & C++). + * check_deprecated: Check if there is any deprecated code to remove. * check_splint: Run blenders source through splint (C only). * check_sparse: Run blenders source through sparse (C only). * check_smatch: Run blenders source through smatch (C only). @@ -73,6 +66,10 @@ Static Source Code Checking using one of the accepted licenses in 'doc/license/SPDX-license-identifiers.txt' Append with 'SHOW_HEADERS=1' to show all unique headers which can be useful for spotting license irregularities. + * check_cmake: Runs our own cmake file checker which detects errors in the cmake file list definitions. + * check_pep8: Checks all Python script are pep8 which are tagged to use the stricter formatting. + * check_mypy: Checks all Python scripts using mypy, + see: source/tools/check_source/check_mypy_config.py scripts which are included. Spell Checkers This runs the spell checker from the developer tools repositor. @@ -400,20 +397,6 @@ package_archive: .FORCE test: .FORCE @$(PYTHON) ./build_files/utils/make_test.py "$(BUILD_DIR)" -# run pep8 check check on scripts we distribute. -test_pep8: .FORCE - @$(PYTHON) tests/python/pep8.py > test_pep8.log 2>&1 - @echo "written: test_pep8.log" - -# run some checks on our CMAKE files. -test_cmake: .FORCE - @$(PYTHON) build_files/cmake/cmake_consistency_check.py > test_cmake_consistency.log 2>&1 - @echo "written: test_cmake_consistency.log" - -# run deprecation tests, see if we have anything to remove. -test_deprecated: .FORCE - @$(PYTHON) tests/check_deprecated.py - # ----------------------------------------------------------------------------- # Project Files @@ -491,11 +474,23 @@ check_descriptions: .FORCE @$(BLENDER_BIN) --background -noaudio --factory-startup --python \ "$(BLENDER_DIR)/source/tools/check_source/check_descriptions.py" +check_deprecated: .FORCE + @PYTHONIOENCODING=utf_8 $(PYTHON) \ + source/tools/check_source/check_deprecated.py + check_licenses: .FORCE @PYTHONIOENCODING=utf_8 $(PYTHON) \ "$(BLENDER_DIR)/source/tools/check_source/check_licenses.py" \ "--show-headers=$(SHOW_HEADERS)" +check_pep8: .FORCE + @PYTHONIOENCODING=utf_8 $(PYTHON) \ + tests/python/pep8.py + +check_cmake: .FORCE + @PYTHONIOENCODING=utf_8 $(PYTHON) \ + source/tools/check_source/check_cmake_consistency.py + # ----------------------------------------------------------------------------- # Utilities diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 3d0f6cacb56..41c6fa495d8 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -366,7 +366,7 @@ CLANG_FORMAT_VERSION_MEX="14.0" PYTHON_VERSION="3.10.2" PYTHON_VERSION_SHORT="3.10" -PYTHON_VERSION_MIN="3.9" +PYTHON_VERSION_MIN="3.10" PYTHON_VERSION_MEX="3.12" PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT PYTHON_FORCE_BUILD=false diff --git a/build_files/cmake/Modules/FindFFmpeg.cmake b/build_files/cmake/Modules/FindFFmpeg.cmake index 216dd2a12b6..c7a2e51adaa 100644 --- a/build_files/cmake/Modules/FindFFmpeg.cmake +++ b/build_files/cmake/Modules/FindFFmpeg.cmake @@ -79,4 +79,6 @@ mark_as_advanced( unset(_ffmpeg_SEARCH_DIRS) unset(_ffmpeg_LIBRARIES) -unset(_ffmpeg_INCLUDE_DIR) +# In cmake version 3.21 and up, we can instead use the NO_CACHE option for +# find_path so we don't need to clear it from the cache here. +unset(_ffmpeg_INCLUDE_DIR CACHE) diff --git a/build_files/cmake/Modules/FindOSL.cmake b/build_files/cmake/Modules/FindOSL.cmake index 612fd9feecc..ab5de53d3c9 100644 --- a/build_files/cmake/Modules/FindOSL.cmake +++ b/build_files/cmake/Modules/FindOSL.cmake @@ -72,6 +72,7 @@ FIND_PATH(OSL_SHADER_DIR /usr/include/OSL/ PATH_SUFFIXES share/OSL/shaders + shaders ) # handle the QUIETLY and REQUIRED arguments and set OSL_FOUND to TRUE if @@ -95,6 +96,7 @@ ENDIF() MARK_AS_ADVANCED( OSL_INCLUDE_DIR + OSL_SHADER_DIR ) FOREACH(COMPONENT ${_osl_FIND_COMPONENTS}) STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) diff --git a/build_files/cmake/Modules/FindOpenColorIO.cmake b/build_files/cmake/Modules/FindOpenColorIO.cmake index f290f8d63cd..8e152008ea7 100644 --- a/build_files/cmake/Modules/FindOpenColorIO.cmake +++ b/build_files/cmake/Modules/FindOpenColorIO.cmake @@ -83,12 +83,14 @@ ENDIF() MARK_AS_ADVANCED( OPENCOLORIO_INCLUDE_DIR OPENCOLORIO_LIBRARY - OPENCOLORIO_OPENCOLORIO_LIBRARY - OPENCOLORIO_TINYXML_LIBRARY - OPENCOLORIO_YAML-CPP_LIBRARY OPENCOLORIO_VERSION ) +FOREACH(COMPONENT ${_opencolorio_FIND_COMPONENTS}) + STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) + MARK_AS_ADVANCED(OPENCOLORIO_${UPPERCOMPONENT}_LIBRARY) +ENDFOREACH() + UNSET(COMPONENT) UNSET(UPPERCOMPONENT) UNSET(_opencolorio_FIND_COMPONENTS) diff --git a/build_files/cmake/Modules/FindOpenEXR.cmake b/build_files/cmake/Modules/FindOpenEXR.cmake index 792bb127f99..f772ef4e1ff 100644 --- a/build_files/cmake/Modules/FindOpenEXR.cmake +++ b/build_files/cmake/Modules/FindOpenEXR.cmake @@ -29,14 +29,6 @@ ENDIF() # Old versions (before 2.0?) do not have any version string, just assuming this should be fine though. SET(_openexr_libs_ver_init "2.0") -SET(_openexr_FIND_COMPONENTS - Half - Iex - IlmImf - IlmThread - Imath -) - SET(_openexr_SEARCH_DIRS ${OPENEXR_ROOT_DIR} /opt/lib/openexr @@ -89,6 +81,24 @@ UNSET(_openexr_libs_ver_init) STRING(REGEX REPLACE "([0-9]+)[.]([0-9]+).*" "\\1_\\2" _openexr_libs_ver ${OPENEXR_VERSION}) +# Different library names in 3.0, and Imath and Half moved out. +IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0") + SET(_openexr_FIND_COMPONENTS + Iex + IlmThread + OpenEXR + OpenEXRCore + ) +ELSE() + SET(_openexr_FIND_COMPONENTS + Half + Iex + IlmImf + IlmThread + Imath + ) +ENDIF() + SET(_openexr_LIBRARIES) FOREACH(COMPONENT ${_openexr_FIND_COMPONENTS}) STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) @@ -107,6 +117,57 @@ ENDFOREACH() UNSET(_openexr_libs_ver) +IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0") + # For OpenEXR 3.x, we also need to find the now separate Imath library. + # For simplicity we add it to the OpenEXR includes and libraries, as we + # have no direct dependency on Imath and it's simpler to support both + # 2.x and 3.x this way. + + # Find include directory + FIND_PATH(IMATH_INCLUDE_DIR + NAMES + Imath/ImathMath.h + HINTS + ${_openexr_SEARCH_DIRS} + PATH_SUFFIXES + include + ) + + # Find version + FIND_FILE(_imath_config + NAMES + ImathConfig.h + PATHS + ${IMATH_INCLUDE_DIR}/Imath + NO_DEFAULT_PATH + ) + + # Find line with version, extract string, and format for library suffix. + FILE(STRINGS "${_imath_config}" _imath_build_specification + REGEX "^[ \t]*#define[ \t]+IMATH_VERSION_STRING[ \t]+\"[.0-9]+\".*$") + STRING(REGEX REPLACE ".*#define[ \t]+IMATH_VERSION_STRING[ \t]+\"([.0-9]+)\".*" + "\\1" _imath_libs_ver ${_imath_build_specification}) + STRING(REGEX REPLACE "([0-9]+)[.]([0-9]+).*" "\\1_\\2" _imath_libs_ver ${_imath_libs_ver}) + + # Find library, with or without version number. + FIND_LIBRARY(IMATH_LIBRARY + NAMES + Imath-${_imath_libs_ver} Imath + NAMES_PER_DIR + HINTS + ${_openexr_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + LIST(APPEND _openexr_LIBRARIES "${IMATH_LIBRARY}") + + # In cmake version 3.21 and up, we can instead use the NO_CACHE option for + # FIND_FILE so we don't need to clear it from the cache here. + UNSET(_imath_config CACHE) + UNSET(_imath_libs_ver) + UNSET(_imath_build_specification) +ENDIF() + # handle the QUIETLY and REQUIRED arguments and set OPENEXR_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) @@ -115,13 +176,25 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenEXR DEFAULT_MSG IF(OPENEXR_FOUND) SET(OPENEXR_LIBRARIES ${_openexr_LIBRARIES}) - # Both include paths are needed because of dummy OSL headers mixing #include <OpenEXR/foo.h> and #include <foo.h> :( - SET(OPENEXR_INCLUDE_DIRS ${OPENEXR_INCLUDE_DIR} ${OPENEXR_INCLUDE_DIR}/OpenEXR) + # Both include paths are needed because of dummy OSL headers mixing + # #include <OpenEXR/foo.h> and #include <foo.h>, as well as Alembic + # include <half.h> directly. + SET(OPENEXR_INCLUDE_DIRS + ${OPENEXR_INCLUDE_DIR} + ${OPENEXR_INCLUDE_DIR}/OpenEXR) + + IF(OPENEXR_VERSION VERSION_GREATER_EQUAL "3.0.0") + LIST(APPEND OPENEXR_INCLUDE_DIRS + ${IMATH_INCLUDE_DIR} + ${IMATH_INCLUDE_DIR}/Imath) + ENDIF() ENDIF() MARK_AS_ADVANCED( OPENEXR_INCLUDE_DIR OPENEXR_VERSION + IMATH_INCLUDE_DIR + IMATH_LIBRARY ) FOREACH(COMPONENT ${_openexr_FIND_COMPONENTS}) STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT) diff --git a/build_files/cmake/Modules/FindOpenImageDenoise.cmake b/build_files/cmake/Modules/FindOpenImageDenoise.cmake index becd2f0cffe..a7f03fefd24 100644 --- a/build_files/cmake/Modules/FindOpenImageDenoise.cmake +++ b/build_files/cmake/Modules/FindOpenImageDenoise.cmake @@ -106,6 +106,7 @@ ENDIF() MARK_AS_ADVANCED( OPENIMAGEDENOISE_INCLUDE_DIR + OPENIMAGEDENOISE_LIBRARY ) FOREACH(COMPONENT ${_openimagedenoise_FIND_COMPONENTS}) diff --git a/build_files/cmake/Modules/FindOpenImageIO.cmake b/build_files/cmake/Modules/FindOpenImageIO.cmake index 27a56647eac..0e8742ef2ed 100644 --- a/build_files/cmake/Modules/FindOpenImageIO.cmake +++ b/build_files/cmake/Modules/FindOpenImageIO.cmake @@ -44,6 +44,8 @@ FIND_LIBRARY(OPENIMAGEIO_LIBRARY lib64 lib ) +set(_openimageio_LIBRARIES ${OPENIMAGEIO_LIBRARY}) + FIND_FILE(OPENIMAGEIO_IDIFF NAMES idiff @@ -53,14 +55,47 @@ FIND_FILE(OPENIMAGEIO_IDIFF bin ) +# Additionally find util library if needed. In old versions this library was +# included in libOpenImageIO and linking to both would duplicate symbols. In +# new versions we need to link to both. +FIND_FILE(_openimageio_export + NAMES + export.h + PATHS + ${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO + NO_DEFAULT_PATH +) + +# Use existence of OIIO_UTIL_API to check if it's a separate lib. +FILE(STRINGS "${_openimageio_export}" _openimageio_util_define + REGEX "^[ \t]*#[ \t]*define[ \t]+OIIO_UTIL_API.*$") + +IF(_openimageio_util_define) + FIND_LIBRARY(OPENIMAGEIO_UTIL_LIBRARY + NAMES + OpenImageIO_Util + HINTS + ${_openimageio_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib + ) + + LIST(APPEND _openimageio_LIBRARIES ${OPENIMAGEIO_UTIL_LIBRARY}) +ENDIF() + +# In cmake version 3.21 and up, we can instead use the NO_CACHE option for +# FIND_FILE so we don't need to clear it from the cache here. +UNSET(_openimageio_export CACHE) +UNSET(_openimageio_util_define) + # handle the QUIETLY and REQUIRED arguments and set OPENIMAGEIO_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenImageIO DEFAULT_MSG - OPENIMAGEIO_LIBRARY OPENIMAGEIO_INCLUDE_DIR) + _openimageio_LIBRARIES OPENIMAGEIO_INCLUDE_DIR) IF(OPENIMAGEIO_FOUND) - SET(OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARY}) + SET(OPENIMAGEIO_LIBRARIES ${_openimageio_LIBRARIES}) SET(OPENIMAGEIO_INCLUDE_DIRS ${OPENIMAGEIO_INCLUDE_DIR}) IF(EXISTS ${OPENIMAGEIO_INCLUDE_DIR}/OpenImageIO/pugixml.hpp) SET(OPENIMAGEIO_PUGIXML_FOUND TRUE) @@ -74,7 +109,9 @@ ENDIF() MARK_AS_ADVANCED( OPENIMAGEIO_INCLUDE_DIR OPENIMAGEIO_LIBRARY + OPENIMAGEIO_UTIL_LIBRARY OPENIMAGEIO_IDIFF ) UNSET(_openimageio_SEARCH_DIRS) +UNSET(_openimageio_LIBRARIES) diff --git a/build_files/cmake/cmake_consistency_check.py b/build_files/cmake/cmake_consistency_check.py deleted file mode 100755 index ec31b4cdfe3..00000000000 --- a/build_files/cmake/cmake_consistency_check.py +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: GPL-2.0-or-later - -# <pep8 compliant> - -# Note: this code should be cleaned up / refactored. - -import sys -if sys.version_info.major < 3: - print("\nPython3.x needed, found %s.\nAborting!\n" % - sys.version.partition(" ")[0]) - sys.exit(1) - -import os -from os.path import ( - dirname, - join, - normpath, - splitext, -) - -from cmake_consistency_check_config import ( - IGNORE_SOURCE, - IGNORE_SOURCE_MISSING, - IGNORE_CMAKE, - UTF8_CHECK, - SOURCE_DIR, - BUILD_DIR, -) - -from typing import ( - Callable, - Dict, - Generator, - Iterator, - List, - Optional, - Tuple, -) - - -global_h = set() -global_c = set() -global_refs: Dict[str, List[Tuple[str, int]]] = {} - -# Flatten `IGNORE_SOURCE_MISSING` to avoid nested looping. -IGNORE_SOURCE_MISSING_FLAT = [ - (k, ignore_path) for k, ig_list in IGNORE_SOURCE_MISSING - for ignore_path in ig_list -] - -# Ignore cmake file, path pairs. -global_ignore_source_missing: Dict[str, List[str]] = {} -for k, v in IGNORE_SOURCE_MISSING_FLAT: - global_ignore_source_missing.setdefault(k, []).append(v) -del IGNORE_SOURCE_MISSING_FLAT - - -def replace_line(f: str, i: int, text: str, keep_indent: bool = True) -> None: - file_handle = open(f, 'r') - data = file_handle.readlines() - file_handle.close() - - l = data[i] - ws = l[:len(l) - len(l.lstrip())] - - data[i] = "%s%s\n" % (ws, text) - - file_handle = open(f, 'w') - file_handle.writelines(data) - file_handle.close() - - -def source_list( - path: str, - filename_check: Optional[Callable[[str], bool]] = None, -) -> Generator[str, None, None]: - for dirpath, dirnames, filenames in os.walk(path): - # skip '.git' - dirnames[:] = [d for d in dirnames if not d.startswith(".")] - - for filename in filenames: - if filename_check is None or filename_check(filename): - yield os.path.join(dirpath, filename) - - -# extension checking -def is_cmake(filename: str) -> bool: - ext = splitext(filename)[1] - return (ext == ".cmake") or (filename == "CMakeLists.txt") - - -def is_c_header(filename: str) -> bool: - ext = splitext(filename)[1] - return (ext in {".h", ".hpp", ".hxx", ".hh"}) - - -def is_c(filename: str) -> bool: - ext = splitext(filename)[1] - return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl", ".metal"}) - - -def is_c_any(filename: str) -> bool: - return is_c(filename) or is_c_header(filename) - - -def cmake_get_src(f: str) -> None: - - sources_h = [] - sources_c = [] - - filen = open(f, "r", encoding="utf8") - it: Optional[Iterator[str]] = iter(filen) - found = False - i = 0 - # print(f) - - def is_definition(l: str, f: str, i: int, name: str) -> bool: - if l.startswith("unset("): - return False - - if ('set(%s' % name) in l or ('set(' in l and l.endswith(name)): - if len(l.split()) > 1: - raise Exception("strict formatting not kept 'set(%s*' %s:%d" % (name, f, i)) - return True - - if ("list(APPEND %s" % name) in l or ('list(APPEND ' in l and l.endswith(name)): - if l.endswith(")"): - raise Exception("strict formatting not kept 'list(APPEND %s...)' on 1 line %s:%d" % (name, f, i)) - return True - return False - - while it is not None: - context_name = "" - while it is not None: - i += 1 - try: - l = next(it) - except StopIteration: - it = None - break - l = l.strip() - if not l.startswith("#"): - found = is_definition(l, f, i, "SRC") - if found: - context_name = "SRC" - break - found = is_definition(l, f, i, "INC") - if found: - context_name = "INC" - break - - if found: - cmake_base = dirname(f) - cmake_base_bin = os.path.join(BUILD_DIR, os.path.relpath(cmake_base, SOURCE_DIR)) - - # Find known missing sources list (if we have one). - f_rel = os.path.relpath(f, SOURCE_DIR) - f_rel_key = f_rel - if os.sep != "/": - f_rel_key = f_rel_key.replace(os.sep, "/") - local_ignore_source_missing = global_ignore_source_missing.get(f_rel_key, []) - - while it is not None: - i += 1 - try: - l = next(it) - except StopIteration: - it = None - break - - l = l.strip() - - if not l.startswith("#"): - - # Remove in-line comments. - l = l.split(" # ")[0].rstrip() - - if ")" in l: - if l.strip() != ")": - raise Exception("strict formatting not kept '*)' %s:%d" % (f, i)) - break - - # replace dirs - l = l.replace("${CMAKE_SOURCE_DIR}", SOURCE_DIR) - l = l.replace("${CMAKE_CURRENT_SOURCE_DIR}", cmake_base) - l = l.replace("${CMAKE_CURRENT_BINARY_DIR}", cmake_base_bin) - l = l.strip('"') - - if not l: - pass - elif l in local_ignore_source_missing: - local_ignore_source_missing.remove(l) - elif l.startswith("$"): - if context_name == "SRC": - # assume if it ends with context_name we know about it - if not l.split("}")[0].endswith(context_name): - print("Can't use var '%s' %s:%d" % (l, f, i)) - elif len(l.split()) > 1: - raise Exception("Multi-line define '%s' %s:%d" % (l, f, i)) - else: - new_file = normpath(join(cmake_base, l)) - - if context_name == "SRC": - if is_c_header(new_file): - sources_h.append(new_file) - global_refs.setdefault(new_file, []).append((f, i)) - elif is_c(new_file): - sources_c.append(new_file) - global_refs.setdefault(new_file, []).append((f, i)) - elif l in {"PARENT_SCOPE", }: - # cmake var, ignore - pass - elif new_file.endswith(".list"): - pass - elif new_file.endswith(".def"): - pass - elif new_file.endswith(".cl"): # opencl - pass - elif new_file.endswith(".cu"): # cuda - pass - elif new_file.endswith(".osl"): # open shading language - pass - elif new_file.endswith(".glsl"): - pass - else: - raise Exception("unknown file type - not c or h %s -> %s" % (f, new_file)) - - elif context_name == "INC": - if new_file.startswith(BUILD_DIR): - # assume generated path - pass - elif os.path.isdir(new_file): - new_path_rel = os.path.relpath(new_file, cmake_base) - - if new_path_rel != l: - print("overly relative path:\n %s:%d\n %s\n %s" % (f, i, l, new_path_rel)) - - # # Save time. just replace the line - # replace_line(f, i - 1, new_path_rel) - - else: - raise Exception("non existent include %s:%d -> %s" % (f, i, new_file)) - - # print(new_file) - - global_h.update(set(sources_h)) - global_c.update(set(sources_c)) - ''' - if not sources_h and not sources_c: - raise Exception("No sources %s" % f) - - sources_h_fs = list(source_list(cmake_base, is_c_header)) - sources_c_fs = list(source_list(cmake_base, is_c)) - ''' - # find missing C files: - ''' - for ff in sources_c_fs: - if ff not in sources_c: - print(" missing: " + ff) - ''' - - # reset - del sources_h[:] - del sources_c[:] - - filen.close() - - -def is_ignore_source(f: str, ignore_used: List[bool]) -> bool: - for index, ignore_path in enumerate(IGNORE_SOURCE): - if ignore_path in f: - ignore_used[index] = True - return True - return False - - -def is_ignore_cmake(f: str, ignore_used: List[bool]) -> bool: - for index, ignore_path in enumerate(IGNORE_CMAKE): - if ignore_path in f: - ignore_used[index] = True - return True - return False - - -def main() -> None: - - print("Scanning:", SOURCE_DIR) - - ignore_used_source = [False] * len(IGNORE_SOURCE) - ignore_used_cmake = [False] * len(IGNORE_CMAKE) - - for cmake in source_list(SOURCE_DIR, is_cmake): - if not is_ignore_cmake(cmake, ignore_used_cmake): - cmake_get_src(cmake) - - # First do stupid check, do these files exist? - print("\nChecking for missing references:") - is_err = False - errs = [] - for f in (global_h | global_c): - if f.startswith(BUILD_DIR): - continue - - if not os.path.exists(f): - refs = global_refs[f] - if refs: - for cf, i in refs: - errs.append((cf, i)) - else: - raise Exception("CMake references missing, internal error, aborting!") - is_err = True - - errs.sort() - errs.reverse() - for cf, i in errs: - print("%s:%d" % (cf, i)) - # Write a 'sed' script, useful if we get a lot of these - # print("sed '%dd' '%s' > '%s.tmp' ; mv '%s.tmp' '%s'" % (i, cf, cf, cf, cf)) - - if is_err: - raise Exception("CMake references missing files, aborting!") - del is_err - del errs - - # now check on files not accounted for. - print("\nC/C++ Files CMake does not know about...") - for cf in sorted(source_list(SOURCE_DIR, is_c)): - if not is_ignore_source(cf, ignore_used_source): - if cf not in global_c: - print("missing_c: ", cf) - - # Check if automake builds a corresponding .o file. - ''' - if cf in global_c: - out1 = os.path.splitext(cf)[0] + ".o" - out2 = os.path.splitext(cf)[0] + ".Po" - out2_dir, out2_file = out2 = os.path.split(out2) - out2 = os.path.join(out2_dir, ".deps", out2_file) - if not os.path.exists(out1) and not os.path.exists(out2): - print("bad_c: ", cf) - ''' - - print("\nC/C++ Headers CMake does not know about...") - for hf in sorted(source_list(SOURCE_DIR, is_c_header)): - if not is_ignore_source(hf, ignore_used_source): - if hf not in global_h: - print("missing_h: ", hf) - - if UTF8_CHECK: - # test encoding - import traceback - for files in (global_c, global_h): - for f in sorted(files): - if os.path.exists(f): - # ignore outside of our source tree - if "extern" not in f: - i = 1 - try: - for _ in open(f, "r", encoding="utf8"): - i += 1 - except UnicodeDecodeError: - print("Non utf8: %s:%d" % (f, i)) - if i > 1: - traceback.print_exc() - - # Check ignores aren't stale - print("\nCheck for unused 'IGNORE_SOURCE' paths...") - for index, ignore_path in enumerate(IGNORE_SOURCE): - if not ignore_used_source[index]: - print("unused ignore: %r" % ignore_path) - - # Check ignores aren't stale - print("\nCheck for unused 'IGNORE_SOURCE_MISSING' paths...") - for k, v in sorted(global_ignore_source_missing.items()): - for ignore_path in v: - print("unused ignore: %r -> %r" % (ignore_path, k)) - - # Check ignores aren't stale - print("\nCheck for unused 'IGNORE_CMAKE' paths...") - for index, ignore_path in enumerate(IGNORE_CMAKE): - if not ignore_used_cmake[index]: - print("unused ignore: %r" % ignore_path) - - -if __name__ == "__main__": - main() diff --git a/build_files/cmake/cmake_consistency_check_config.py b/build_files/cmake/cmake_consistency_check_config.py deleted file mode 100644 index 94e5ddb4289..00000000000 --- a/build_files/cmake/cmake_consistency_check_config.py +++ /dev/null @@ -1,64 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -import os - -IGNORE_SOURCE = ( - "/test/", - "/tests/gtests/", - "/release/", - - # specific source files - "extern/audaspace/", - - # Use for `WIN32` only. - "source/creator/blender_launcher_win32.c", - - # specific source files - "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp", - "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp", - "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.cpp", - "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.cpp", - "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.cpp", - "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.cpp", - "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp", - "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp", - - "doc/doxygen/doxygen.extern.h", - "doc/doxygen/doxygen.intern.h", - "doc/doxygen/doxygen.main.h", - "doc/doxygen/doxygen.source.h", - "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h", - "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h", - "extern/bullet2/src/BulletCollision/CollisionDispatch/btInternalEdgeUtility.h", - "extern/bullet2/src/BulletCollision/CollisionShapes/btBox2dShape.h", - "extern/bullet2/src/BulletCollision/CollisionShapes/btConvex2dShape.h", - "extern/bullet2/src/BulletDynamics/Character/btKinematicCharacterController.h", - "extern/bullet2/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h", - "extern/bullet2/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h", -) - -# Ignore cmake file, path pairs. -IGNORE_SOURCE_MISSING = ( - ( # Use for cycles stand-alone. - "intern/cycles/util/CMakeLists.txt", ( - "../../third_party/numaapi/include", - )), - ( # Use for `WITH_NANOVDB`. - "intern/cycles/kernel/CMakeLists.txt", ( - "nanovdb/util/CSampleFromVoxels.h", - "nanovdb/util/SampleFromVoxels.h", - "nanovdb/NanoVDB.h", - "nanovdb/CNanoVDB.h", - ), - ), -) - -IGNORE_CMAKE = ( - "extern/audaspace/CMakeLists.txt", -) - -UTF8_CHECK = True - -SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..", "..")))) - -# doesn't have to exist, just use as reference -BUILD_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(SOURCE_DIR, "..", "build")))) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 96c402760b7..77d1db97997 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -345,6 +345,7 @@ if(WITH_BOOST) find_package(IcuLinux) endif() mark_as_advanced(Boost_DIR) # why doesn't boost do this? + mark_as_advanced(Boost_INCLUDE_DIR) # why doesn't boost do this? endif() set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS}) @@ -848,3 +849,45 @@ if(WITH_COMPILER_CCACHE) set(WITH_COMPILER_CCACHE OFF) endif() endif() + +# On some platforms certain atomic operations are not possible with assembly and/or intrinsics and +# they are emulated in software with locks. For example, on armel there is no intrinsics to grant +# 64 bit atomic operations and STL library uses libatomic to offload software emulation of atomics +# to. +# This function will check whether libatomic is required and if so will configure linker flags. +# If atomic operations are possible without libatomic then linker flags are left as-is. +function(CONFIGURE_ATOMIC_LIB_IF_NEEDED) + # Source which is used to enforce situation when software emulation of atomics is required. + # Assume that using 64bit integer gives a definitive asnwer (as in, if 64bit atomic operations + # are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible. + set(_source + "#include <atomic> + #include <cstdint> + int main(int argc, char **argv) { + std::atomic<uint64_t> uint64; uint64++; + return 0; + }") + + include(CheckCXXSourceCompiles) + check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITHOUT_LIBATOMIC) + + if(NOT ATOMIC_OPS_WITHOUT_LIBATOMIC) + # Compilation of the test program has failed. + # Try it again with -latomic to see if this is what is needed, or whether something else is + # going on. + + set(CMAKE_REQUIRED_LIBRARIES atomic) + check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITH_LIBATOMIC) + + if(ATOMIC_OPS_WITH_LIBATOMIC) + set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -latomic" PARENT_SCOPE) + else() + # Atomic operations are required part of Blender and it is not possible to process forward. + # We expect that either standard library or libatomic will make atomics to work. If both + # cases has failed something fishy o na bigger scope is going on. + message(FATAL_ERROR "Failed to detect required configuration for atomic operations") + endif() + endif() +endfunction() + +CONFIGURE_ATOMIC_LIB_IF_NEEDED() diff --git a/doc/python_api/static/css/version_switch.css b/doc/python_api/static/css/version_switch.css index 360ff2eea0e..adb80b01c0a 100644 --- a/doc/python_api/static/css/version_switch.css +++ b/doc/python_api/static/css/version_switch.css @@ -1,5 +1,6 @@ /* Override RTD theme */ .rst-versions { + display: none; border-top: 0px; overflow: visible; } diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp index de3ca099696..69bb45119a6 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp @@ -177,7 +177,7 @@ void FFMPEGReader::init(int stream) // get a decoder and open it #ifndef FFMPEG_OLD_CODE - AVCodec* aCodec = avcodec_find_decoder(m_formatCtx->streams[m_stream]->codecpar->codec_id); + const AVCodec* aCodec = avcodec_find_decoder(m_formatCtx->streams[m_stream]->codecpar->codec_id); if(!aCodec) AUD_THROW(FileException, "File couldn't be read, no decoder found with ffmpeg."); diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp index 10517d1d596..32eb2330594 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGWriter.cpp @@ -23,6 +23,7 @@ extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avio.h> +#include <libavutil/channel_layout.h> } AUD_NAMESPACE_BEGIN @@ -171,66 +172,66 @@ FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container fo if(avformat_alloc_output_context2(&m_formatCtx, nullptr, formats[format], filename.c_str()) < 0) AUD_THROW(FileException, "File couldn't be written, format couldn't be found with ffmpeg."); - AVOutputFormat* outputFmt = m_formatCtx->oformat; + const AVOutputFormat* outputFmt = m_formatCtx->oformat; if(!outputFmt) { avformat_free_context(m_formatCtx); AUD_THROW(FileException, "File couldn't be written, output format couldn't be found with ffmpeg."); } - outputFmt->audio_codec = AV_CODEC_ID_NONE; + AVCodecID audio_codec = AV_CODEC_ID_NONE; switch(codec) { case CODEC_AAC: - outputFmt->audio_codec = AV_CODEC_ID_AAC; + audio_codec = AV_CODEC_ID_AAC; break; case CODEC_AC3: - outputFmt->audio_codec = AV_CODEC_ID_AC3; + audio_codec = AV_CODEC_ID_AC3; break; case CODEC_FLAC: - outputFmt->audio_codec = AV_CODEC_ID_FLAC; + audio_codec = AV_CODEC_ID_FLAC; break; case CODEC_MP2: - outputFmt->audio_codec = AV_CODEC_ID_MP2; + audio_codec = AV_CODEC_ID_MP2; break; case CODEC_MP3: - outputFmt->audio_codec = AV_CODEC_ID_MP3; + audio_codec = AV_CODEC_ID_MP3; break; case CODEC_OPUS: - outputFmt->audio_codec = AV_CODEC_ID_OPUS; + audio_codec = AV_CODEC_ID_OPUS; break; case CODEC_PCM: switch(specs.format) { case FORMAT_U8: - outputFmt->audio_codec = AV_CODEC_ID_PCM_U8; + audio_codec = AV_CODEC_ID_PCM_U8; break; case FORMAT_S16: - outputFmt->audio_codec = AV_CODEC_ID_PCM_S16LE; + audio_codec = AV_CODEC_ID_PCM_S16LE; break; case FORMAT_S24: - outputFmt->audio_codec = AV_CODEC_ID_PCM_S24LE; + audio_codec = AV_CODEC_ID_PCM_S24LE; break; case FORMAT_S32: - outputFmt->audio_codec = AV_CODEC_ID_PCM_S32LE; + audio_codec = AV_CODEC_ID_PCM_S32LE; break; case FORMAT_FLOAT32: - outputFmt->audio_codec = AV_CODEC_ID_PCM_F32LE; + audio_codec = AV_CODEC_ID_PCM_F32LE; break; case FORMAT_FLOAT64: - outputFmt->audio_codec = AV_CODEC_ID_PCM_F64LE; + audio_codec = AV_CODEC_ID_PCM_F64LE; break; default: - outputFmt->audio_codec = AV_CODEC_ID_NONE; + audio_codec = AV_CODEC_ID_NONE; break; } break; case CODEC_VORBIS: - outputFmt->audio_codec = AV_CODEC_ID_VORBIS; + audio_codec = AV_CODEC_ID_VORBIS; break; default: - outputFmt->audio_codec = AV_CODEC_ID_NONE; + audio_codec = AV_CODEC_ID_NONE; break; } @@ -268,10 +269,10 @@ FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container fo try { - if(outputFmt->audio_codec == AV_CODEC_ID_NONE) + if(audio_codec == AV_CODEC_ID_NONE) AUD_THROW(FileException, "File couldn't be written, audio codec not found with ffmpeg."); - AVCodec* codec = avcodec_find_encoder(outputFmt->audio_codec); + const AVCodec* codec = avcodec_find_encoder(audio_codec); if(!codec) AUD_THROW(FileException, "File couldn't be written, audio encoder couldn't be found with ffmpeg."); diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 75ff7114dd7..3248ef0dcda 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -33,15 +33,19 @@ else() endif() if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) - list(APPEND LIBRARIES ${GLUT_LIBRARIES}) + add_definitions(${GL_DEFINITIONS}) + list(APPEND INC_SYS + ${GLEW_INCLUDE_DIR} + ${SDL2_INCLUDE_DIRS} + ) + list(APPEND LIBRARIES + ${CYCLES_GL_LIBRARIES} + ${SDL2_LIBRARIES} + ) endif() -list(APPEND LIBRARIES ${CYCLES_GL_LIBRARIES}) - # Common configuration. -add_definitions(${GL_DEFINITIONS}) - include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) @@ -55,6 +59,18 @@ if(WITH_CYCLES_STANDALONE) oiio_output_driver.cpp oiio_output_driver.h ) + + if(WITH_CYCLES_STANDALONE_GUI) + list(APPEND SRC + opengl/display_driver.cpp + opengl/display_driver.h + opengl/shader.cpp + opengl/shader.h + opengl/window.cpp + opengl/window.h + ) + endif() + add_executable(cycles ${SRC} ${INC} ${INC_SYS}) unset(SRC) @@ -69,6 +85,10 @@ if(WITH_CYCLES_STANDALONE) # OpenImageDenoise uses BNNS from the Accelerate framework. set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework Accelerate") endif() + if(WITH_CYCLES_STANDALONE_GUI) + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS + " -framework Cocoa -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework ForceFeedback -framework CoreVideo") + endif() endif() if(UNIX AND NOT APPLE) diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 0e425ac3d8f..ef20f64debd 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -27,11 +27,10 @@ #include "app/oiio_output_driver.h" #ifdef WITH_CYCLES_STANDALONE_GUI -# include "util/view.h" +# include "opengl/display_driver.h" +# include "opengl/window.h" #endif -#include "app/cycles_xml.h" - CCL_NAMESPACE_BEGIN struct Options { @@ -117,7 +116,14 @@ static void session_init() options.output_pass = "combined"; options.session = new Session(options.session_params, options.scene_params); - if (!options.output_filepath.empty()) { +#ifdef WITH_CYCLES_STANDALONE_GUI + if (!options.session_params.background) { + options.session->set_display_driver(make_unique<OpenGLDisplayDriver>( + window_opengl_context_enable, window_opengl_context_disable)); + } + else +#endif + if (!options.output_filepath.empty()) { options.session->set_output_driver(make_unique<OIIOOutputDriver>( options.output_filepath, options.output_pass, session_print)); } @@ -126,7 +132,7 @@ static void session_init() options.session->progress.set_update_callback(function_bind(&session_print_status)); #ifdef WITH_CYCLES_STANDALONE_GUI else - options.session->progress.set_update_callback(function_bind(&view_redraw)); + options.session->progress.set_update_callback(function_bind(&window_redraw)); #endif /* load scene */ @@ -191,10 +197,10 @@ static void display_info(Progress &progress) sample_time, interactive.c_str()); - view_display_info(str.c_str()); + window_display_info(str.c_str()); if (options.show_help) - view_display_help(); + window_display_help(); } static void display() @@ -525,15 +531,15 @@ int main(int argc, const char **argv) string title = "Cycles: " + path_filename(options.filepath); /* init/exit are callback so they run while GL is initialized */ - view_main_loop(title.c_str(), - options.width, - options.height, - session_init, - session_exit, - resize, - display, - keyboard, - motion); + window_main_loop(title.c_str(), + options.width, + options.height, + session_init, + session_exit, + resize, + display, + keyboard, + motion); } #endif diff --git a/intern/cycles/app/opengl/display_driver.cpp b/intern/cycles/app/opengl/display_driver.cpp new file mode 100644 index 00000000000..8b99f3b6feb --- /dev/null +++ b/intern/cycles/app/opengl/display_driver.cpp @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "app/opengl/display_driver.h" +#include "app/opengl/shader.h" + +#include "util/log.h" +#include "util/string.h" + +#include <GL/glew.h> +#include <SDL.h> + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * OpenGLDisplayDriver. + */ + +OpenGLDisplayDriver::OpenGLDisplayDriver(const function<bool()> &gl_context_enable, + const function<void()> &gl_context_disable) + : gl_context_enable_(gl_context_enable), gl_context_disable_(gl_context_disable) +{ +} + +OpenGLDisplayDriver::~OpenGLDisplayDriver() +{ +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +void OpenGLDisplayDriver::next_tile_begin() +{ + /* Assuming no tiles used in interactive display. */ +} + +bool OpenGLDisplayDriver::update_begin(const Params ¶ms, int texture_width, int texture_height) +{ + /* Note that it's the responsibility of OpenGLDisplayDriver to ensure updating and drawing + * the texture does not happen at the same time. This is achieved indirectly. + * + * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock. + * This same lock is also held when do_draw() is called, which together ensure mutual + * exclusion. + * + * This locking is not performed on the Cycles side, because that would cause lock inversion. */ + if (!gl_context_enable_()) { + return false; + } + + if (gl_render_sync_) { + glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (!gl_texture_resources_ensure()) { + gl_context_disable_(); + return false; + } + + /* Update texture dimensions if needed. */ + if (texture_.width != texture_width || texture_.height != texture_height) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0); + texture_.width = texture_width; + texture_.height = texture_height; + glBindTexture(GL_TEXTURE_2D, 0); + + /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to + * avoid undefined content. */ + texture_.need_clear = true; + } + + /* Update PBO dimensions if needed. + * + * NOTE: Allocate the PBO for the size which will fit the final render resolution (as in, + * at a resolution divider 1. This was we don't need to recreate graphics interoperability + * objects which are costly and which are tied to the specific underlying buffer size. + * The downside of this approach is that when graphics interoperability is not used we are + * sending too much data to GPU when resolution divider is not 1. */ + const int buffer_width = params.full_size.x; + const int buffer_height = params.full_size.y; + if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) { + const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.buffer_width = buffer_width; + texture_.buffer_height = buffer_height; + } + + /* New content will be provided to the texture in one way or another, so mark this in a + * centralized place. */ + texture_.need_update = true; + + return true; +} + +void OpenGLDisplayDriver::update_end() +{ + gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + gl_context_disable_(); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *OpenGLDisplayDriver::map_texture_buffer() +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + + half4 *mapped_rgba_pixels = reinterpret_cast<half4 *>( + glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + if (!mapped_rgba_pixels) { + LOG(ERROR) << "Error mapping OpenGLDisplayDriver pixel buffer object."; + } + + if (texture_.need_clear) { + const int64_t texture_width = texture_.width; + const int64_t texture_height = texture_.height; + memset(reinterpret_cast<void *>(mapped_rgba_pixels), + 0, + texture_width * texture_height * sizeof(half4)); + texture_.need_clear = false; + } + + return mapped_rgba_pixels; +} + +void OpenGLDisplayDriver::unmap_texture_buffer() +{ + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +OpenGLDisplayDriver::GraphicsInterop OpenGLDisplayDriver::graphics_interop_get() +{ + GraphicsInterop interop_dst; + + interop_dst.buffer_width = texture_.buffer_width; + interop_dst.buffer_height = texture_.buffer_height; + interop_dst.opengl_pbo_id = texture_.gl_pbo_id; + + interop_dst.need_clear = texture_.need_clear; + texture_.need_clear = false; + + return interop_dst; +} + +void OpenGLDisplayDriver::graphics_interop_activate() +{ + gl_context_enable_(); +} + +void OpenGLDisplayDriver::graphics_interop_deactivate() +{ + gl_context_disable_(); +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +void OpenGLDisplayDriver::clear() +{ + texture_.need_clear = true; +} + +void OpenGLDisplayDriver::draw(const Params ¶ms) +{ + /* See do_update_begin() for why no locking is required here. */ + if (texture_.need_clear) { + /* Texture is requested to be cleared and was not yet cleared. + * Do early return which should be equivalent of drawing all-zero texture. */ + return; + } + + if (!gl_draw_resources_ensure()) { + return; + } + + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + display_shader_.bind(params.full_size.x, params.full_size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + + if (texture_.width != params.size.x || texture_.height != params.size.y) { + /* Resolution divider is different from 1, force nearest interpolation. */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); + + texture_update_if_needed(); + vertex_buffer_update(params); + + GLuint vertex_array_object; + glGenVertexArrays(1, &vertex_array_object); + glBindVertexArray(vertex_array_object); + + const int texcoord_attribute = display_shader_.get_tex_coord_attrib_location(); + const int position_attribute = display_shader_.get_position_attrib_location(); + + glEnableVertexAttribArray(texcoord_attribute); + glEnableVertexAttribArray(position_attribute); + + glVertexAttribPointer( + texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); + glVertexAttribPointer(position_attribute, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(float), + (const GLvoid *)(sizeof(float) * 2)); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glDeleteVertexArrays(1, &vertex_array_object); + + display_shader_.unbind(); + + glDisable(GL_BLEND); + + gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); +} + +bool OpenGLDisplayDriver::gl_draw_resources_ensure() +{ + if (!texture_.gl_id) { + /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can + * can not continue. Note that this is not an unrecoverable error, so once the texture is known + * we will come back here and create all the GPU resources needed for draw. */ + return false; + } + + if (gl_draw_resource_creation_attempted_) { + return gl_draw_resources_created_; + } + gl_draw_resource_creation_attempted_ = true; + + if (!vertex_buffer_) { + glGenBuffers(1, &vertex_buffer_); + if (!vertex_buffer_) { + LOG(ERROR) << "Error creating vertex buffer."; + return false; + } + } + + gl_draw_resources_created_ = true; + + return true; +} + +void OpenGLDisplayDriver::gl_resources_destroy() +{ + gl_context_enable_(); + + if (vertex_buffer_ != 0) { + glDeleteBuffers(1, &vertex_buffer_); + } + + if (texture_.gl_pbo_id) { + glDeleteBuffers(1, &texture_.gl_pbo_id); + texture_.gl_pbo_id = 0; + } + + if (texture_.gl_id) { + glDeleteTextures(1, &texture_.gl_id); + texture_.gl_id = 0; + } + + gl_context_disable_(); +} + +bool OpenGLDisplayDriver::gl_texture_resources_ensure() +{ + if (texture_.creation_attempted) { + return texture_.is_created; + } + texture_.creation_attempted = true; + + DCHECK(!texture_.gl_id); + DCHECK(!texture_.gl_pbo_id); + + /* Create texture. */ + glGenTextures(1, &texture_.gl_id); + if (!texture_.gl_id) { + LOG(ERROR) << "Error creating texture."; + return false; + } + + /* Configure the texture. */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Create PBO for the texture. */ + glGenBuffers(1, &texture_.gl_pbo_id); + if (!texture_.gl_pbo_id) { + LOG(ERROR) << "Error creating texture pixel buffer object."; + return false; + } + + /* Creation finished with a success. */ + texture_.is_created = true; + + return true; +} + +void OpenGLDisplayDriver::texture_update_if_needed() +{ + if (!texture_.need_update) { + return; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.need_update = false; +} + +void OpenGLDisplayDriver::vertex_buffer_update(const Params ¶ms) +{ + /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be + * rendered. */ + glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); + + float *vpointer = reinterpret_cast<float *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + if (!vpointer) { + return; + } + + vpointer[0] = 0.0f; + vpointer[1] = 0.0f; + vpointer[2] = params.full_offset.x; + vpointer[3] = params.full_offset.y; + + vpointer[4] = 1.0f; + vpointer[5] = 0.0f; + vpointer[6] = (float)params.size.x + params.full_offset.x; + vpointer[7] = params.full_offset.y; + + vpointer[8] = 1.0f; + vpointer[9] = 1.0f; + vpointer[10] = (float)params.size.x + params.full_offset.x; + vpointer[11] = (float)params.size.y + params.full_offset.y; + + vpointer[12] = 0.0f; + vpointer[13] = 1.0f; + vpointer[14] = params.full_offset.x; + vpointer[15] = (float)params.size.y + params.full_offset.y; + + glUnmapBuffer(GL_ARRAY_BUFFER); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/opengl/display_driver.h b/intern/cycles/app/opengl/display_driver.h new file mode 100644 index 00000000000..92578412d68 --- /dev/null +++ b/intern/cycles/app/opengl/display_driver.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include <atomic> + +#include "app/opengl/shader.h" + +#include "session/display_driver.h" + +#include "util/function.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class OpenGLDisplayDriver : public DisplayDriver { + public: + /* Callbacks for enabling and disabling the OpenGL context. Must be provided to support enabling + * the context on the Cycles render thread independent of the main thread. */ + OpenGLDisplayDriver(const function<bool()> &gl_context_enable, + const function<void()> &gl_context_disable); + ~OpenGLDisplayDriver(); + + virtual void graphics_interop_activate() override; + virtual void graphics_interop_deactivate() override; + + virtual void clear() override; + + void set_zoom(float zoom_x, float zoom_y); + + protected: + virtual void next_tile_begin() override; + + virtual bool update_begin(const Params ¶ms, int texture_width, int texture_height) override; + virtual void update_end() override; + + virtual half4 *map_texture_buffer() override; + virtual void unmap_texture_buffer() override; + + virtual GraphicsInterop graphics_interop_get() override; + + virtual void draw(const Params ¶ms) override; + + /* Make sure texture is allocated and its initial configuration is performed. */ + bool gl_texture_resources_ensure(); + + /* Ensure all runtime GPU resources needed for drawing are allocated. + * Returns true if all resources needed for drawing are available. */ + bool gl_draw_resources_ensure(); + + /* Destroy all GPU resources which are being used by this object. */ + void gl_resources_destroy(); + + /* Update GPU texture dimensions and content if needed (new pixel data was provided). + * + * NOTE: The texture needs to be bound. */ + void texture_update_if_needed(); + + /* Update vertex buffer with new coordinates of vertex positions and texture coordinates. + * This buffer is used to render texture in the viewport. + * + * NOTE: The buffer needs to be bound. */ + void vertex_buffer_update(const Params ¶ms); + + /* Texture which contains pixels of the render result. */ + struct { + /* Indicates whether texture creation was attempted and succeeded. + * Used to avoid multiple attempts of texture creation on GPU issues or GPU context + * misconfiguration. */ + bool creation_attempted = false; + bool is_created = false; + + /* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write + * pixels to it. + * + * NOTE: Allocated on the engine's context. */ + uint gl_id = 0; + uint gl_pbo_id = 0; + + /* Is true when new data was written to the PBO, meaning, the texture might need to be resized + * and new data is to be uploaded to the GPU. */ + bool need_update = false; + + /* Content of the texture is to be filled with zeroes. */ + std::atomic<bool> need_clear = true; + + /* Dimensions of the texture in pixels. */ + int width = 0; + int height = 0; + + /* Dimensions of the underlying PBO. */ + int buffer_width = 0; + int buffer_height = 0; + } texture_; + + OpenGLShader display_shader_; + + /* Special track of whether GPU resources were attempted to be created, to avoid attempts of + * their re-creation on failure on every redraw. */ + bool gl_draw_resource_creation_attempted_ = false; + bool gl_draw_resources_created_ = false; + + /* Vertex buffer which hold vertices of a triangle fan which is textures with the texture + * holding the render result. */ + uint vertex_buffer_ = 0; + + void *gl_render_sync_ = nullptr; + void *gl_upload_sync_ = nullptr; + + float2 zoom_ = make_float2(1.0f, 1.0f); + + function<bool()> gl_context_enable_ = nullptr; + function<void()> gl_context_disable_ = nullptr; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/opengl/shader.cpp b/intern/cycles/app/opengl/shader.cpp new file mode 100644 index 00000000000..9db9ea7fce9 --- /dev/null +++ b/intern/cycles/app/opengl/shader.cpp @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include "app/opengl/shader.h" + +#include "util/log.h" +#include "util/string.h" + +#include <GL/glew.h> + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * OpenGLShader. + */ + +static const char *VERTEX_SHADER = + "#version 330\n" + "uniform vec2 fullscreen;\n" + "in vec2 texCoord;\n" + "in vec2 pos;\n" + "out vec2 texCoord_interp;\n" + "\n" + "vec2 normalize_coordinates()\n" + "{\n" + " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n" + "}\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n" + " texCoord_interp = texCoord;\n" + "}\n\0"; + +static const char *FRAGMENT_SHADER = + "#version 330\n" + "uniform sampler2D image_texture;\n" + "in vec2 texCoord_interp;\n" + "out vec4 fragColor;\n" + "\n" + "void main()\n" + "{\n" + " vec4 rgba = texture(image_texture, texCoord_interp);\n" + /* Harcoded Rec.709 gamma, should use OpenColorIO eventually. */ + " fragColor = pow(rgba, vec4(0.45, 0.45, 0.45, 1.0));\n" + "}\n\0"; + +static void shader_print_errors(const char *task, const char *log, const char *code) +{ + LOG(ERROR) << "Shader: " << task << " error:"; + LOG(ERROR) << "===== shader string ===="; + + stringstream stream(code); + string partial; + + int line = 1; + while (getline(stream, partial, '\n')) { + if (line < 10) { + LOG(ERROR) << " " << line << " " << partial; + } + else { + LOG(ERROR) << line << " " << partial; + } + line++; + } + LOG(ERROR) << log; +} + +static int compile_shader_program(void) +{ + const struct Shader { + const char *source; + const GLenum type; + } shaders[2] = {{VERTEX_SHADER, GL_VERTEX_SHADER}, {FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; + + const GLuint program = glCreateProgram(); + + for (int i = 0; i < 2; i++) { + const GLuint shader = glCreateShader(shaders[i].type); + + string source_str = shaders[i].source; + const char *c_str = source_str.c_str(); + + glShaderSource(shader, 1, &c_str, NULL); + glCompileShader(shader); + + GLint compile_status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + + if (!compile_status) { + GLchar log[5000]; + GLsizei length = 0; + glGetShaderInfoLog(shader, sizeof(log), &length, log); + shader_print_errors("compile", log, c_str); + return 0; + } + + glAttachShader(program, shader); + } + + /* Link output. */ + glBindFragDataLocation(program, 0, "fragColor"); + + /* Link and error check. */ + glLinkProgram(program); + + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLchar log[5000]; + GLsizei length = 0; + glGetShaderInfoLog(program, sizeof(log), &length, log); + shader_print_errors("linking", log, VERTEX_SHADER); + shader_print_errors("linking", log, FRAGMENT_SHADER); + return 0; + } + + return program; +} + +int OpenGLShader::get_position_attrib_location() +{ + if (position_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name); + } + return position_attribute_location_; +} + +int OpenGLShader::get_tex_coord_attrib_location() +{ + if (tex_coord_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name); + } + return tex_coord_attribute_location_; +} + +void OpenGLShader::bind(int width, int height) +{ + create_shader_if_needed(); + + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + glUniform1i(image_texture_location_, 0); + glUniform2f(fullscreen_location_, width, height); +} + +void OpenGLShader::unbind() +{ +} + +uint OpenGLShader::get_shader_program() +{ + return shader_program_; +} + +void OpenGLShader::create_shader_if_needed() +{ + if (shader_program_ || shader_compile_attempted_) { + return; + } + + shader_compile_attempted_ = true; + + shader_program_ = compile_shader_program(); + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + + image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture"); + if (image_texture_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; + destroy_shader(); + return; + } + + fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen"); + if (fullscreen_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; + destroy_shader(); + return; + } +} + +void OpenGLShader::destroy_shader() +{ + glDeleteProgram(shader_program_); + shader_program_ = 0; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/opengl/shader.h b/intern/cycles/app/opengl/shader.h new file mode 100644 index 00000000000..6ca121ca6ff --- /dev/null +++ b/intern/cycles/app/opengl/shader.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 OpenGL Foundation */ + +#pragma once + +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +class OpenGLShader { + public: + static constexpr const char *position_attribute_name = "pos"; + static constexpr const char *tex_coord_attribute_name = "texCoord"; + + OpenGLShader() = default; + virtual ~OpenGLShader() = default; + + /* Get attribute location for position and texture coordinate respectively. + * NOTE: The shader needs to be bound to have access to those. */ + int get_position_attrib_location(); + int get_tex_coord_attrib_location(); + + void bind(int width, int height); + void unbind(); + + protected: + uint get_shader_program(); + + void create_shader_if_needed(); + void destroy_shader(); + + /* Cached values of various OpenGL resources. */ + int position_attribute_location_ = -1; + int tex_coord_attribute_location_ = -1; + + uint shader_program_ = 0; + int image_texture_location_ = -1; + int fullscreen_location_ = -1; + + /* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or + * linking has failed. Do not attempt to re-compile the shader. */ + bool shader_compile_attempted_ = false; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/opengl/window.cpp b/intern/cycles/app/opengl/window.cpp new file mode 100644 index 00000000000..7351ae3eecd --- /dev/null +++ b/intern/cycles/app/opengl/window.cpp @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include <stdio.h> +#include <stdlib.h> + +#include "app/opengl/window.h" + +#include "util/string.h" +#include "util/thread.h" +#include "util/time.h" +#include "util/version.h" + +#include <GL/glew.h> +#include <SDL.h> + +CCL_NAMESPACE_BEGIN + +/* structs */ + +struct Window { + WindowInitFunc initf = nullptr; + WindowExitFunc exitf = nullptr; + WindowResizeFunc resize = nullptr; + WindowDisplayFunc display = nullptr; + WindowKeyboardFunc keyboard = nullptr; + WindowMotionFunc motion = nullptr; + + bool first_display = true; + bool redraw = false; + + int mouseX = 0, mouseY = 0; + int mouseBut0 = 0, mouseBut2 = 0; + + int width = 0, height = 0; + + SDL_Window *window = nullptr; + SDL_GLContext gl_context = nullptr; + thread_mutex gl_context_mutex; +} V; + +/* public */ + +static void window_display_text(int x, int y, const char *text) +{ +/* Not currently supported, need to add text rendering support. */ +#if 0 + const char *c; + + glRasterPos3f(x, y, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + printf("display %s\n", text); + + for (c = text; *c != '\0'; c++) { + const uint8_t *bitmap = helvetica10_character_map[*c]; + glBitmap(bitmap[0], + helvetica10_height, + helvetica10_x_offset, + helvetica10_y_offset, + bitmap[0], + 0.0f, + bitmap + 1); + } +#else + static string last_text = ""; + + if (text != last_text) { + printf("%s\n", text); + last_text = text; + } +#endif +} + +void window_display_info(const char *info) +{ + const int height = 20; + +#if 0 + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.1f, 0.1f, 0.1f, 0.8f); + glRectf(0.0f, V.height - height, V.width, V.height); + glDisable(GL_BLEND); + + glColor3f(0.5f, 0.5f, 0.5f); +#endif + + window_display_text(10, 7 + V.height - height, info); + +#if 0 + glColor3f(1.0f, 1.0f, 1.0f); +#endif +} + +void window_display_help() +{ + const int w = (int)((float)V.width / 1.15f); + const int h = (int)((float)V.height / 1.15f); + + const int x1 = (V.width - w) / 2; +#if 0 + const int x2 = x1 + w; +#endif + + const int y1 = (V.height - h) / 2; + const int y2 = y1 + h; + +#if 0 + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.5f, 0.5f, 0.5f, 0.8f); + glRectf(x1, y1, x2, y2); + glDisable(GL_BLEND); + + glColor3f(0.8f, 0.8f, 0.8f); +#endif + + string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING; + + window_display_text(x1 + 20, y2 - 20, info.c_str()); + window_display_text(x1 + 20, y2 - 40, "(C) 2011-2016 Blender Foundation"); + window_display_text(x1 + 20, y2 - 80, "Controls:"); + window_display_text(x1 + 20, y2 - 100, "h: Info/Help"); + window_display_text(x1 + 20, y2 - 120, "r: Reset"); + window_display_text(x1 + 20, y2 - 140, "p: Pause"); + window_display_text(x1 + 20, y2 - 160, "esc: Cancel"); + window_display_text(x1 + 20, y2 - 180, "q: Quit program"); + + window_display_text(x1 + 20, y2 - 210, "i: Interactive mode"); + window_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera"); + window_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera"); + window_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera"); + window_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces"); + +#if 0 + glColor3f(1.0f, 1.0f, 1.0f); +#endif +} + +static void window_display() +{ + if (V.first_display) { + if (V.initf) { + V.initf(); + } + if (V.exitf) { + atexit(V.exitf); + } + + V.first_display = false; + } + + window_opengl_context_enable(); + + glViewport(0, 0, V.width, V.height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glClearColor(0.05f, 0.05f, 0.05f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, V.width, 0, V.height, -1, 1); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glRasterPos3f(0, 0, 0); + + if (V.display) + V.display(); + + SDL_GL_SwapWindow(V.window); + window_opengl_context_disable(); +} + +static void window_reshape(int width, int height) +{ + if (V.width != width || V.height != height) { + if (V.resize) { + V.resize(width, height); + } + } + + V.width = width; + V.height = height; +} + +static bool window_keyboard(unsigned char key) +{ + if (V.keyboard) + V.keyboard(key); + + if (key == 'q') { + if (V.exitf) + V.exitf(); + return true; + } + + return false; +} + +static void window_mouse(int button, int state, int x, int y) +{ + if (button == SDL_BUTTON_LEFT) { + if (state == SDL_MOUSEBUTTONDOWN) { + V.mouseX = x; + V.mouseY = y; + V.mouseBut0 = 1; + } + else if (state == SDL_MOUSEBUTTONUP) { + V.mouseBut0 = 0; + } + } + else if (button == SDL_BUTTON_RIGHT) { + if (state == SDL_MOUSEBUTTONDOWN) { + V.mouseX = x; + V.mouseY = y; + V.mouseBut2 = 1; + } + else if (state == SDL_MOUSEBUTTONUP) { + V.mouseBut2 = 0; + } + } +} + +static void window_motion(int x, int y) +{ + const int but = V.mouseBut0 ? 0 : 2; + const int distX = x - V.mouseX; + const int distY = y - V.mouseY; + + if (V.motion) + V.motion(distX, distY, but); + + V.mouseX = x; + V.mouseY = y; +} + +bool window_opengl_context_enable() +{ + V.gl_context_mutex.lock(); + SDL_GL_MakeCurrent(V.window, V.gl_context); + return true; +} + +void window_opengl_context_disable() +{ + SDL_GL_MakeCurrent(V.window, nullptr); + V.gl_context_mutex.unlock(); +} + +void window_main_loop(const char *title, + int width, + int height, + WindowInitFunc initf, + WindowExitFunc exitf, + WindowResizeFunc resize, + WindowDisplayFunc display, + WindowKeyboardFunc keyboard, + WindowMotionFunc motion) +{ + V.width = width; + V.height = height; + V.first_display = true; + V.redraw = false; + V.initf = initf; + V.exitf = exitf; + V.resize = resize; + V.display = display; + V.keyboard = keyboard; + V.motion = motion; + + SDL_Init(SDL_INIT_VIDEO); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + V.window = SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, + height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); + if (V.window == nullptr) { + fprintf(stderr, "Failed to create window: %s\n", SDL_GetError()); + return; + } + + SDL_RaiseWindow(V.window); + + V.gl_context = SDL_GL_CreateContext(V.window); + glewInit(); + SDL_GL_MakeCurrent(V.window, nullptr); + + window_reshape(width, height); + window_display(); + + while (true) { + bool quit = false; + SDL_Event event; + while (!quit && SDL_PollEvent(&event)) { + if (event.type == SDL_TEXTINPUT) { + quit = window_keyboard(event.text.text[0]); + } + else if (event.type == SDL_MOUSEMOTION) { + window_motion(event.motion.x, event.motion.y); + } + else if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) { + window_mouse(event.button.button, event.button.state, event.button.x, event.button.y); + } + else if (event.type == SDL_WINDOWEVENT) { + if (event.window.event == SDL_WINDOWEVENT_RESIZED || + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { + window_reshape(event.window.data1, event.window.data2); + } + } + else if (event.type == SDL_QUIT) { + if (V.exitf) { + V.exitf(); + } + quit = true; + } + } + + if (quit) { + break; + } + + if (V.redraw) { + V.redraw = false; + window_display(); + } + + SDL_WaitEventTimeout(NULL, 100); + } + + SDL_GL_DeleteContext(V.gl_context); + SDL_DestroyWindow(V.window); + SDL_Quit(); +} + +void window_redraw() +{ + V.redraw = true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/opengl/window.h b/intern/cycles/app/opengl/window.h new file mode 100644 index 00000000000..531b5cab3fc --- /dev/null +++ b/intern/cycles/app/opengl/window.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +/* Functions to display a simple OpenGL window using SDL, simplified to the + * bare minimum we need to reduce boilerplate code in tests apps. */ + +CCL_NAMESPACE_BEGIN + +typedef void (*WindowInitFunc)(); +typedef void (*WindowExitFunc)(); +typedef void (*WindowResizeFunc)(int width, int height); +typedef void (*WindowDisplayFunc)(); +typedef void (*WindowKeyboardFunc)(unsigned char key); +typedef void (*WindowMotionFunc)(int x, int y, int button); + +void window_main_loop(const char *title, + int width, + int height, + WindowInitFunc initf, + WindowExitFunc exitf, + WindowResizeFunc resize, + WindowDisplayFunc display, + WindowKeyboardFunc keyboard, + WindowMotionFunc motion); + +void window_display_info(const char *info); +void window_display_help(); +void window_redraw(); + +bool window_opengl_context_enable(); +void window_opengl_context_disable(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 4e62bae6fe3..e3e5734c6b6 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1514,9 +1514,12 @@ class CyclesPreferences(bpy.types.AddonPreferences): row.prop(self, "peer_memory") if compute_device_type == 'METAL': - row = layout.row() - row.use_property_split = True - row.prop(self, "use_metalrt") + import platform + # MetalRT only works on Apple Silicon at present, pending argument encoding fixes on AMD + if platform.machine() == 'arm64': + row = layout.row() + row.use_property_split = True + row.prop(self, "use_metalrt") def draw(self, context): diff --git a/intern/cycles/blender/session.cpp b/intern/cycles/blender/session.cpp index 8917c703700..5e9066da5de 100644 --- a/intern/cycles/blender/session.cpp +++ b/intern/cycles/blender/session.cpp @@ -493,8 +493,13 @@ void BlenderSession::render_frame_finish() session->set_output_driver(nullptr); session->full_buffer_written_cb = function_null; - /* The display driver holds OpenGL resources which belong to an OpenGL context held by the render - * engine on Blender side. Force destruction of those resources. */ + /* The display driver is the source of drawing context for both drawing and possible graphics + * interop objects in the path trace. Once the frame is finished the OpenGL context might be + * freed form Blender side. Need to ensure that all GPU resources are freed prior to that + * point. + * Ideally would only do this when OpenGL context is actually destroyed, but there is no way to + * know when this happens (at least in the code at the time when this comment was written). + * The penalty of re-creating resources on every frame is unlikely to be noticed. */ display_driver_ = nullptr; session->set_display_driver(nullptr); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 9de507966d8..ec50ad9db9a 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -32,7 +32,8 @@ typedef map<string, ConvertNode *> ProxyMap; void BlenderSync::find_shader(BL::ID &id, array<Node *> &used_shaders, Shader *default_shader) { - Shader *shader = (id) ? shader_map.find(id) : default_shader; + Shader *synced_shader = (id) ? shader_map.find(id) : nullptr; + Shader *shader = (synced_shader) ? synced_shader : default_shader; used_shaders.push_back_slow(shader); shader->tag_used(scene); @@ -1573,18 +1574,13 @@ void BlenderSync::sync_lights(BL::Depsgraph &b_depsgraph, bool update_all) } } -void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d) +void BlenderSync::sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all) { - /* for auto refresh images */ - ImageManager *image_manager = scene->image_manager; - const int frame = b_scene.frame_current(); - const bool auto_refresh_update = image_manager->set_animation_frame_update(frame); - shader_map.pre_sync(); - sync_world(b_depsgraph, b_v3d, auto_refresh_update); - sync_lights(b_depsgraph, auto_refresh_update); - sync_materials(b_depsgraph, auto_refresh_update); + sync_world(b_depsgraph, b_v3d, update_all); + sync_lights(b_depsgraph, update_all); + sync_materials(b_depsgraph, update_all); } CCL_NAMESPACE_END diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 0b11af2dbf9..d4949a5ff30 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -246,7 +246,12 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, int height, void **python_thread_state) { - if (!has_updates_) { + /* For auto refresh images. */ + ImageManager *image_manager = scene->image_manager; + const int frame = b_scene.frame_current(); + const bool auto_refresh_update = image_manager->set_animation_frame_update(frame); + + if (!has_updates_ && !auto_refresh_update) { return; } @@ -261,7 +266,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, sync_view_layer(b_view_layer); sync_integrator(b_view_layer, background); sync_film(b_view_layer, b_v3d); - sync_shaders(b_depsgraph, b_v3d); + sync_shaders(b_depsgraph, b_v3d, auto_refresh_update); sync_images(); geometry_synced.clear(); /* use for objects and motion sync */ diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index d92efb80a5d..5cc18452ac1 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -114,7 +114,7 @@ class BlenderSync { /* Shader */ array<Node *> find_used_shaders(BL::Object &b_ob); void sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all); - void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d); + void sync_shaders(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all); void sync_nodes(Shader *shader, BL::ShaderNodeTree &b_ntree); /* Object */ diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index c964fbe0d72..6ad64d684c0 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -479,26 +479,22 @@ else() endif() ########################################################################### -# GLUT +# SDL ########################################################################### if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) - if(MSVC AND EXISTS ${_cycles_lib_dir}) - add_definitions(-DFREEGLUT_STATIC -DFREEGLUT_LIB_PRAGMAS=0) - set(GLUT_LIBRARIES "${_cycles_lib_dir}/opengl/lib/freeglut_static.lib") - set(GLUT_INCLUDE_DIR "${_cycles_lib_dir}/opengl/include") - else() - find_package(GLUT) + # We can't use the version from the Blender precompiled libraries because + # it does not include the video subsystem. + find_package(SDL2) - if(NOT GLUT_FOUND) - set(WITH_CYCLES_STANDALONE_GUI OFF) - message(STATUS "GLUT not found, disabling Cycles standalone GUI") - endif() + if(NOT SDL2_FOUND) + set(WITH_CYCLES_STANDALONE_GUI OFF) + message(STATUS "SDL not found, disabling Cycles standalone GUI") endif() include_directories( SYSTEM - ${GLUT_INCLUDE_DIR} + ${SDL2_INCLUDE_DIRS} ) endif() diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp index 158a789db5a..612c391f7d5 100644 --- a/intern/cycles/device/cpu/device_impl.cpp +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -191,7 +191,7 @@ device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_ void CPUDevice::const_copy_to(const char *name, void *host, size_t size) { -#if WITH_EMBREE +#ifdef WITH_EMBREE if (strcmp(name, "__data") == 0) { assert(size <= sizeof(KernelData)); diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h index 00269ac287c..9afef3789af 100644 --- a/intern/cycles/device/hip/device_impl.h +++ b/intern/cycles/device/hip/device_impl.h @@ -12,8 +12,6 @@ # ifdef WITH_HIP_DYNLOAD # include "hipew.h" -# else -# include "util/opengl.h" # endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm index 593c9c3cf06..c01f51fb506 100644 --- a/intern/cycles/device/metal/device_impl.mm +++ b/intern/cycles/device/metal/device_impl.mm @@ -77,11 +77,11 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile } case METAL_GPU_APPLE: { max_threads_per_threadgroup = 512; + use_metalrt = info.use_metalrt; break; } } - use_metalrt = info.use_metalrt; if (auto metalrt = getenv("CYCLES_METALRT")) { use_metalrt = (atoi(metalrt) != 0); } diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp index 220d4c9ffa2..eb12b0a6a11 100644 --- a/intern/cycles/integrator/path_trace.cpp +++ b/intern/cycles/integrator/path_trace.cpp @@ -54,14 +54,7 @@ PathTrace::PathTrace(Device *device, PathTrace::~PathTrace() { - /* Destroy any GPU resource which was used for graphics interop. - * Need to have access to the PathTraceDisplay as it is the only source of drawing context which - * is used for interop. */ - if (display_) { - for (auto &&path_trace_work : path_trace_works_) { - path_trace_work->destroy_gpu_resources(display_.get()); - } - } + destroy_gpu_resources(); } void PathTrace::load_kernels() @@ -559,6 +552,11 @@ void PathTrace::set_output_driver(unique_ptr<OutputDriver> driver) void PathTrace::set_display_driver(unique_ptr<DisplayDriver> driver) { + /* The display driver is the source of the drawing context which might be used by + * path trace works. Make sure there is no graphics interop using resources from + * the old display, as it might no longer be available after this call. */ + destroy_gpu_resources(); + if (driver) { display_ = make_unique<PathTraceDisplay>(move(driver)); } @@ -1075,6 +1073,18 @@ bool PathTrace::has_denoised_result() const return render_state_.has_denoised_result; } +void PathTrace::destroy_gpu_resources() +{ + /* Destroy any GPU resource which was used for graphics interop. + * Need to have access to the PathTraceDisplay as it is the only source of drawing context which + * is used for interop. */ + if (display_) { + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->destroy_gpu_resources(display_.get()); + } + } +} + /* -------------------------------------------------------------------- * Report generation. */ diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h index 1be5ce847bc..a470a6e1402 100644 --- a/intern/cycles/integrator/path_trace.h +++ b/intern/cycles/integrator/path_trace.h @@ -226,6 +226,9 @@ class PathTrace { void progress_set_status(const string &status, const string &substatus = ""); + /* Destroy GPU resources (such as graphics interop) used by work. */ + void destroy_gpu_resources(); + /* Pointer to a device which is configured to be used for path tracing. If multiple devices * are configured this is a `MultiDevice`. */ Device *device_ = nullptr; diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp index fe4697e082b..90a5a01320b 100644 --- a/intern/cycles/integrator/render_scheduler.cpp +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -244,7 +244,7 @@ void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work) render_work.tile.write = tile_write; render_work.full.write = full_write; - /* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which + /* Do not write tile if it has zero samples in it, treat it similarly to all other tiles which * got canceled. */ if (!state_.tile_result_was_written && has_rendered_samples) { render_work.tile.write = true; diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h index 404f65e98a1..dce876d44bd 100644 --- a/intern/cycles/integrator/render_scheduler.h +++ b/intern/cycles/integrator/render_scheduler.h @@ -124,7 +124,7 @@ class RenderScheduler { /* Get sample up to which rendering has been done. * This is an absolute 0-based value. * - * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * For example, if start sample is 10 and 5 samples were rendered, then this call will * return 14. * * If there were no samples rendered, then the behavior is undefined. */ @@ -132,7 +132,7 @@ class RenderScheduler { /* Get number of samples rendered within the current scheduling session. * - * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * For example, if start sample is 10 and 5 samples were rendered, then this call will * return 5. * * Note that this is based on the scheduling information. In practice this means that if someone diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h index 1fd3a3f2850..71045157372 100644 --- a/intern/cycles/kernel/bvh/util.h +++ b/intern/cycles/kernel/bvh/util.h @@ -5,27 +5,6 @@ CCL_NAMESPACE_BEGIN -/* Ray offset to avoid self intersection. - * - * This function should be used to compute a modified ray start position for - * rays leaving from a surface. This is from "A Fast and Robust Method for Avoiding - * Self-Intersection" see https://research.nvidia.com/publication/2019-03_A-Fast-and - */ -ccl_device_inline float3 ray_offset(float3 P, float3 Ng) -{ - const float int_scale = 256.0f; - int3 of_i = make_int3((int)(int_scale * Ng.x), (int)(int_scale * Ng.y), (int)(int_scale * Ng.z)); - - float3 p_i = make_float3(__int_as_float(__float_as_int(P.x) + ((P.x < 0) ? -of_i.x : of_i.x)), - __int_as_float(__float_as_int(P.y) + ((P.y < 0) ? -of_i.y : of_i.y)), - __int_as_float(__float_as_int(P.z) + ((P.z < 0) ? -of_i.z : of_i.z))); - const float origin = 1.0f / 32.0f; - const float float_scale = 1.0f / 65536.0f; - return make_float3(fabsf(P.x) < origin ? P.x + float_scale * Ng.x : p_i.x, - fabsf(P.y) < origin ? P.y + float_scale * Ng.y : p_i.y, - fabsf(P.z) < origin ? P.z + float_scale * Ng.z : p_i.z); -} - #if defined(__KERNEL_CPU__) ccl_device int intersections_compare(const void *a, const void *b) { diff --git a/intern/cycles/kernel/geom/point.h b/intern/cycles/kernel/geom/point.h index f7c6cb86c5e..041ecb3c2cf 100644 --- a/intern/cycles/kernel/geom/point.h +++ b/intern/cycles/kernel/geom/point.h @@ -128,9 +128,10 @@ ccl_device float point_radius(KernelGlobals kg, ccl_private const ShaderData *sd return r; } else { - float3 dir = make_float3(r, r, r); + const float normalized_r = r * (1.0f / M_SQRT3_F); + float3 dir = make_float3(normalized_r, normalized_r, normalized_r); object_dir_transform(kg, sd, &dir); - return average(dir); + return len(dir); } } diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h index e616123e9e7..b84059d6676 100644 --- a/intern/cycles/kernel/integrator/init_from_bake.h +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -30,6 +30,50 @@ ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) return ((((int)fu) & 1) ? 1.0f - u : u) * max; } +/* Offset towards center of triangle to avoid ray-tracing precision issues. */ +ccl_device const float2 bake_offset_towards_center(KernelGlobals kg, + const int prim, + const float u, + const float v) +{ + float3 tri_verts[3]; + triangle_vertices(kg, prim, tri_verts); + + /* Empirically determined values, by no means perfect. */ + const float position_offset = 1e-4f; + const float uv_offset = 1e-5f; + + /* Offset position towards center, amount relative to absolute size of position coordinates. */ + const float3 P = u * tri_verts[0] + v * tri_verts[1] + (1.0f - u - v) * tri_verts[2]; + const float3 center = (tri_verts[0] + tri_verts[1] + tri_verts[2]) / 3.0f; + const float3 to_center = center - P; + + const float3 offset_P = P + normalize(to_center) * + min(len(to_center), max(max3(fabs(P)), 1.0f) * position_offset); + + /* Compute barycentric coordinates at new position. */ + const float3 v1 = tri_verts[1] - tri_verts[0]; + const float3 v2 = tri_verts[2] - tri_verts[0]; + const float3 vP = offset_P - tri_verts[0]; + + const float d11 = dot(v1, v1); + const float d12 = dot(v1, v2); + const float d22 = dot(v2, v2); + const float dP1 = dot(vP, v1); + const float dP2 = dot(vP, v2); + + const float denom = d11 * d22 - d12 * d12; + if (denom == 0.0f) { + return make_float2(0.0f, 0.0f); + } + + const float offset_v = clamp((d22 * dP1 - d12 * dP2) / denom, uv_offset, 1.0f - uv_offset); + const float offset_w = clamp((d11 * dP2 - d12 * dP1) / denom, uv_offset, 1.0f - uv_offset); + const float offset_u = clamp(1.0f - offset_v - offset_w, uv_offset, 1.0f - uv_offset); + + return make_float2(offset_u, offset_v); +} + /* Return false to indicate that this pixel is finished. * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known * that the pixel did converge. */ @@ -87,7 +131,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, /* Initialize path state for path integration. */ path_state_init_integrator(kg, state, sample, rng_hash); - /* Barycentric UV with sub-pixel offset. */ + /* Barycentric UV. */ float u = primitive[2]; float v = primitive[3]; @@ -96,6 +140,14 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg, float dvdx = differential[2]; float dvdy = differential[3]; + /* Exactly at vertex? Nudge inwards to avoid self-intersection. */ + if ((u == 0.0f || u == 1.0f) && (v == 0.0f || v == 1.0f)) { + const float2 uv = bake_offset_towards_center(kg, prim, u, v); + u = uv.x; + v = uv.y; + } + + /* Sub-pixel offset. */ if (sample > 0) { u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f); v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f), diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index f548d91031d..d2442755646 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -352,12 +352,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, float ao_pdf; sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - if (!(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f)) { - return; - } - Ray ray ccl_optional_struct_init; - ray.P = sd->P; + ray.P = shadow_ray_offset(kg, sd, ao_D); ray.D = ao_D; ray.t = kernel_data.integrator.ao_bounces_distance; ray.time = sd->time; diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 16e76b37b0b..85bdb47600e 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1638,12 +1638,16 @@ bool OSLRenderServices::trace(TraceOpt &options, ray.D = TO_FLOAT3(R); ray.t = (options.maxdist == 1.0e30f) ? FLT_MAX : options.maxdist - options.mindist; ray.time = sd->time; + ray.self.object = OBJECT_NONE; + ray.self.prim = PRIM_NONE; + ray.self.light_object = OBJECT_NONE; + ray.self.light_prim = PRIM_NONE; if (options.mindist == 0.0f) { /* avoid self-intersections */ if (ray.P == sd->P) { - bool transmit = (dot(sd->Ng, ray.D) < 0.0f); - ray.P = ray_offset(sd->P, (transmit) ? -sd->Ng : sd->Ng); + ray.self.object = sd->object; + ray.self.prim = sd->prim; } } else { diff --git a/intern/cycles/kernel/svm/light_path.h b/intern/cycles/kernel/svm/light_path.h index dce0f83da68..7c2189d3608 100644 --- a/intern/cycles/kernel/svm/light_path.h +++ b/intern/cycles/kernel/svm/light_path.h @@ -58,8 +58,8 @@ ccl_device_noinline void svm_node_light_path(KernelGlobals kg, info = (float)integrator_state_bounce(state, path_flag); } - /* For background, light emission and shadow evaluation we from a - * surface or volume we are effective one bounce further. */ + /* For background, light emission and shadow evaluation from a + * surface or volume we are effectively one bounce further. */ if (path_flag & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { info += 1.0f; } diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp index dde250d5d78..8a08f2a5be9 100644 --- a/intern/cycles/scene/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -817,28 +817,28 @@ void ShaderManager::init_xyz_transforms() Transform xyz_to_rgb; if (config->hasRole("aces_interchange")) { - /* Standard OpenColorIO role, defined as ACES2065-1. */ - const Transform xyz_E_to_aces = make_transform(1.0498110175f, - 0.0f, - -0.0000974845f, - 0.0f, - -0.4959030231f, - 1.3733130458f, - 0.0982400361f, - 0.0f, - 0.0f, - 0.0f, - 0.9912520182f, - 0.0f); - const Transform xyz_D65_to_E = make_transform( - 1.0521111f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9184170f, 0.0f); - + /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */ Transform aces_to_rgb; if (!to_scene_linear_transform(config, "aces_interchange", aces_to_rgb)) { return; } - xyz_to_rgb = aces_to_rgb * xyz_E_to_aces * xyz_D65_to_E; + /* This is the OpenColorIO builtin transform: + * UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ + const Transform ACES_AP0_to_xyz_D65 = make_transform(0.938280f, + -0.004451f, + 0.016628f, + 0.000000f, + 0.337369f, + 0.729522f, + -0.066890f, + 0.000000f, + 0.001174f, + -0.003711f, + 1.091595f, + 0.000000f); + const Transform xyz_to_aces = transform_inverse(ACES_AP0_to_xyz_D65); + xyz_to_rgb = aces_to_rgb * xyz_to_aces; } else if (config->hasRole("XYZ")) { /* Custom role used before the standard existed. */ diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 0e348b1ac0f..fddac1dbbcf 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -7,7 +7,6 @@ set(INC ) set(INC_SYS - ${GLEW_INCLUDE_DIR} ) set(SRC @@ -34,14 +33,6 @@ set(LIB ${TBB_LIBRARIES} ) -if(WITH_CYCLES_STANDALONE) - if(WITH_CYCLES_STANDALONE_GUI) - list(APPEND SRC - view.cpp - ) - endif() -endif() - set(SRC_HEADERS algorithm.h aligned_malloc.h @@ -142,7 +133,6 @@ set(SRC_HEADERS unique_ptr.h vector.h version.h - view.h windows.h xml.h ) diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h index ed9f230398d..555a5304764 100644 --- a/intern/cycles/util/math.h +++ b/intern/cycles/util/math.h @@ -67,6 +67,9 @@ CCL_NAMESPACE_BEGIN #ifndef M_SQRT2_F # define M_SQRT2_F (1.4142135623730950f) /* sqrt(2) */ #endif +#ifndef M_SQRT3_F +# define M_SQRT3_F (1.7320508075688772f) /* sqrt(3) */ +#endif #ifndef M_LN2_F # define M_LN2_F (0.6931471805599453f) /* ln(2) */ #endif diff --git a/intern/cycles/util/view.cpp b/intern/cycles/util/view.cpp deleted file mode 100644 index 475f8dbcee8..00000000000 --- a/intern/cycles/util/view.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#include <stdio.h> -#include <stdlib.h> - -#include "util/opengl.h" -#include "util/string.h" -#include "util/time.h" -#include "util/version.h" -#include "util/view.h" - -#ifdef __APPLE__ -# include <GLUT/glut.h> -#else -# include <GL/glut.h> -#endif - -CCL_NAMESPACE_BEGIN - -/* structs */ - -struct View { - ViewInitFunc initf; - ViewExitFunc exitf; - ViewResizeFunc resize; - ViewDisplayFunc display; - ViewKeyboardFunc keyboard; - ViewMotionFunc motion; - - bool first_display; - bool redraw; - - int mouseX, mouseY; - int mouseBut0, mouseBut2; - - int width, height; -} V; - -/* public */ - -static void view_display_text(int x, int y, const char *text) -{ - const char *c; - - glRasterPos3f(x, y, 0); - - for (c = text; *c != '\0'; c++) - glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, *c); -} - -void view_display_info(const char *info) -{ - const int height = 20; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0.1f, 0.1f, 0.1f, 0.8f); - glRectf(0.0f, V.height - height, V.width, V.height); - glDisable(GL_BLEND); - - glColor3f(0.5f, 0.5f, 0.5f); - - view_display_text(10, 7 + V.height - height, info); - - glColor3f(1.0f, 1.0f, 1.0f); -} - -void view_display_help() -{ - const int w = (int)((float)V.width / 1.15f); - const int h = (int)((float)V.height / 1.15f); - - const int x1 = (V.width - w) / 2; - const int x2 = x1 + w; - - const int y1 = (V.height - h) / 2; - const int y2 = y1 + h; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0.5f, 0.5f, 0.5f, 0.8f); - glRectf(x1, y1, x2, y2); - glDisable(GL_BLEND); - - glColor3f(0.8f, 0.8f, 0.8f); - - string info = string("Cycles Renderer ") + CYCLES_VERSION_STRING; - - view_display_text(x1 + 20, y2 - 20, info.c_str()); - view_display_text(x1 + 20, y2 - 40, "(C) 2011-2022 Blender Foundation"); - view_display_text(x1 + 20, y2 - 80, "Controls:"); - view_display_text(x1 + 20, y2 - 100, "h: Info/Help"); - view_display_text(x1 + 20, y2 - 120, "r: Reset"); - view_display_text(x1 + 20, y2 - 140, "p: Pause"); - view_display_text(x1 + 20, y2 - 160, "esc: Cancel"); - view_display_text(x1 + 20, y2 - 180, "q: Quit program"); - - view_display_text(x1 + 20, y2 - 210, "i: Interactive mode"); - view_display_text(x1 + 20, y2 - 230, "Left mouse: Move camera"); - view_display_text(x1 + 20, y2 - 250, "Right mouse: Rotate camera"); - view_display_text(x1 + 20, y2 - 270, "W/A/S/D: Move camera"); - view_display_text(x1 + 20, y2 - 290, "0/1/2/3: Set max bounces"); - - glColor3f(1.0f, 1.0f, 1.0f); -} - -static void view_display() -{ - if (V.first_display) { - if (V.initf) - V.initf(); - if (V.exitf) - atexit(V.exitf); - - V.first_display = false; - } - - glClearColor(0.05f, 0.05f, 0.05f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, V.width, 0, V.height, -1, 1); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glRasterPos3f(0, 0, 0); - - if (V.display) - V.display(); - - glutSwapBuffers(); -} - -static void view_reshape(int width, int height) -{ - if (width <= 0 || height <= 0) - return; - - V.width = width; - V.height = height; - - glViewport(0, 0, width, height); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (V.resize) - V.resize(width, height); -} - -static void view_keyboard(unsigned char key, int x, int y) -{ - if (V.keyboard) - V.keyboard(key); - - if (key == 'm') - printf("mouse %d %d\n", x, y); - if (key == 'q') { - if (V.exitf) - V.exitf(); - exit(0); - } -} - -static void view_mouse(int button, int state, int x, int y) -{ - if (button == 0) { - if (state == GLUT_DOWN) { - V.mouseX = x; - V.mouseY = y; - V.mouseBut0 = 1; - } - else if (state == GLUT_UP) { - V.mouseBut0 = 0; - } - } - else if (button == 2) { - if (state == GLUT_DOWN) { - V.mouseX = x; - V.mouseY = y; - V.mouseBut2 = 1; - } - else if (state == GLUT_UP) { - V.mouseBut2 = 0; - } - } -} - -static void view_motion(int x, int y) -{ - const int but = V.mouseBut0 ? 0 : 2; - const int distX = x - V.mouseX; - const int distY = y - V.mouseY; - - if (V.motion) - V.motion(distX, distY, but); - - V.mouseX = x; - V.mouseY = y; -} - -static void view_idle() -{ - if (V.redraw) { - V.redraw = false; - glutPostRedisplay(); - } - - time_sleep(0.1); -} - -void view_main_loop(const char *title, - int width, - int height, - ViewInitFunc initf, - ViewExitFunc exitf, - ViewResizeFunc resize, - ViewDisplayFunc display, - ViewKeyboardFunc keyboard, - ViewMotionFunc motion) -{ - const char *name = "app"; - char *argv = (char *)name; - int argc = 1; - - memset(&V, 0, sizeof(V)); - V.width = width; - V.height = height; - V.first_display = true; - V.redraw = false; - V.initf = initf; - V.exitf = exitf; - V.resize = resize; - V.display = display; - V.keyboard = keyboard; - V.motion = motion; - - glutInit(&argc, &argv); - glutInitWindowSize(width, height); - glutInitWindowPosition(0, 0); - glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); - glutCreateWindow(title); - - glewInit(); - - view_reshape(width, height); - - glutDisplayFunc(view_display); - glutIdleFunc(view_idle); - glutReshapeFunc(view_reshape); - glutKeyboardFunc(view_keyboard); - glutMouseFunc(view_mouse); - glutMotionFunc(view_motion); - - glutMainLoop(); -} - -void view_redraw() -{ - V.redraw = true; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/util/view.h b/intern/cycles/util/view.h deleted file mode 100644 index 51c242c21f7..00000000000 --- a/intern/cycles/util/view.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#ifndef __UTIL_VIEW_H__ -#define __UTIL_VIEW_H__ - -/* Functions to display a simple OpenGL window using GLUT, simplified to the - * bare minimum we need to reduce boilerplate code in tests apps. */ - -CCL_NAMESPACE_BEGIN - -typedef void (*ViewInitFunc)(); -typedef void (*ViewExitFunc)(); -typedef void (*ViewResizeFunc)(int width, int height); -typedef void (*ViewDisplayFunc)(); -typedef void (*ViewKeyboardFunc)(unsigned char key); -typedef void (*ViewMotionFunc)(int x, int y, int button); - -void view_main_loop(const char *title, - int width, - int height, - ViewInitFunc initf, - ViewExitFunc exitf, - ViewResizeFunc resize, - ViewDisplayFunc display, - ViewKeyboardFunc keyboard, - ViewMotionFunc motion); - -void view_display_info(const char *info); -void view_display_help(); -void view_redraw(); - -CCL_NAMESPACE_END - -#endif /*__UTIL_VIEW_H__*/ diff --git a/intern/ffmpeg/tests/ffmpeg_codecs.cc b/intern/ffmpeg/tests/ffmpeg_codecs.cc index bb6e13579e5..d0c40736884 100644 --- a/intern/ffmpeg/tests/ffmpeg_codecs.cc +++ b/intern/ffmpeg/tests/ffmpeg_codecs.cc @@ -4,12 +4,13 @@ extern "C" { #include <libavcodec/avcodec.h> +#include <libavutil/channel_layout.h> #include <libavutil/log.h> } namespace { -bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat) +bool test_vcodec(const AVCodec *codec, AVPixelFormat pixelformat) { av_log_set_level(AV_LOG_QUIET); bool result = false; @@ -30,7 +31,7 @@ bool test_vcodec(AVCodec *codec, AVPixelFormat pixelformat) } return result; } -bool test_acodec(AVCodec *codec, AVSampleFormat fmt) +bool test_acodec(const AVCodec *codec, AVSampleFormat fmt) { av_log_set_level(AV_LOG_QUIET); bool result = false; @@ -54,7 +55,7 @@ bool test_acodec(AVCodec *codec, AVSampleFormat fmt) bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat) { bool result = false; - AVCodec *codec = avcodec_find_encoder(codec_id); + const AVCodec *codec = avcodec_find_encoder(codec_id); if (codec) result = test_vcodec(codec, pixelformat); return result; @@ -63,7 +64,7 @@ bool test_codec_video_by_codecid(AVCodecID codec_id, AVPixelFormat pixelformat) bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat) { bool result = false; - AVCodec *codec = avcodec_find_encoder_by_name(codecname); + const AVCodec *codec = avcodec_find_encoder_by_name(codecname); if (codec) result = test_vcodec(codec, pixelformat); return result; @@ -72,7 +73,7 @@ bool test_codec_video_by_name(const char *codecname, AVPixelFormat pixelformat) bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt) { bool result = false; - AVCodec *codec = avcodec_find_encoder(codec_id); + const AVCodec *codec = avcodec_find_encoder(codec_id); if (codec) result = test_acodec(codec, fmt); return result; @@ -81,7 +82,7 @@ bool test_codec_audio_by_codecid(AVCodecID codec_id, AVSampleFormat fmt) bool test_codec_audio_by_name(const char *codecname, AVSampleFormat fmt) { bool result = false; - AVCodec *codec = avcodec_find_encoder_by_name(codecname); + const AVCodec *codec = avcodec_find_encoder_by_name(codecname); if (codec) result = test_acodec(codec, fmt); return result; diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 441c7315f1a..4e48a908c00 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -249,6 +249,16 @@ extern GHOST_TSuccess GHOST_EndFullScreen(GHOST_SystemHandle systemhandle); */ extern int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle); +/** + * Get the Window under the cursor. + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return The window under the cursor or nullptr in none. + */ +extern GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle, + int32_t x, + int32_t y); + /*************************************************************************************** * Event management functionality ***************************************************************************************/ diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index 837ec25d0f8..ed193ee7e5d 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -309,6 +309,14 @@ class GHOST_ISystem { */ virtual void useWindowFocus(const bool use_focus) = 0; + /** + * Get the Window under the cursor. + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return The window under the cursor or nullptr if none. + */ + virtual GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) = 0; + /*************************************************************************************** * Event management functionality ***************************************************************************************/ diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index a47d2468937..e3d01c24283 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -233,6 +233,16 @@ int GHOST_GetFullScreen(GHOST_SystemHandle systemhandle) return (int)system->getFullScreen(); } +GHOST_WindowHandle GHOST_GetWindowUnderCursor(GHOST_SystemHandle systemhandle, + int32_t x, + int32_t y) +{ + GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; + GHOST_IWindow *window = system->getWindowUnderCursor(x, y); + + return (GHOST_WindowHandle)window; +} + bool GHOST_ProcessEvents(GHOST_SystemHandle systemhandle, bool waitForEvent) { GHOST_ISystem *system = (GHOST_ISystem *)systemhandle; diff --git a/intern/ghost/intern/GHOST_ContextD3D.cpp b/intern/ghost/intern/GHOST_ContextD3D.cpp index 11f15fd1ee3..ded76daa145 100644 --- a/intern/ghost/intern/GHOST_ContextD3D.cpp +++ b/intern/ghost/intern/GHOST_ContextD3D.cpp @@ -110,9 +110,11 @@ class GHOST_SharedOpenGLResource { struct SharedData { HANDLE device; GLuint fbo; - HANDLE render_buf{nullptr}; + HANDLE render_target{nullptr}; } m_shared; + enum RenderTarget { TARGET_RENDERBUF, TARGET_TEX2D }; + public: GHOST_SharedOpenGLResource(ID3D11Device *device, ID3D11DeviceContext *device_ctx, @@ -179,37 +181,64 @@ class GHOST_SharedOpenGLResource { } if (m_is_initialized) { - if (m_shared.render_buf) { - wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf); + if (m_shared.render_target +#if 1 + /* TODO: #wglDXUnregisterObjectNV() causes an access violation on AMD when the shared + * resource is a GL texture. Since there is currently no good alternative, just skip + * unregistering the shared resource. */ + && !m_use_gl_texture2d +#endif + ) { + wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target); } if (m_shared.device) { wglDXCloseDeviceNV(m_shared.device); } glDeleteFramebuffers(1, &m_shared.fbo); - glDeleteRenderbuffers(1, &m_gl_render_buf); + if (m_use_gl_texture2d) { + glDeleteTextures(1, &m_gl_render_target); + } + else { + glDeleteRenderbuffers(1, &m_gl_render_target); + } } } - void reregisterSharedObject() + /* Returns true if the shared object was successfully registered, false otherwise. */ + bool reregisterSharedObject(RenderTarget target) { - if (m_shared.render_buf) { - wglDXUnregisterObjectNV(m_shared.device, m_shared.render_buf); + if (m_shared.render_target) { + wglDXUnregisterObjectNV(m_shared.device, m_shared.render_target); } if (!m_render_target_tex) { - return; + return false; } - m_shared.render_buf = wglDXRegisterObjectNV(m_shared.device, - m_render_target_tex, - m_gl_render_buf, - GL_RENDERBUFFER, - WGL_ACCESS_READ_WRITE_NV); + if (target == TARGET_TEX2D) { + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + m_cur_width, + m_cur_height, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + nullptr); + } - if (!m_shared.render_buf) { + m_shared.render_target = wglDXRegisterObjectNV(m_shared.device, + m_render_target_tex, + m_gl_render_target, + (target == TARGET_TEX2D) ? GL_TEXTURE_2D : + GL_RENDERBUFFER, + WGL_ACCESS_READ_WRITE_NV); + if (!m_shared.render_target) { fprintf(stderr, "Error registering shared object using wglDXRegisterObjectNV()\n"); - return; + return false; } + + return true; } GHOST_TSuccess initialize() @@ -221,16 +250,33 @@ class GHOST_SharedOpenGLResource { } /* Build the renderbuffer. */ - glGenRenderbuffers(1, &m_gl_render_buf); - glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_buf); + glGenRenderbuffers(1, &m_gl_render_target); + glBindRenderbuffer(GL_RENDERBUFFER, m_gl_render_target); + + if (!reregisterSharedObject(TARGET_RENDERBUF)) { + glBindRenderbuffer(GL_RENDERBUFFER, 0); + if (m_gl_render_target) { + glDeleteRenderbuffers(1, &m_gl_render_target); + } + /* Fall back to texture 2d. */ + m_use_gl_texture2d = true; + glGenTextures(1, &m_gl_render_target); + glBindTexture(GL_TEXTURE_2D, m_gl_render_target); - reregisterSharedObject(); + reregisterSharedObject(TARGET_TEX2D); + } /* Build the framebuffer */ glGenFramebuffers(1, &m_shared.fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_shared.fbo); - glFramebufferRenderbuffer( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_buf); + if (m_use_gl_texture2d) { + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_gl_render_target, 0); + } + else { + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_gl_render_target); + } m_is_initialized = true; return GHOST_kSuccess; @@ -245,7 +291,7 @@ class GHOST_SharedOpenGLResource { if ((m_cur_width != width) || (m_cur_height != height)) { m_cur_width = width; m_cur_height = height; - reregisterSharedObject(); + reregisterSharedObject(m_use_gl_texture2d ? TARGET_TEX2D : TARGET_RENDERBUF); } } @@ -293,18 +339,19 @@ class GHOST_SharedOpenGLResource { private: void beginGLOnly() { - wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_buf); + wglDXLockObjectsNV(m_shared.device, 1, &m_shared.render_target); } void endGLOnly() { - wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_buf); + wglDXUnlockObjectsNV(m_shared.device, 1, &m_shared.render_target); } ID3D11Device *m_device; ID3D11DeviceContext *m_device_ctx; - GLuint m_gl_render_buf; + GLuint m_gl_render_target; unsigned int m_cur_width, m_cur_height; bool m_is_initialized{false}; + bool m_use_gl_texture2d{false}; }; GHOST_SharedOpenGLResource *GHOST_ContextD3D::createSharedOpenGLResource( diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index e9c63502f66..3df85e18bc7 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -189,6 +189,25 @@ bool GHOST_System::getFullScreen(void) return fullScreen; } +GHOST_IWindow *GHOST_System::getWindowUnderCursor(int32_t x, int32_t y) +{ + /* TODO: This solution should follow the order of the activated windows (Z-order). + * It is imperfect but usable in most cases. */ + for (GHOST_IWindow *iwindow : m_windowManager->getWindows()) { + if (iwindow->getState() == GHOST_kWindowStateMinimized) { + continue; + } + + GHOST_Rect bounds; + iwindow->getClientBounds(bounds); + if (bounds.isInside(x, y)) { + return iwindow; + } + } + + return NULL; +} + void GHOST_System::dispatchEvents() { #ifdef WITH_INPUT_NDOF diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 8602dd94e8c..0e1e3f734ae 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -157,6 +157,14 @@ class GHOST_System : public GHOST_ISystem { void useWindowFocus(const bool use_focus); bool m_windowFocus; + /** + * Get the Window under the cursor. + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return The window under the cursor or nullptr if none. + */ + GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y); + /*************************************************************************************** * Event management functionality ***************************************************************************************/ diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index a601d00561a..8b6dfb4efed 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -109,6 +109,14 @@ class GHOST_SystemCocoa : public GHOST_System { */ GHOST_TSuccess disposeContext(GHOST_IContext *context); + /** + * Get the Window under the cursor. + * \param x: The x-coordinate of the cursor. + * \param y: The y-coordinate of the cursor. + * \return The window under the cursor or nullptr if none. + */ + GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y); + /*************************************************************************************** * Event management functionality ***************************************************************************************/ diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 51d4083b436..f0db6b6fdfc 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -307,6 +307,7 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) case ']': return GHOST_kKeyRightBracket; case '`': + case '<': /* The position of '`' is equivalent to this symbol in the French layout. */ return GHOST_kKeyAccentGrave; default: return GHOST_kKeyUnknown; @@ -771,6 +772,20 @@ GHOST_TSuccess GHOST_SystemCocoa::disposeContext(GHOST_IContext *context) return GHOST_kSuccess; } +GHOST_IWindow *GHOST_SystemCocoa::getWindowUnderCursor(int32_t x, int32_t y) +{ + NSPoint scr_co = NSMakePoint(x, y); + + int windowNumberAtPoint = [NSWindow windowNumberAtPoint:scr_co belowWindowWithWindowNumber:0]; + NSWindow *nswindow = [NSApp windowWithWindowNumber:windowNumberAtPoint]; + + if (nswindow == nil) { + return nil; + } + + return m_windowManager->getWindowAssociatedWithOSWindow((void *)nswindow); +} + /** * \note : returns coordinates in Cocoa screen coordinates */ diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h index 7b92d1d13a1..48973a00573 100644 --- a/intern/ghost/intern/GHOST_SystemNULL.h +++ b/intern/ghost/intern/GHOST_SystemNULL.h @@ -114,4 +114,9 @@ class GHOST_SystemNULL : public GHOST_System { type, ((glSettings.flags & GHOST_glStereoVisual) != 0)); } + + GHOST_IWindow *getWindowUnderCursor(int32_t x, int32_t y) + { + return NULL; + } }; diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 1c26935ed64..e588c7485b4 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -53,9 +53,6 @@ #ifndef VK_COMMA # define VK_COMMA 0xBC #endif // VK_COMMA -#ifndef VK_QUOTE -# define VK_QUOTE 0xDE -#endif // VK_QUOTE #ifndef VK_BACK_QUOTE # define VK_BACK_QUOTE 0xC0 #endif // VK_BACK_QUOTE @@ -635,14 +632,32 @@ GHOST_TKey GHOST_SystemWin32::hardKey(RAWINPUT const &raw, GHOST_TKey GHOST_SystemWin32::processSpecialKey(short vKey, short scanCode) const { GHOST_TKey key = GHOST_kKeyUnknown; - switch (PRIMARYLANGID(m_langId)) { - case LANG_FRENCH: - if (vKey == VK_OEM_8) - key = GHOST_kKeyF13; // oem key; used purely for shortcuts . + char ch = (char)MapVirtualKeyA(vKey, MAPVK_VK_TO_CHAR); + switch (ch) { + case u'\"': + case u'\'': + key = GHOST_kKeyQuote; break; - case LANG_ENGLISH: - if (SUBLANGID(m_langId) == SUBLANG_ENGLISH_UK && vKey == VK_OEM_8) // "`¬" - key = GHOST_kKeyAccentGrave; + case u'.': + key = GHOST_kKeyNumpadPeriod; + break; + case u'/': + key = GHOST_kKeySlash; + break; + case u'`': + case u'²': + key = GHOST_kKeyAccentGrave; + break; + default: + if (vKey == VK_OEM_7) { + key = GHOST_kKeyQuote; + } + else if (vKey == VK_OEM_8) { + if (PRIMARYLANGID(m_langId) == LANG_FRENCH) { + /* OEM key; used purely for shortcuts. */ + key = GHOST_kKeyF13; + } + } break; } @@ -777,9 +792,6 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten case VK_CLOSE_BRACKET: key = GHOST_kKeyRightBracket; break; - case VK_QUOTE: - key = GHOST_kKeyQuote; - break; case VK_GR_LESS: key = GHOST_kKeyGrLess; break; @@ -821,9 +833,6 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten case VK_CAPITAL: key = GHOST_kKeyCapsLock; break; - case VK_OEM_8: - key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode); - break; case VK_MEDIA_PLAY_PAUSE: key = GHOST_kKeyMediaPlay; break; @@ -836,8 +845,10 @@ GHOST_TKey GHOST_SystemWin32::convertKey(short vKey, short scanCode, short exten case VK_MEDIA_NEXT_TRACK: key = GHOST_kKeyMediaLast; break; + case VK_OEM_7: + case VK_OEM_8: default: - key = GHOST_kKeyUnknown; + key = ((GHOST_SystemWin32 *)getSystem())->processSpecialKey(vKey, scanCode); break; } } diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index 5d8feb8e48a..2ac3d9ec2a5 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -412,6 +412,9 @@ void GHOST_XrContext::getExtensionsToEnable( /* Interaction profile extensions. */ try_ext.push_back(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME); try_ext.push_back(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME); +#ifdef XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME + try_ext.push_back(XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME); +#endif try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME); /* Controller model extension. */ diff --git a/intern/opencolorio/CMakeLists.txt b/intern/opencolorio/CMakeLists.txt index dfccb9301ac..be6ccc5c2c5 100644 --- a/intern/opencolorio/CMakeLists.txt +++ b/intern/opencolorio/CMakeLists.txt @@ -7,6 +7,7 @@ set(INC ../guardedalloc ../../source/blender/blenlib ../../source/blender/gpu + ../../source/blender/gpu/intern ../../source/blender/makesdna ) @@ -20,6 +21,7 @@ set(SRC ocio_capi.h ocio_impl.h + ocio_shader_shared.hh ) set(LIB @@ -56,8 +58,38 @@ if(WITH_OPENCOLORIO) ) endif() - data_to_c_simple(gpu_shader_display_transform.glsl SRC) - data_to_c_simple(gpu_shader_display_transform_vertex.glsl SRC) + set(GLSL_SRC + gpu_shader_display_transform_vert.glsl + gpu_shader_display_transform_frag.glsl + + ocio_shader_shared.hh + ) + + set(GLSL_C) + foreach(GLSL_FILE ${GLSL_SRC}) + data_to_c_simple(${GLSL_FILE} GLSL_C) + endforeach() + + blender_add_lib(bf_ocio_shaders "${GLSL_C}" "" "" "") + + list(APPEND LIB + bf_ocio_shaders + ) + + set(GLSL_SOURCE_CONTENT "") + foreach(GLSL_FILE ${GLSL_SRC}) + get_filename_component(GLSL_FILE_NAME ${GLSL_FILE} NAME) + string(REPLACE "." "_" GLSL_FILE_NAME_UNDERSCORES ${GLSL_FILE_NAME}) + string(APPEND GLSL_SOURCE_CONTENT "SHADER_SOURCE\(datatoc_${GLSL_FILE_NAME_UNDERSCORES}, \"${GLSL_FILE_NAME}\", \"${GLSL_FILE}\"\)\n") + endforeach() + + set(glsl_source_list_file "${CMAKE_CURRENT_BINARY_DIR}/glsl_ocio_source_list.h") + file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}") + list(APPEND SRC ${glsl_source_list_file}) + list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) + + target_include_directories(bf_ocio_shaders PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + endif() diff --git a/intern/opencolorio/gpu_shader_display_transform.glsl b/intern/opencolorio/gpu_shader_display_transform_frag.glsl index f5a7a7bf45d..3c2352c13ba 100644 --- a/intern/opencolorio/gpu_shader_display_transform.glsl +++ b/intern/opencolorio/gpu_shader_display_transform_frag.glsl @@ -1,39 +1,10 @@ /* Blender OpenColorIO implementation */ -uniform sampler2D image_texture; -uniform sampler2D overlay_texture; - -uniform float dither; -uniform float scale; -uniform float exponent; -uniform bool predivide; -uniform bool overlay; +/* -------------------------------------------------------------------- */ +/** \name Curve Mapping Implementation + * \{ */ #ifdef USE_CURVE_MAPPING -uniform sampler1D curve_mapping_texture; - -layout(std140) uniform OCIO_GPUCurveMappingParameters -{ - /* Curve mapping parameters - * - * See documentation for OCIO_CurveMappingSettings to get fields descriptions. - * (this ones pretty much copies stuff from C structure.) - */ - vec4 curve_mapping_mintable; - vec4 curve_mapping_range; - vec4 curve_mapping_ext_in_x; - vec4 curve_mapping_ext_in_y; - vec4 curve_mapping_ext_out_x; - vec4 curve_mapping_ext_out_y; - vec4 curve_mapping_first_x; - vec4 curve_mapping_first_y; - vec4 curve_mapping_last_x; - vec4 curve_mapping_last_y; - vec4 curve_mapping_black; - vec4 curve_mapping_bwmul; - int curve_mapping_lut_size; - int curve_mapping_use_extend_extrapolate; -}; float read_curve_mapping(int table, int index) { @@ -43,27 +14,27 @@ float read_curve_mapping(int table, int index) float curvemap_calc_extend(int table, float x, vec2 first, vec2 last) { if (x <= first[0]) { - if (curve_mapping_use_extend_extrapolate == 0) { + if (curve_mapping.use_extend_extrapolate == 0) { /* horizontal extrapolation */ return first[1]; } else { - float fac = (curve_mapping_ext_in_x[table] != 0.0) ? - ((x - first[0]) / curve_mapping_ext_in_x[table]) : + float fac = (curve_mapping.ext_in_x[table] != 0.0) ? + ((x - first[0]) / curve_mapping.ext_in_x[table]) : 10000.0; - return first[1] + curve_mapping_ext_in_y[table] * fac; + return first[1] + curve_mapping.ext_in_y[table] * fac; } } else if (x >= last[0]) { - if (curve_mapping_use_extend_extrapolate == 0) { + if (curve_mapping.use_extend_extrapolate == 0) { /* horizontal extrapolation */ return last[1]; } else { - float fac = (curve_mapping_ext_out_x[table] != 0.0) ? - ((x - last[0]) / curve_mapping_ext_out_x[table]) : + float fac = (curve_mapping.ext_out_x[table] != 0.0) ? + ((x - last[0]) / curve_mapping.ext_out_x[table]) : -10000.0; - return last[1] + curve_mapping_ext_out_y[table] * fac; + return last[1] + curve_mapping.ext_out_y[table] * fac; } } return 0.0; @@ -71,10 +42,10 @@ float curvemap_calc_extend(int table, float x, vec2 first, vec2 last) float curvemap_evaluateF(int table, float value) { - float mintable_ = curve_mapping_mintable[table]; - float range = curve_mapping_range[table]; + float mintable_ = curve_mapping.mintable[table]; + float range = curve_mapping.range[table]; float mintable = 0.0; - int CM_TABLE = curve_mapping_lut_size - 1; + int CM_TABLE = curve_mapping.lut_size - 1; float fi; int i; @@ -87,8 +58,8 @@ float curvemap_evaluateF(int table, float value) if (fi < 0.0 || fi > float(CM_TABLE)) { return curvemap_calc_extend(table, value, - vec2(curve_mapping_first_x[table], curve_mapping_first_y[table]), - vec2(curve_mapping_last_x[table], curve_mapping_last_y[table])); + vec2(curve_mapping.first_x[table], curve_mapping.first_y[table]), + vec2(curve_mapping.last_x[table], curve_mapping.last_y[table])); } else { if (i < 0) { @@ -106,7 +77,7 @@ float curvemap_evaluateF(int table, float value) vec4 curvemapping_evaluate_premulRGBF(vec4 col) { - col.rgb = (col.rgb - curve_mapping_black.rgb) * curve_mapping_bwmul.rgb; + col.rgb = (col.rgb - curve_mapping.black.rgb) * curve_mapping.bwmul.rgb; vec4 result; result.r = curvemap_evaluateF(0, col.r); @@ -115,8 +86,15 @@ vec4 curvemapping_evaluate_premulRGBF(vec4 col) result.a = col.a; return result; } + #endif /* USE_CURVE_MAPPING */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dithering + * \{ */ + /* Using a triangle distribution which gives a more final uniform noise. * See Banding in Games:A Noisy Rant(revision 5) Mikkel Gjøl, Playdead (slide 27) */ /* GPUs are rounding before writing to framebuffer so we center the distribution around 0.0. */ @@ -135,23 +113,33 @@ float dither_random_value(vec2 co) vec2 round_to_pixel(sampler2D tex, vec2 uv) { - vec2 size = textureSize(tex, 0); - return vec2(ivec2(uv * size)) / size; + vec2 size = vec2(textureSize(tex, 0)); + return floor(uv * size) / size; } vec4 apply_dither(vec4 col, vec2 uv) { - col.rgb += dither_random_value(uv) * 0.0033 * dither; + col.rgb += dither_random_value(uv) * 0.0033 * parameters.dither; return col; } -vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Processing + * \{ */ + +/* Prototypes: Implementation is generaterd and defined after. */ +vec4 OCIO_to_scene_linear(vec4 pixel); +vec4 OCIO_to_display(vec4 pixel); + +vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay) { #ifdef USE_CURVE_MAPPING col = curvemapping_evaluate_premulRGBF(col); #endif - if (predivide) { + if (parameters.use_predivide) { if (col.a > 0.0 && col.a < 1.0) { col.rgb *= 1.0 / col.a; } @@ -166,7 +154,7 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv) col = OCIO_to_scene_linear(col); /* Apply exposure in scene linear. */ - col.rgb *= scale; + col.rgb *= parameters.scale; /* Convert to display space. */ col = OCIO_to_display(col); @@ -177,34 +165,31 @@ vec4 OCIO_ProcessColor(vec4 col, vec4 col_overlay, vec2 noise_uv) * i.e: The linear color space w.r.t. display chromaticity and radiometry. * We separate the colormanagement process into two steps to be able to * merge UI using alpha blending in the correct color space. */ - if (overlay) { - col.rgb = pow(col.rgb, vec3(exponent * 2.2)); + if (parameters.use_overlay) { + col.rgb = pow(col.rgb, vec3(parameters.exponent * 2.2)); col = clamp(col, 0.0, 1.0); col *= 1.0 - col_overlay.a; col += col_overlay; /* Assumed unassociated alpha. */ col.rgb = pow(col.rgb, vec3(1.0 / 2.2)); } else { - col.rgb = pow(col.rgb, vec3(exponent)); + col.rgb = pow(col.rgb, vec3(parameters.exponent)); } - if (dither > 0.0) { + if (parameters.dither > 0.0) { + vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st); col = apply_dither(col, noise_uv); } return col; } -/* ------------------------------------------------------------------------ */ - -in vec2 texCoord_interp; -out vec4 fragColor; +/** \} */ void main() { vec4 col = texture(image_texture, texCoord_interp.st); vec4 col_overlay = texture(overlay_texture, texCoord_interp.st); - vec2 noise_uv = round_to_pixel(image_texture, texCoord_interp.st); - fragColor = OCIO_ProcessColor(col, col_overlay, noise_uv); + fragColor = OCIO_ProcessColor(col, col_overlay); } diff --git a/intern/opencolorio/gpu_shader_display_transform_vertex.glsl b/intern/opencolorio/gpu_shader_display_transform_vert.glsl index 8cf9628b06b..06788be11de 100644 --- a/intern/opencolorio/gpu_shader_display_transform_vertex.glsl +++ b/intern/opencolorio/gpu_shader_display_transform_vert.glsl @@ -1,10 +1,4 @@ -uniform mat4 ModelViewProjectionMatrix; - -in vec2 texCoord; -in vec2 pos; -out vec2 texCoord_interp; - void main() { gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f); diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc index 11e2a5f7371..a02a37522b9 100644 --- a/intern/opencolorio/ocio_impl.cc +++ b/intern/opencolorio/ocio_impl.cc @@ -320,16 +320,18 @@ void OCIOImpl::configGetXYZtoRGB(OCIO_ConstConfigRcPtr *config_, float xyz_to_rg } if (config->hasRole("aces_interchange")) { - /* Standard OpenColorIO role, defined as ACES2065-1. */ - const float xyz_E_to_aces[3][3] = {{1.0498110175f, -0.4959030231f, 0.0f}, - {0.0f, 1.3733130458f, 0.0f}, - {-0.0000974845f, 0.0982400361f, 0.9912520182f}}; - const float xyz_D65_to_E[3][3] = { - {1.0521111f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.9184170f}}; - + /* Standard OpenColorIO role, defined as ACES AP0 (ACES2065-1). */ float aces_to_rgb[3][3]; if (to_scene_linear_matrix(config, "aces_interchange", aces_to_rgb)) { - mul_m3_series(xyz_to_rgb, aces_to_rgb, xyz_E_to_aces, xyz_D65_to_E); + /* This is the OpenColorIO builtin transform: + * UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD. */ + const float ACES_AP0_to_xyz_D65[3][3] = {{0.938280f, 0.337369f, 0.001174f}, + {-0.004451f, 0.729522f, -0.003711f}, + {0.016628f, -0.066890f, 1.091595f}}; + float xyz_to_aces[3][3]; + invert_m3_m3(xyz_to_aces, ACES_AP0_to_xyz_D65); + + mul_m3_m3m3(xyz_to_rgb, aces_to_rgb, xyz_to_aces); } } else if (config->hasRole("XYZ")) { diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc index 09803cd8038..e3d44ae9d55 100644 --- a/intern/opencolorio/ocio_impl_glsl.cc +++ b/intern/opencolorio/ocio_impl_glsl.cc @@ -21,14 +21,14 @@ #include "GPU_shader.h" #include "GPU_uniform_buffer.h" +#include "gpu_shader_create_info.hh" + using namespace OCIO_NAMESPACE; #include "MEM_guardedalloc.h" #include "ocio_impl.h" - -extern "C" char datatoc_gpu_shader_display_transform_glsl[]; -extern "C" char datatoc_gpu_shader_display_transform_vertex_glsl[]; +#include "ocio_shader_shared.hh" /* **** OpenGL drawing routines using GLSL for color space transform ***** */ @@ -39,41 +39,19 @@ enum OCIO_GPUTextureSlots { TEXTURE_SLOT_LUTS_OFFSET = 3, }; -/* Curve mapping parameters - * - * See documentation for OCIO_CurveMappingSettings to get fields descriptions. - * (this ones pretty much copies stuff from C structure.) - */ -struct OCIO_GPUCurveMappingParameters { - float curve_mapping_mintable[4]; - float curve_mapping_range[4]; - float curve_mapping_ext_in_x[4]; - float curve_mapping_ext_in_y[4]; - float curve_mapping_ext_out_x[4]; - float curve_mapping_ext_out_y[4]; - float curve_mapping_first_x[4]; - float curve_mapping_first_y[4]; - float curve_mapping_last_x[4]; - float curve_mapping_last_y[4]; - float curve_mapping_black[4]; - float curve_mapping_bwmul[4]; - int curve_mapping_lut_size; - int curve_mapping_use_extend_extrapolate; - int _pad[2]; - /** WARNING: Needs to be 16byte aligned. Used as UBO data. */ +enum OCIO_GPUUniformBufSlots { + UNIFORMBUF_SLOT_DISPLAY = 0, + UNIFORMBUF_SLOT_CURVEMAP = 1, + UNIFORMBUF_SLOT_LUTS = 2, }; struct OCIO_GPUShader { /* GPU shader. */ struct GPUShader *shader = nullptr; - /** Uniform locations. */ - int scale_loc = 0; - int exponent_loc = 0; - int dither_loc = 0; - int overlay_loc = 0; - int predivide_loc = 0; - int ubo_bind = 0; + /** Uniform parameters. */ + OCIO_GPUParameters parameters = {}; + GPUUniformBuf *parameters_buffer = nullptr; /* Destructor. */ ~OCIO_GPUShader() @@ -81,6 +59,9 @@ struct OCIO_GPUShader { if (shader) { GPU_shader_free(shader); } + if (parameters_buffer) { + GPU_uniformbuf_free(parameters_buffer); + } } }; @@ -103,6 +84,7 @@ struct OCIO_GPUTextures { /* Uniforms */ std::vector<OCIO_GPUUniform> uniforms; + GPUUniformBuf *uniforms_buffer = nullptr; /* Destructor. */ ~OCIO_GPUTextures() @@ -113,6 +95,9 @@ struct OCIO_GPUTextures { if (dummy) { GPU_texture_free(dummy); } + if (uniforms_buffer) { + GPU_uniformbuf_free(uniforms_buffer); + } } }; @@ -165,97 +150,134 @@ static bool createGPUShader(OCIO_GPUShader &shader, const GpuShaderDescRcPtr &shaderdesc_to_display, const bool use_curve_mapping) { - std::ostringstream os; - { - /* Fragment shader */ + using namespace blender::gpu::shader; - /* Work around OpenColorIO not supporting latest GLSL yet. */ - os << "#define texture2D texture\n"; - os << "#define texture3D texture\n"; + std::string source; + source += shaderdesc_to_scene_linear->getShaderText(); + source += "\n"; + source += shaderdesc_to_display->getShaderText(); + source += "\n"; - if (use_curve_mapping) { - os << "#define USE_CURVE_MAPPING\n"; + { + /* Replace all uniform declarations by a comment. + * This avoids double declarations from the backend. */ + size_t index = 0; + while (true) { + index = source.find("uniform ", index); + if (index == -1) { + break; + } + source.replace(index, 2, "//"); + index += 2; } - - os << shaderdesc_to_scene_linear->getShaderText() << "\n"; - os << shaderdesc_to_display->getShaderText() << "\n"; - - os << datatoc_gpu_shader_display_transform_glsl; } - shader.shader = GPU_shader_create(datatoc_gpu_shader_display_transform_vertex_glsl, - os.str().c_str(), - nullptr, - nullptr, - nullptr, - "OCIOShader"); - - if (shader.shader == nullptr) { - return false; - } - - shader.scale_loc = GPU_shader_get_uniform(shader.shader, "scale"); - shader.exponent_loc = GPU_shader_get_uniform(shader.shader, "exponent"); - shader.dither_loc = GPU_shader_get_uniform(shader.shader, "dither"); - shader.overlay_loc = GPU_shader_get_uniform(shader.shader, "overlay"); - shader.predivide_loc = GPU_shader_get_uniform(shader.shader, "predivide"); - shader.ubo_bind = GPU_shader_get_uniform_block_binding(shader.shader, - "OCIO_GPUCurveMappingParameters"); - - GPU_shader_bind(shader.shader); - - /* Set texture bind point uniform once. This is saved by the shader. */ - GPUShader *sh = shader.shader; - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "image_texture"), TEXTURE_SLOT_IMAGE); - GPU_shader_uniform_int(sh, GPU_shader_get_uniform(sh, "overlay_texture"), TEXTURE_SLOT_OVERLAY); + StageInterfaceInfo iface("OCIO_Interface", ""); + iface.smooth(Type::VEC2, "texCoord_interp"); + + ShaderCreateInfo info("OCIO_Display"); + /* Work around OpenColorIO not supporting latest GLSL yet. */ + info.define("texture2D", "texture"); + info.define("texture3D", "texture"); + info.typedef_source("ocio_shader_shared.hh"); + info.sampler(TEXTURE_SLOT_IMAGE, ImageType::FLOAT_2D, "image_texture"); + info.sampler(TEXTURE_SLOT_OVERLAY, ImageType::FLOAT_2D, "overlay_texture"); + info.uniform_buf(UNIFORMBUF_SLOT_DISPLAY, "OCIO_GPUParameters", "parameters"); + info.push_constant(Type::MAT4, "ModelViewProjectionMatrix"); + info.vertex_in(0, Type::VEC2, "pos"); + info.vertex_in(1, Type::VEC2, "texCoord"); + info.vertex_out(iface); + info.fragment_out(0, Type::VEC4, "fragColor"); + info.vertex_source("gpu_shader_display_transform_vert.glsl"); + info.fragment_source("gpu_shader_display_transform_frag.glsl"); + info.fragment_source_generated = source; if (use_curve_mapping) { - GPU_shader_uniform_int( - sh, GPU_shader_get_uniform(sh, "curve_mapping_texture"), TEXTURE_SLOT_CURVE_MAPPING); + info.define("USE_CURVE_MAPPING"); + info.uniform_buf(UNIFORMBUF_SLOT_CURVEMAP, "OCIO_GPUCurveMappingParameters", "curve_mapping"); + info.sampler(TEXTURE_SLOT_CURVE_MAPPING, ImageType::FLOAT_1D, "curve_mapping_texture"); } /* Set LUT textures. */ - for (int i = 0; i < textures.luts.size(); i++) { - GPU_shader_uniform_int(sh, - GPU_shader_get_uniform(sh, textures.luts[i].sampler_name.c_str()), - TEXTURE_SLOT_LUTS_OFFSET + i); - } + int slot = TEXTURE_SLOT_LUTS_OFFSET; + for (OCIO_GPULutTexture &texture : textures.luts) { + ImageType type = GPU_texture_dimensions(texture.texture) == 2 ? ImageType::FLOAT_2D : + ImageType::FLOAT_3D; + info.sampler(slot++, type, texture.sampler_name.c_str()); + } + + /* Set LUT uniforms. */ + if (!textures.uniforms.empty()) { + /* NOTE: For simplicity, we pad everything to size of vec4 avoiding sorting and alignment + * issues. It is unlikely that this becomes a real issue. */ + size_t ubo_size = textures.uniforms.size() * sizeof(float) * 4; + void *ubo_data_buf = malloc(ubo_size); + + uint32_t *ubo_data = reinterpret_cast<uint32_t *>(ubo_data_buf); + + std::stringstream ss; + ss << "struct OCIO_GPULutParameters {\n"; + + int index = 0; + for (OCIO_GPUUniform &uniform : textures.uniforms) { + index += 1; + const GpuShaderDesc::UniformData &data = uniform.data; + const char *name = uniform.name.c_str(); + char prefix = ' '; + int vec_len; + switch (data.m_type) { + case UNIFORM_DOUBLE: { + vec_len = 1; + float value = float(data.m_getDouble()); + memcpy(ubo_data, &value, sizeof(float)); + break; + } + case UNIFORM_BOOL: { + prefix = 'b'; + vec_len = 1; + int value = int(data.m_getBool()); + memcpy(ubo_data, &value, sizeof(int)); + break; + } + case UNIFORM_FLOAT3: + vec_len = 3; + memcpy(ubo_data, data.m_getFloat3().data(), sizeof(float) * 3); + break; + case UNIFORM_VECTOR_FLOAT: + vec_len = data.m_vectorFloat.m_getSize(); + memcpy(ubo_data, data.m_vectorFloat.m_getVector(), sizeof(float) * vec_len); + break; + case UNIFORM_VECTOR_INT: + prefix = 'i'; + vec_len = data.m_vectorInt.m_getSize(); + memcpy(ubo_data, data.m_vectorInt.m_getVector(), sizeof(int) * vec_len); + break; + default: + continue; + } + /* Align every member to 16bytes. */ + ubo_data += 4; + /* Use a generic variable name because some GLSL compilers can interpret the preprocessor + * define as recursive. */ + ss << " " << prefix << "vec4 var" << index << ";\n"; + /* Use a define to keep the generated code working. */ + blender::StringRef suffix = blender::StringRefNull("xyzw").substr(0, vec_len); + ss << "#define " << name << " lut_parameters.var" << index << "." << suffix << "\n"; + } + ss << "};\n"; + info.typedef_source_generated = ss.str(); - /* Set uniforms. */ - for (OCIO_GPUUniform &uniform : textures.uniforms) { - const GpuShaderDesc::UniformData &data = uniform.data; - const char *name = uniform.name.c_str(); + info.uniform_buf(UNIFORMBUF_SLOT_LUTS, "OCIO_GPULutParameters", "lut_parameters"); - if (data.m_getDouble) { - GPU_shader_uniform_1f(sh, name, (float)data.m_getDouble()); - } - else if (data.m_getBool) { - GPU_shader_uniform_1f(sh, name, (float)(data.m_getBool() ? 1.0f : 0.0f)); - } - else if (data.m_getFloat3) { - GPU_shader_uniform_3f(sh, - name, - (float)data.m_getFloat3()[0], - (float)data.m_getFloat3()[1], - (float)data.m_getFloat3()[2]); - } - else if (data.m_vectorFloat.m_getSize && data.m_vectorFloat.m_getVector) { - GPU_shader_uniform_vector(sh, - GPU_shader_get_uniform(sh, name), - (int)data.m_vectorFloat.m_getSize(), - 1, - (float *)data.m_vectorFloat.m_getVector()); - } - else if (data.m_vectorInt.m_getSize && data.m_vectorInt.m_getVector) { - GPU_shader_uniform_vector_int(sh, - GPU_shader_get_uniform(sh, name), - (int)data.m_vectorInt.m_getSize(), - 1, - (int *)data.m_vectorInt.m_getVector()); - } + textures.uniforms_buffer = GPU_uniformbuf_create_ex( + ubo_size, ubo_data_buf, "OCIO_LutParameters"); + + free(ubo_data_buf); } - return true; + shader.shader = GPU_shader_create_from_info(reinterpret_cast<GPUShaderCreateInfo *>(&info)); + + return (shader.shader != nullptr); } /** \} */ @@ -302,7 +324,7 @@ static bool addGPULut2D(OCIO_GPUTextures &textures, GPU_R16F; OCIO_GPULutTexture lut; - lut.texture = GPU_texture_create_2d(texture_name, width, height, 0, format, values); + lut.texture = GPU_texture_create_2d(texture_name, width, height, 1, format, values); if (lut.texture == nullptr) { return false; } @@ -334,7 +356,7 @@ static bool addGPULut3D(OCIO_GPUTextures &textures, OCIO_GPULutTexture lut; lut.texture = GPU_texture_create_3d( - texture_name, edgelen, edgelen, edgelen, 0, GPU_RGB16F, GPU_DATA_FLOAT, values); + texture_name, edgelen, edgelen, edgelen, 1, GPU_RGB16F, GPU_DATA_FLOAT, values); if (lut.texture == nullptr) { return false; } @@ -438,27 +460,65 @@ static void updateGPUCurveMapping(OCIO_GPUCurveMappping &curvemap, /* Update uniforms. */ OCIO_GPUCurveMappingParameters data; for (int i = 0; i < 4; i++) { - data.curve_mapping_range[i] = curve_mapping_settings->range[i]; - data.curve_mapping_mintable[i] = curve_mapping_settings->mintable[i]; - data.curve_mapping_ext_in_x[i] = curve_mapping_settings->ext_in_x[i]; - data.curve_mapping_ext_in_y[i] = curve_mapping_settings->ext_in_y[i]; - data.curve_mapping_ext_out_x[i] = curve_mapping_settings->ext_out_x[i]; - data.curve_mapping_ext_out_y[i] = curve_mapping_settings->ext_out_y[i]; - data.curve_mapping_first_x[i] = curve_mapping_settings->first_x[i]; - data.curve_mapping_first_y[i] = curve_mapping_settings->first_y[i]; - data.curve_mapping_last_x[i] = curve_mapping_settings->last_x[i]; - data.curve_mapping_last_y[i] = curve_mapping_settings->last_y[i]; + data.range[i] = curve_mapping_settings->range[i]; + data.mintable[i] = curve_mapping_settings->mintable[i]; + data.ext_in_x[i] = curve_mapping_settings->ext_in_x[i]; + data.ext_in_y[i] = curve_mapping_settings->ext_in_y[i]; + data.ext_out_x[i] = curve_mapping_settings->ext_out_x[i]; + data.ext_out_y[i] = curve_mapping_settings->ext_out_y[i]; + data.first_x[i] = curve_mapping_settings->first_x[i]; + data.first_y[i] = curve_mapping_settings->first_y[i]; + data.last_x[i] = curve_mapping_settings->last_x[i]; + data.last_y[i] = curve_mapping_settings->last_y[i]; } for (int i = 0; i < 3; i++) { - data.curve_mapping_black[i] = curve_mapping_settings->black[i]; - data.curve_mapping_bwmul[i] = curve_mapping_settings->bwmul[i]; + data.black[i] = curve_mapping_settings->black[i]; + data.bwmul[i] = curve_mapping_settings->bwmul[i]; } - data.curve_mapping_lut_size = curve_mapping_settings->lut_size; - data.curve_mapping_use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate; + data.lut_size = curve_mapping_settings->lut_size; + data.use_extend_extrapolate = curve_mapping_settings->use_extend_extrapolate; GPU_uniformbuf_update(curvemap.buffer, &data); } +static void updateGPUDisplayParameters(OCIO_GPUShader &shader, + float scale, + float exponent, + float dither, + bool use_predivide, + bool use_overlay) +{ + bool do_update = false; + if (shader.parameters_buffer == nullptr) { + shader.parameters_buffer = GPU_uniformbuf_create(sizeof(OCIO_GPUParameters)); + do_update = true; + } + OCIO_GPUParameters &data = shader.parameters; + if (data.scale != scale) { + data.scale = scale; + do_update = true; + } + if (data.exponent != exponent) { + data.exponent = exponent; + do_update = true; + } + if (data.dither != dither) { + data.dither = dither; + do_update = true; + } + if (bool(data.use_predivide) != use_predivide) { + data.use_predivide = use_predivide; + do_update = true; + } + if (bool(data.use_overlay) != use_overlay) { + data.use_overlay = use_overlay; + do_update = true; + } + if (do_update) { + GPU_uniformbuf_update(shader.parameters_buffer, &data); + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -609,7 +669,7 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, /* Update and bind curve mapping data. */ if (curve_mapping_settings) { updateGPUCurveMapping(curvemap, curve_mapping_settings); - GPU_uniformbuf_bind(curvemap.buffer, shader.ubo_bind); + GPU_uniformbuf_bind(curvemap.buffer, UNIFORMBUF_SLOT_CURVEMAP); GPU_texture_bind(curvemap.texture, TEXTURE_SLOT_CURVE_MAPPING); } @@ -623,17 +683,16 @@ bool OCIOImpl::gpuDisplayShaderBind(OCIO_ConstConfigRcPtr *config, GPU_texture_bind(textures.luts[i].texture, TEXTURE_SLOT_LUTS_OFFSET + i); } + if (textures.uniforms_buffer) { + GPU_uniformbuf_bind(textures.uniforms_buffer, UNIFORMBUF_SLOT_LUTS); + } + + updateGPUDisplayParameters(shader, scale, exponent, dither, use_predivide, use_overlay); + GPU_uniformbuf_bind(shader.parameters_buffer, UNIFORMBUF_SLOT_DISPLAY); + /* TODO(fclem): remove remains of IMM. */ immBindShader(shader.shader); - /* Bind Shader and set uniforms. */ - // GPU_shader_bind(shader.shader); - GPU_shader_uniform_float(shader.shader, shader.scale_loc, scale); - GPU_shader_uniform_float(shader.shader, shader.exponent_loc, exponent); - GPU_shader_uniform_float(shader.shader, shader.dither_loc, dither); - GPU_shader_uniform_int(shader.shader, shader.overlay_loc, use_overlay); - GPU_shader_uniform_int(shader.shader, shader.predivide_loc, use_predivide); - return true; } diff --git a/intern/opencolorio/ocio_shader_shared.hh b/intern/opencolorio/ocio_shader_shared.hh new file mode 100644 index 00000000000..c7045217196 --- /dev/null +++ b/intern/opencolorio/ocio_shader_shared.hh @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. All rights reserved. */ + +#ifndef GPU_SHADER +# include "GPU_shader_shared_utils.h" +#endif + +struct OCIO_GPUCurveMappingParameters { + /* Curve mapping parameters + * + * See documentation for OCIO_CurveMappingSettings to get fields descriptions. + * (this ones pretty much copies stuff from C structure.) + */ + float4 mintable; + float4 range; + float4 ext_in_x; + float4 ext_in_y; + float4 ext_out_x; + float4 ext_out_y; + float4 first_x; + float4 first_y; + float4 last_x; + float4 last_y; + float4 black; + float4 bwmul; + int lut_size; + int use_extend_extrapolate; + int _pad0; + int _pad1; +}; + +struct OCIO_GPUParameters { + float dither; + float scale; + float exponent; + bool1 use_predivide; + bool1 use_overlay; + int _pad0; + int _pad1; + int _pad2; +}; diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index 38ce9791b5a..bb3aa16a9fe 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -66,6 +66,8 @@ if(WITH_OPENSUBDIV) internal/evaluator/evaluator_capi.cc internal/evaluator/evaluator_impl.cc internal/evaluator/evaluator_impl.h + internal/evaluator/gl_compute_evaluator.cc + internal/evaluator/gl_compute_evaluator.h internal/evaluator/patch_map.cc internal/evaluator/patch_map.h @@ -104,6 +106,8 @@ if(WITH_OPENSUBDIV) add_definitions(-DNOMINMAX) add_definitions(-D_USE_MATH_DEFINES) endif() + + data_to_c_simple(internal/evaluator/shaders/glsl_compute_kernel.glsl SRC) else() list(APPEND SRC stub/opensubdiv_stub.cc diff --git a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h index 783efd484aa..dc137e4322e 100644 --- a/intern/opensubdiv/internal/evaluator/eval_output_gpu.h +++ b/intern/opensubdiv/internal/evaluator/eval_output_gpu.h @@ -20,13 +20,11 @@ #define OPENSUBDIV_EVAL_OUTPUT_GPU_H_ #include "internal/evaluator/eval_output.h" +#include "internal/evaluator/gl_compute_evaluator.h" -#include <opensubdiv/osd/glComputeEvaluator.h> #include <opensubdiv/osd/glPatchTable.h> #include <opensubdiv/osd/glVertexBuffer.h> -using OpenSubdiv::Osd::GLComputeEvaluator; -using OpenSubdiv::Osd::GLStencilTableSSBO; using OpenSubdiv::Osd::GLVertexBuffer; namespace blender { diff --git a/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc new file mode 100644 index 00000000000..acf628c7035 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.cc @@ -0,0 +1,647 @@ +// +// Copyright 2015 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// + +#include "gl_compute_evaluator.h" + +#include <GL/glew.h> + +#include <opensubdiv/far/error.h> +#include <opensubdiv/far/patchDescriptor.h> +#include <opensubdiv/far/stencilTable.h> +#include <opensubdiv/osd/glslPatchShaderSource.h> + +#include <cassert> +#include <cmath> +#include <sstream> +#include <string> +#include <vector> + +using OpenSubdiv::Far::LimitStencilTable; +using OpenSubdiv::Far::StencilTable; +using OpenSubdiv::Osd::BufferDescriptor; +using OpenSubdiv::Osd::PatchArray; +using OpenSubdiv::Osd::PatchArrayVector; + +extern "C" char datatoc_glsl_compute_kernel_glsl[]; + +namespace blender { +namespace opensubdiv { + +template<class T> GLuint createSSBO(std::vector<T> const &src) +{ + if (src.empty()) { + return 0; + } + + GLuint devicePtr = 0; + +#if defined(GL_ARB_direct_state_access) + if (GLEW_ARB_direct_state_access) { + glCreateBuffers(1, &devicePtr); + glNamedBufferData(devicePtr, src.size() * sizeof(T), &src.at(0), GL_STATIC_DRAW); + } + else +#endif + { + GLint prev = 0; + glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, &prev); + glGenBuffers(1, &devicePtr); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, devicePtr); + glBufferData(GL_SHADER_STORAGE_BUFFER, src.size() * sizeof(T), &src.at(0), GL_STATIC_DRAW); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, prev); + } + + return devicePtr; +} + +GLStencilTableSSBO::GLStencilTableSSBO(StencilTable const *stencilTable) +{ + _numStencils = stencilTable->GetNumStencils(); + if (_numStencils > 0) { + _sizes = createSSBO(stencilTable->GetSizes()); + _offsets = createSSBO(stencilTable->GetOffsets()); + _indices = createSSBO(stencilTable->GetControlIndices()); + _weights = createSSBO(stencilTable->GetWeights()); + _duWeights = _dvWeights = 0; + _duuWeights = _duvWeights = _dvvWeights = 0; + } + else { + _sizes = _offsets = _indices = _weights = 0; + _duWeights = _dvWeights = 0; + _duuWeights = _duvWeights = _dvvWeights = 0; + } +} + +GLStencilTableSSBO::GLStencilTableSSBO(LimitStencilTable const *limitStencilTable) +{ + _numStencils = limitStencilTable->GetNumStencils(); + if (_numStencils > 0) { + _sizes = createSSBO(limitStencilTable->GetSizes()); + _offsets = createSSBO(limitStencilTable->GetOffsets()); + _indices = createSSBO(limitStencilTable->GetControlIndices()); + _weights = createSSBO(limitStencilTable->GetWeights()); + _duWeights = createSSBO(limitStencilTable->GetDuWeights()); + _dvWeights = createSSBO(limitStencilTable->GetDvWeights()); + _duuWeights = createSSBO(limitStencilTable->GetDuuWeights()); + _duvWeights = createSSBO(limitStencilTable->GetDuvWeights()); + _dvvWeights = createSSBO(limitStencilTable->GetDvvWeights()); + } + else { + _sizes = _offsets = _indices = _weights = 0; + _duWeights = _dvWeights = 0; + _duuWeights = _duvWeights = _dvvWeights = 0; + } +} + +GLStencilTableSSBO::~GLStencilTableSSBO() +{ + if (_sizes) + glDeleteBuffers(1, &_sizes); + if (_offsets) + glDeleteBuffers(1, &_offsets); + if (_indices) + glDeleteBuffers(1, &_indices); + if (_weights) + glDeleteBuffers(1, &_weights); + if (_duWeights) + glDeleteBuffers(1, &_duWeights); + if (_dvWeights) + glDeleteBuffers(1, &_dvWeights); + if (_duuWeights) + glDeleteBuffers(1, &_duuWeights); + if (_duvWeights) + glDeleteBuffers(1, &_duvWeights); + if (_dvvWeights) + glDeleteBuffers(1, &_dvvWeights); +} + +// --------------------------------------------------------------------------- + +GLComputeEvaluator::GLComputeEvaluator() : _workGroupSize(64), _patchArraysSSBO(0) +{ + memset((void *)&_stencilKernel, 0, sizeof(_stencilKernel)); + memset((void *)&_patchKernel, 0, sizeof(_patchKernel)); +} + +GLComputeEvaluator::~GLComputeEvaluator() +{ + if (_patchArraysSSBO) { + glDeleteBuffers(1, &_patchArraysSSBO); + } +} + +static GLuint compileKernel(BufferDescriptor const &srcDesc, + BufferDescriptor const &dstDesc, + BufferDescriptor const &duDesc, + BufferDescriptor const &dvDesc, + BufferDescriptor const &duuDesc, + BufferDescriptor const &duvDesc, + BufferDescriptor const &dvvDesc, + const char *kernelDefine, + int workGroupSize) +{ + GLuint program = glCreateProgram(); + + GLuint shader = glCreateShader(GL_COMPUTE_SHADER); + + std::string patchBasisShaderSource = + OpenSubdiv::Osd::GLSLPatchShaderSource::GetPatchBasisShaderSource(); + const char *patchBasisShaderSourceDefine = "#define OSD_PATCH_BASIS_GLSL\n"; + + std::ostringstream defines; + defines << "#define LENGTH " << srcDesc.length << "\n" + << "#define SRC_STRIDE " << srcDesc.stride << "\n" + << "#define DST_STRIDE " << dstDesc.stride << "\n" + << "#define WORK_GROUP_SIZE " << workGroupSize << "\n" + << kernelDefine << "\n" + << patchBasisShaderSourceDefine << "\n"; + + bool deriv1 = (duDesc.length > 0 || dvDesc.length > 0); + bool deriv2 = (duuDesc.length > 0 || duvDesc.length > 0 || dvvDesc.length > 0); + if (deriv1) { + defines << "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n"; + } + if (deriv2) { + defines << "#define OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES\n"; + } + + std::string defineStr = defines.str(); + + const char *shaderSources[4] = {"#version 430\n", 0, 0, 0}; + + shaderSources[1] = defineStr.c_str(); + shaderSources[2] = patchBasisShaderSource.c_str(); + shaderSources[3] = datatoc_glsl_compute_kernel_glsl; + glShaderSource(shader, 4, shaderSources, NULL); + glCompileShader(shader); + glAttachShader(program, shader); + + GLint linked = 0; + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &linked); + + if (linked == GL_FALSE) { + char buffer[1024]; + glGetShaderInfoLog(shader, 1024, NULL, buffer); + OpenSubdiv::Far::Error(OpenSubdiv::Far::FAR_RUNTIME_ERROR, buffer); + + glGetProgramInfoLog(program, 1024, NULL, buffer); + OpenSubdiv::Far::Error(OpenSubdiv::Far::FAR_RUNTIME_ERROR, buffer); + + glDeleteProgram(program); + return 0; + } + + glDeleteShader(shader); + + return program; +} + +bool GLComputeEvaluator::Compile(BufferDescriptor const &srcDesc, + BufferDescriptor const &dstDesc, + BufferDescriptor const &duDesc, + BufferDescriptor const &dvDesc, + BufferDescriptor const &duuDesc, + BufferDescriptor const &duvDesc, + BufferDescriptor const &dvvDesc) +{ + + // create a stencil kernel + if (!_stencilKernel.Compile( + srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, _workGroupSize)) { + return false; + } + + // create a patch kernel + if (!_patchKernel.Compile( + srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, _workGroupSize)) { + return false; + } + + // create a patch arrays buffer + if (!_patchArraysSSBO) { + glGenBuffers(1, &_patchArraysSSBO); + } + + return true; +} + +/* static */ +void GLComputeEvaluator::Synchronize(void * /*kernel*/) +{ + // XXX: this is currently just for the performance measuring purpose. + // need to be reimplemented by fence and sync. + glFinish(); +} + +int GLComputeEvaluator::GetDispatchSize(int count) const +{ + return (count + _workGroupSize - 1) / _workGroupSize; +} + +void GLComputeEvaluator::DispatchCompute(int totalDispatchSize) const +{ + int maxWorkGroupCount[2] = {0, 0}; + + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &maxWorkGroupCount[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &maxWorkGroupCount[1]); + + const GLuint maxResX = static_cast<GLuint>(maxWorkGroupCount[0]); + + const int dispatchSize = GetDispatchSize(totalDispatchSize); + GLuint dispatchRX = static_cast<GLuint>(dispatchSize); + GLuint dispatchRY = 1u; + if (dispatchRX > maxResX) { + /* Since there are some limitations with regards to the maximum work group size (could be as + * low as 64k elements per call), we split the number elements into a "2d" number, with the + * final index being computed as `res_x + res_y * max_work_group_size`. Even with a maximum + * work group size of 64k, that still leaves us with roughly `64k * 64k = 4` billion elements + * total, which should be enough. If not, we could also use the 3rd dimension. */ + /* TODO(fclem): We could dispatch fewer groups if we compute the prime factorization and + * get the smallest rect fitting the requirements. */ + dispatchRX = dispatchRY = std::ceil(std::sqrt(dispatchSize)); + /* Avoid a completely empty dispatch line caused by rounding. */ + if ((dispatchRX * (dispatchRY - 1)) >= dispatchSize) { + dispatchRY -= 1; + } + } + + /* X and Y dimensions may have different limits so the above computation may not be right, but + * even with the standard 64k minimum on all dimensions we still have a lot of room. Therefore, + * we presume it all fits. */ + assert(dispatchRY < static_cast<GLuint>(maxWorkGroupCount[1])); + + glDispatchCompute(dispatchRX, dispatchRY, 1); +} + +bool GLComputeEvaluator::EvalStencils(GLuint srcBuffer, + BufferDescriptor const &srcDesc, + GLuint dstBuffer, + BufferDescriptor const &dstDesc, + GLuint duBuffer, + BufferDescriptor const &duDesc, + GLuint dvBuffer, + BufferDescriptor const &dvDesc, + GLuint sizesBuffer, + GLuint offsetsBuffer, + GLuint indicesBuffer, + GLuint weightsBuffer, + GLuint duWeightsBuffer, + GLuint dvWeightsBuffer, + int start, + int end) const +{ + + return EvalStencils(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + 0, + BufferDescriptor(), + 0, + BufferDescriptor(), + 0, + BufferDescriptor(), + sizesBuffer, + offsetsBuffer, + indicesBuffer, + weightsBuffer, + duWeightsBuffer, + dvWeightsBuffer, + 0, + 0, + 0, + start, + end); +} + +bool GLComputeEvaluator::EvalStencils(GLuint srcBuffer, + BufferDescriptor const &srcDesc, + GLuint dstBuffer, + BufferDescriptor const &dstDesc, + GLuint duBuffer, + BufferDescriptor const &duDesc, + GLuint dvBuffer, + BufferDescriptor const &dvDesc, + GLuint duuBuffer, + BufferDescriptor const &duuDesc, + GLuint duvBuffer, + BufferDescriptor const &duvDesc, + GLuint dvvBuffer, + BufferDescriptor const &dvvDesc, + GLuint sizesBuffer, + GLuint offsetsBuffer, + GLuint indicesBuffer, + GLuint weightsBuffer, + GLuint duWeightsBuffer, + GLuint dvWeightsBuffer, + GLuint duuWeightsBuffer, + GLuint duvWeightsBuffer, + GLuint dvvWeightsBuffer, + int start, + int end) const +{ + + if (!_stencilKernel.program) + return false; + int count = end - start; + if (count <= 0) { + return true; + } + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, srcBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, dstBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, duBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, dvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, duuBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, duvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, dvvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, sizesBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, offsetsBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, indicesBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, weightsBuffer); + if (duWeightsBuffer) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, duWeightsBuffer); + if (dvWeightsBuffer) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 9, dvWeightsBuffer); + if (duuWeightsBuffer) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 13, duuWeightsBuffer); + if (duvWeightsBuffer) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 14, duvWeightsBuffer); + if (dvvWeightsBuffer) + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 15, dvvWeightsBuffer); + + glUseProgram(_stencilKernel.program); + + glUniform1i(_stencilKernel.uniformStart, start); + glUniform1i(_stencilKernel.uniformEnd, end); + glUniform1i(_stencilKernel.uniformSrcOffset, srcDesc.offset); + glUniform1i(_stencilKernel.uniformDstOffset, dstDesc.offset); + if (_stencilKernel.uniformDuDesc > 0) { + glUniform3i(_stencilKernel.uniformDuDesc, duDesc.offset, duDesc.length, duDesc.stride); + } + if (_stencilKernel.uniformDvDesc > 0) { + glUniform3i(_stencilKernel.uniformDvDesc, dvDesc.offset, dvDesc.length, dvDesc.stride); + } + if (_stencilKernel.uniformDuuDesc > 0) { + glUniform3i(_stencilKernel.uniformDuuDesc, duuDesc.offset, duuDesc.length, duuDesc.stride); + } + if (_stencilKernel.uniformDuvDesc > 0) { + glUniform3i(_stencilKernel.uniformDuvDesc, duvDesc.offset, duvDesc.length, duvDesc.stride); + } + if (_stencilKernel.uniformDvvDesc > 0) { + glUniform3i(_stencilKernel.uniformDvvDesc, dvvDesc.offset, dvvDesc.length, dvvDesc.stride); + } + + DispatchCompute(count); + + glUseProgram(0); + + glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); + for (int i = 0; i < 16; ++i) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, 0); + } + + return true; +} + +bool GLComputeEvaluator::EvalPatches(GLuint srcBuffer, + BufferDescriptor const &srcDesc, + GLuint dstBuffer, + BufferDescriptor const &dstDesc, + GLuint duBuffer, + BufferDescriptor const &duDesc, + GLuint dvBuffer, + BufferDescriptor const &dvDesc, + int numPatchCoords, + GLuint patchCoordsBuffer, + const PatchArrayVector &patchArrays, + GLuint patchIndexBuffer, + GLuint patchParamsBuffer) const +{ + + return EvalPatches(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + 0, + BufferDescriptor(), + 0, + BufferDescriptor(), + 0, + BufferDescriptor(), + numPatchCoords, + patchCoordsBuffer, + patchArrays, + patchIndexBuffer, + patchParamsBuffer); +} + +bool GLComputeEvaluator::EvalPatches(GLuint srcBuffer, + BufferDescriptor const &srcDesc, + GLuint dstBuffer, + BufferDescriptor const &dstDesc, + GLuint duBuffer, + BufferDescriptor const &duDesc, + GLuint dvBuffer, + BufferDescriptor const &dvDesc, + GLuint duuBuffer, + BufferDescriptor const &duuDesc, + GLuint duvBuffer, + BufferDescriptor const &duvDesc, + GLuint dvvBuffer, + BufferDescriptor const &dvvDesc, + int numPatchCoords, + GLuint patchCoordsBuffer, + const PatchArrayVector &patchArrays, + GLuint patchIndexBuffer, + GLuint patchParamsBuffer) const +{ + + if (!_patchKernel.program) + return false; + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, srcBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, dstBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, duBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, dvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, duuBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, duvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, dvvBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, patchCoordsBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, patchIndexBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, patchParamsBuffer); + + glUseProgram(_patchKernel.program); + + glUniform1i(_patchKernel.uniformSrcOffset, srcDesc.offset); + glUniform1i(_patchKernel.uniformDstOffset, dstDesc.offset); + + int patchArraySize = sizeof(PatchArray); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, _patchArraysSSBO); + glBufferData( + GL_SHADER_STORAGE_BUFFER, patchArrays.size() * patchArraySize, NULL, GL_STATIC_DRAW); + for (int i = 0; i < (int)patchArrays.size(); ++i) { + glBufferSubData( + GL_SHADER_STORAGE_BUFFER, i * patchArraySize, sizeof(PatchArray), &patchArrays[i]); + } + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, _patchArraysSSBO); + + if (_patchKernel.uniformDuDesc > 0) { + glUniform3i(_patchKernel.uniformDuDesc, duDesc.offset, duDesc.length, duDesc.stride); + } + if (_patchKernel.uniformDvDesc > 0) { + glUniform3i(_patchKernel.uniformDvDesc, dvDesc.offset, dvDesc.length, dvDesc.stride); + } + if (_patchKernel.uniformDuuDesc > 0) { + glUniform3i(_patchKernel.uniformDuuDesc, duuDesc.offset, duuDesc.length, duuDesc.stride); + } + if (_patchKernel.uniformDuvDesc > 0) { + glUniform3i(_patchKernel.uniformDuvDesc, duvDesc.offset, duvDesc.length, duvDesc.stride); + } + if (_patchKernel.uniformDvvDesc > 0) { + glUniform3i(_patchKernel.uniformDvvDesc, dvvDesc.offset, dvvDesc.length, dvvDesc.stride); + } + + DispatchCompute(numPatchCoords); + + glUseProgram(0); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, 0); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 10, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 11, 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 12, 0); + + return true; +} +// --------------------------------------------------------------------------- + +GLComputeEvaluator::_StencilKernel::_StencilKernel() : program(0) +{ +} +GLComputeEvaluator::_StencilKernel::~_StencilKernel() +{ + if (program) { + glDeleteProgram(program); + } +} + +bool GLComputeEvaluator::_StencilKernel::Compile(BufferDescriptor const &srcDesc, + BufferDescriptor const &dstDesc, + BufferDescriptor const &duDesc, + BufferDescriptor const &dvDesc, + BufferDescriptor const &duuDesc, + BufferDescriptor const &duvDesc, + BufferDescriptor const &dvvDesc, + int workGroupSize) +{ + // create stencil kernel + if (program) { + glDeleteProgram(program); + } + + const char *kernelDefine = "#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS\n"; + + program = compileKernel( + srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, kernelDefine, workGroupSize); + if (program == 0) + return false; + + // cache uniform locations (TODO: use uniform block) + uniformStart = glGetUniformLocation(program, "batchStart"); + uniformEnd = glGetUniformLocation(program, "batchEnd"); + uniformSrcOffset = glGetUniformLocation(program, "srcOffset"); + uniformDstOffset = glGetUniformLocation(program, "dstOffset"); + uniformDuDesc = glGetUniformLocation(program, "duDesc"); + uniformDvDesc = glGetUniformLocation(program, "dvDesc"); + uniformDuuDesc = glGetUniformLocation(program, "duuDesc"); + uniformDuvDesc = glGetUniformLocation(program, "duvDesc"); + uniformDvvDesc = glGetUniformLocation(program, "dvvDesc"); + + return true; +} + +// --------------------------------------------------------------------------- + +GLComputeEvaluator::_PatchKernel::_PatchKernel() : program(0) +{ +} +GLComputeEvaluator::_PatchKernel::~_PatchKernel() +{ + if (program) { + glDeleteProgram(program); + } +} + +bool GLComputeEvaluator::_PatchKernel::Compile(BufferDescriptor const &srcDesc, + BufferDescriptor const &dstDesc, + BufferDescriptor const &duDesc, + BufferDescriptor const &dvDesc, + BufferDescriptor const &duuDesc, + BufferDescriptor const &duvDesc, + BufferDescriptor const &dvvDesc, + int workGroupSize) +{ + // create stencil kernel + if (program) { + glDeleteProgram(program); + } + + const char *kernelDefine = "#define OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES\n"; + + program = compileKernel( + srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc, kernelDefine, workGroupSize); + if (program == 0) + return false; + + // cache uniform locations + uniformSrcOffset = glGetUniformLocation(program, "srcOffset"); + uniformDstOffset = glGetUniformLocation(program, "dstOffset"); + uniformPatchArray = glGetUniformLocation(program, "patchArray"); + uniformDuDesc = glGetUniformLocation(program, "duDesc"); + uniformDvDesc = glGetUniformLocation(program, "dvDesc"); + uniformDuuDesc = glGetUniformLocation(program, "duuDesc"); + uniformDuvDesc = glGetUniformLocation(program, "duvDesc"); + uniformDvvDesc = glGetUniformLocation(program, "dvvDesc"); + + return true; +} + +} // namespace opensubdiv +} // namespace blender diff --git a/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h new file mode 100644 index 00000000000..85c12f73b08 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/gl_compute_evaluator.h @@ -0,0 +1,2465 @@ +// +// Copyright 2015 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// + +#ifndef OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_ +#define OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_ + +#include <opensubdiv/osd/bufferDescriptor.h> +#include <opensubdiv/osd/opengl.h> +#include <opensubdiv/osd/types.h> +#include <opensubdiv/version.h> + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { +namespace Far { +class LimitStencilTable; +class StencilTable; +} // namespace Far +} // namespace OPENSUBDIV_VERSION +} // namespace OpenSubdiv + +namespace blender { +namespace opensubdiv { + +/// \brief GL stencil table (Shader Storage buffer) +/// +/// This class is a GLSL SSBO representation of OpenSubdiv::Far::StencilTable. +/// +/// GLSLComputeKernel consumes this table to apply stencils +/// +class GLStencilTableSSBO { + public: + static GLStencilTableSSBO *Create(OpenSubdiv::Far::StencilTable const *stencilTable, + void *deviceContext = NULL) + { + (void)deviceContext; // unused + return new GLStencilTableSSBO(stencilTable); + } + static GLStencilTableSSBO *Create(OpenSubdiv::Far::LimitStencilTable const *limitStencilTable, + void *deviceContext = NULL) + { + (void)deviceContext; // unused + return new GLStencilTableSSBO(limitStencilTable); + } + + explicit GLStencilTableSSBO(OpenSubdiv::Far::StencilTable const *stencilTable); + explicit GLStencilTableSSBO(OpenSubdiv::Far::LimitStencilTable const *limitStencilTable); + ~GLStencilTableSSBO(); + + // interfaces needed for GLSLComputeKernel + GLuint GetSizesBuffer() const + { + return _sizes; + } + GLuint GetOffsetsBuffer() const + { + return _offsets; + } + GLuint GetIndicesBuffer() const + { + return _indices; + } + GLuint GetWeightsBuffer() const + { + return _weights; + } + GLuint GetDuWeightsBuffer() const + { + return _duWeights; + } + GLuint GetDvWeightsBuffer() const + { + return _dvWeights; + } + GLuint GetDuuWeightsBuffer() const + { + return _duuWeights; + } + GLuint GetDuvWeightsBuffer() const + { + return _duvWeights; + } + GLuint GetDvvWeightsBuffer() const + { + return _dvvWeights; + } + int GetNumStencils() const + { + return _numStencils; + } + + private: + GLuint _sizes; + GLuint _offsets; + GLuint _indices; + GLuint _weights; + GLuint _duWeights; + GLuint _dvWeights; + GLuint _duuWeights; + GLuint _duvWeights; + GLuint _dvvWeights; + int _numStencils; +}; + +// --------------------------------------------------------------------------- + +class GLComputeEvaluator { + public: + typedef bool Instantiatable; + static GLComputeEvaluator *Create(OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + void *deviceContext = NULL) + { + return Create(srcDesc, + dstDesc, + duDesc, + dvDesc, + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor(), + deviceContext); + } + + static GLComputeEvaluator *Create(OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + void *deviceContext = NULL) + { + (void)deviceContext; // not used + GLComputeEvaluator *instance = new GLComputeEvaluator(); + if (instance->Compile(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc)) + return instance; + delete instance; + return NULL; + } + + /// Constructor. + GLComputeEvaluator(); + + /// Destructor. note that the GL context must be made current. + ~GLComputeEvaluator(); + + /// ---------------------------------------------------------------------- + /// + /// Stencil evaluations with StencilTable + /// + /// ---------------------------------------------------------------------- + + /// \brief Generic static stencil function. This function has a same + /// signature as other device kernels have so that it can be called + /// transparently from OsdMesh template interface. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLSL kernel + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + static bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + STENCIL_TABLE const *stencilTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalStencils(srcBuffer, srcDesc, dstBuffer, dstDesc, stencilTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, + dstDesc, + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor()); + if (instance) { + bool r = instance->EvalStencils(srcBuffer, srcDesc, dstBuffer, dstDesc, stencilTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic static stencil function. This function has a same + /// signature as other device kernels have so that it can be called + /// transparently from OsdMesh template interface. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLSL kernel + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + static bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + STENCIL_TABLE const *stencilTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalStencils(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + stencilTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc); + if (instance) { + bool r = instance->EvalStencils(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + stencilTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic static stencil function. This function has a same + /// signature as other device kernels have so that it can be called + /// transparently from OsdMesh template interface. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLSL kernel + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + static bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + STENCIL_TABLE const *stencilTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalStencils(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + stencilTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc); + if (instance) { + bool r = instance->EvalStencils(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + stencilTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic stencil function. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + STENCIL_TABLE const *stencilTable) const + { + return EvalStencils(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + 0, + OpenSubdiv::Osd::BufferDescriptor(), + 0, + OpenSubdiv::Osd::BufferDescriptor(), + stencilTable->GetSizesBuffer(), + stencilTable->GetOffsetsBuffer(), + stencilTable->GetIndicesBuffer(), + stencilTable->GetWeightsBuffer(), + 0, + 0, + /* start = */ 0, + /* end = */ stencilTable->GetNumStencils()); + } + + /// \brief Generic stencil function. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + STENCIL_TABLE const *stencilTable) const + { + return EvalStencils(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + stencilTable->GetSizesBuffer(), + stencilTable->GetOffsetsBuffer(), + stencilTable->GetIndicesBuffer(), + stencilTable->GetWeightsBuffer(), + stencilTable->GetDuWeightsBuffer(), + stencilTable->GetDvWeightsBuffer(), + /* start = */ 0, + /* end = */ stencilTable->GetNumStencils()); + } + + /// \brief Generic stencil function. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param stencilTable stencil table to be applied. The table must have + /// SSBO interfaces. + /// + template<typename SRC_BUFFER, typename DST_BUFFER, typename STENCIL_TABLE> + bool EvalStencils(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + STENCIL_TABLE const *stencilTable) const + { + return EvalStencils(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + duuBuffer->BindVBO(), + duuDesc, + duvBuffer->BindVBO(), + duvDesc, + dvvBuffer->BindVBO(), + dvvDesc, + stencilTable->GetSizesBuffer(), + stencilTable->GetOffsetsBuffer(), + stencilTable->GetIndicesBuffer(), + stencilTable->GetWeightsBuffer(), + stencilTable->GetDuWeightsBuffer(), + stencilTable->GetDvWeightsBuffer(), + stencilTable->GetDuuWeightsBuffer(), + stencilTable->GetDuvWeightsBuffer(), + stencilTable->GetDvvWeightsBuffer(), + /* start = */ 0, + /* end = */ stencilTable->GetNumStencils()); + } + + /// \brief Dispatch the GLSL compute kernel on GPU asynchronously + /// returns false if the kernel hasn't been compiled yet. + /// + /// @param srcBuffer GL buffer of input primvar source data + /// + /// @param srcDesc vertex buffer descriptor for the srcBuffer + /// + /// @param dstBuffer GL buffer of output primvar destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer GL buffer of output derivative wrt u + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer GL buffer of output derivative wrt v + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param sizesBuffer GL buffer of the sizes in the stencil table + /// + /// @param offsetsBuffer GL buffer of the offsets in the stencil table + /// + /// @param indicesBuffer GL buffer of the indices in the stencil table + /// + /// @param weightsBuffer GL buffer of the weights in the stencil table + /// + /// @param duWeightsBuffer GL buffer of the du weights in the stencil table + /// + /// @param dvWeightsBuffer GL buffer of the dv weights in the stencil table + /// + /// @param start start index of stencil table + /// + /// @param end end index of stencil table + /// + bool EvalStencils(GLuint srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + GLuint dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + GLuint duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + GLuint dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + GLuint sizesBuffer, + GLuint offsetsBuffer, + GLuint indicesBuffer, + GLuint weightsBuffer, + GLuint duWeightsBuffer, + GLuint dvWeightsBuffer, + int start, + int end) const; + + /// \brief Dispatch the GLSL compute kernel on GPU asynchronously + /// returns false if the kernel hasn't been compiled yet. + /// + /// @param srcBuffer GL buffer of input primvar source data + /// + /// @param srcDesc vertex buffer descriptor for the srcBuffer + /// + /// @param dstBuffer GL buffer of output primvar destination data + /// + /// @param dstDesc vertex buffer descriptor for the dstBuffer + /// + /// @param duBuffer GL buffer of output derivative wrt u + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer GL buffer of output derivative wrt v + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer GL buffer of output 2nd derivative wrt u + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer GL buffer of output 2nd derivative wrt u and v + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer GL buffer of output 2nd derivative wrt v + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param sizesBuffer GL buffer of the sizes in the stencil table + /// + /// @param offsetsBuffer GL buffer of the offsets in the stencil table + /// + /// @param indicesBuffer GL buffer of the indices in the stencil table + /// + /// @param weightsBuffer GL buffer of the weights in the stencil table + /// + /// @param duWeightsBuffer GL buffer of the du weights in the stencil table + /// + /// @param dvWeightsBuffer GL buffer of the dv weights in the stencil table + /// + /// @param duuWeightsBuffer GL buffer of the duu weights in the stencil table + /// + /// @param duvWeightsBuffer GL buffer of the duv weights in the stencil table + /// + /// @param dvvWeightsBuffer GL buffer of the dvv weights in the stencil table + /// + /// @param start start index of stencil table + /// + /// @param end end index of stencil table + /// + bool EvalStencils(GLuint srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + GLuint dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + GLuint duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + GLuint dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + GLuint duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + GLuint duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + GLuint dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + GLuint sizesBuffer, + GLuint offsetsBuffer, + GLuint indicesBuffer, + GLuint weightsBuffer, + GLuint duWeightsBuffer, + GLuint dvWeightsBuffer, + GLuint duuWeightsBuffer, + GLuint duvWeightsBuffer, + GLuint dvvWeightsBuffer, + int start, + int end) const; + + /// ---------------------------------------------------------------------- + /// + /// Limit evaluations with PatchTable + /// + /// ---------------------------------------------------------------------- + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatches( + srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, + dstDesc, + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor()); + if (instance) { + bool r = instance->EvalPatches( + srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatches(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc); + if (instance) { + bool r = instance->EvalPatches(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatches(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc); + if (instance) { + bool r = instance->EvalPatches(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + 0, + OpenSubdiv::Osd::BufferDescriptor(), + 0, + OpenSubdiv::Osd::BufferDescriptor(), + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetPatchArrays(), + patchTable->GetPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + /// \brief Generic limit eval function with derivatives. This function has + /// a same signature as other device kernels have so that it can be + /// called in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetPatchArrays(), + patchTable->GetPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + /// \brief Generic limit eval function with derivatives. This function has + /// a same signature as other device kernels have so that it can be + /// called in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatches(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + duuBuffer->BindVBO(), + duuDesc, + duvBuffer->BindVBO(), + duvDesc, + dvvBuffer->BindVBO(), + dvvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetPatchArrays(), + patchTable->GetPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + bool EvalPatches(GLuint srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + GLuint dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + GLuint duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + GLuint dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + GLuint patchCoordsBuffer, + const OpenSubdiv::Osd::PatchArrayVector &patchArrays, + GLuint patchIndexBuffer, + GLuint patchParamsBuffer) const; + + bool EvalPatches(GLuint srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + GLuint dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + GLuint duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + GLuint dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + GLuint duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + GLuint duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + GLuint dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + GLuint patchCoordsBuffer, + const OpenSubdiv::Osd::PatchArrayVector &patchArrays, + GLuint patchIndexBuffer, + GLuint patchParamsBuffer) const; + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesVarying( + srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, + dstDesc, + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor()); + if (instance) { + bool r = instance->EvalPatchesVarying( + srcBuffer, srcDesc, dstBuffer, dstDesc, numPatchCoords, patchCoords, patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + 0, + OpenSubdiv::Osd::BufferDescriptor(), + 0, + OpenSubdiv::Osd::BufferDescriptor(), + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetVaryingPatchArrays(), + patchTable->GetVaryingPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc); + if (instance) { + bool r = instance->EvalPatchesVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetVaryingPatchArrays(), + patchTable->GetVaryingPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc); + if (instance) { + bool r = instance->EvalPatchesVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + duuBuffer->BindVBO(), + duuDesc, + duvBuffer->BindVBO(), + duvDesc, + dvvBuffer->BindVBO(), + dvvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetVaryingPatchArrays(), + patchTable->GetVaryingPatchIndexBuffer(), + patchTable->GetPatchParamBuffer()); + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, + dstDesc, + OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor()); + if (instance) { + bool r = instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel = 0) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + 0, + OpenSubdiv::Osd::BufferDescriptor(), + 0, + OpenSubdiv::Osd::BufferDescriptor(), + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetFVarPatchArrays(fvarChannel), + patchTable->GetFVarPatchIndexBuffer(fvarChannel), + patchTable->GetFVarPatchParamBuffer(fvarChannel)); + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc); + if (instance) { + bool r = instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel = 0) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetFVarPatchArrays(fvarChannel), + patchTable->GetFVarPatchIndexBuffer(fvarChannel), + patchTable->GetFVarPatchParamBuffer(fvarChannel)); + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + /// @param instance cached compiled instance. Clients are supposed to + /// pre-compile an instance of this class and provide + /// to this function. If it's null the kernel still + /// compute by instantiating on-demand kernel although + /// it may cause a performance problem. + /// + /// @param deviceContext not used in the GLXFB evaluator + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + static bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel, + GLComputeEvaluator const *instance, + void *deviceContext = NULL) + { + + if (instance) { + return instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + } + else { + // Create an instance on demand (slow) + (void)deviceContext; // unused + instance = Create(srcDesc, dstDesc, duDesc, dvDesc, duuDesc, duvDesc, dvvDesc); + if (instance) { + bool r = instance->EvalPatchesFaceVarying(srcBuffer, + srcDesc, + dstBuffer, + dstDesc, + duBuffer, + duDesc, + dvBuffer, + dvDesc, + duuBuffer, + duuDesc, + duvBuffer, + duvDesc, + dvvBuffer, + dvvDesc, + numPatchCoords, + patchCoords, + patchTable, + fvarChannel); + delete instance; + return r; + } + return false; + } + } + + /// \brief Generic limit eval function. This function has a same + /// signature as other device kernels have so that it can be called + /// in the same way. + /// + /// @param srcBuffer Input primvar buffer. + /// must have BindVBO() method returning a GL + /// buffer object of source data + /// + /// @param srcDesc vertex buffer descriptor for the input buffer + /// + /// @param dstBuffer Output primvar buffer + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dstDesc vertex buffer descriptor for the output buffer + /// + /// @param duBuffer Output buffer derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duDesc vertex buffer descriptor for the duBuffer + /// + /// @param dvBuffer Output buffer derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvDesc vertex buffer descriptor for the dvBuffer + /// + /// @param duuBuffer Output buffer 2nd derivative wrt u + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duuDesc vertex buffer descriptor for the duuBuffer + /// + /// @param duvBuffer Output buffer 2nd derivative wrt u and v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param duvDesc vertex buffer descriptor for the duvBuffer + /// + /// @param dvvBuffer Output buffer 2nd derivative wrt v + /// must have BindVBO() method returning a GL + /// buffer object of destination data + /// + /// @param dvvDesc vertex buffer descriptor for the dvvBuffer + /// + /// @param numPatchCoords number of patchCoords. + /// + /// @param patchCoords array of locations to be evaluated. + /// must have BindVBO() method returning an + /// array of PatchCoord struct in VBO. + /// + /// @param patchTable GLPatchTable or equivalent + /// + /// @param fvarChannel face-varying channel + /// + template<typename SRC_BUFFER, + typename DST_BUFFER, + typename PATCHCOORD_BUFFER, + typename PATCH_TABLE> + bool EvalPatchesFaceVarying(SRC_BUFFER *srcBuffer, + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + DST_BUFFER *dstBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + DST_BUFFER *duBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + DST_BUFFER *dvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + DST_BUFFER *duuBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + DST_BUFFER *duvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + DST_BUFFER *dvvBuffer, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int numPatchCoords, + PATCHCOORD_BUFFER *patchCoords, + PATCH_TABLE *patchTable, + int fvarChannel = 0) const + { + + return EvalPatches(srcBuffer->BindVBO(), + srcDesc, + dstBuffer->BindVBO(), + dstDesc, + duBuffer->BindVBO(), + duDesc, + dvBuffer->BindVBO(), + dvDesc, + duuBuffer->BindVBO(), + duuDesc, + duvBuffer->BindVBO(), + duvDesc, + dvvBuffer->BindVBO(), + dvvDesc, + numPatchCoords, + patchCoords->BindVBO(), + patchTable->GetFVarPatchArrays(fvarChannel), + patchTable->GetFVarPatchIndexBuffer(fvarChannel), + patchTable->GetFVarPatchParamBuffer(fvarChannel)); + } + + /// ---------------------------------------------------------------------- + /// + /// Other methods + /// + /// ---------------------------------------------------------------------- + + /// Configure GLSL kernel. A valid GL context must be made current before + /// calling this function. Returns false if it fails to compile the kernel. + bool Compile( + OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + OpenSubdiv::Osd::BufferDescriptor const &duDesc = OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor const &dvDesc = OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor const &duuDesc = OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor const &duvDesc = OpenSubdiv::Osd::BufferDescriptor(), + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc = OpenSubdiv::Osd::BufferDescriptor()); + + /// Wait the dispatched kernel finishes. + static void Synchronize(void *deviceContext); + + private: + struct _StencilKernel { + _StencilKernel(); + ~_StencilKernel(); + bool Compile(OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int workGroupSize); + GLuint program; + GLuint uniformStart; + GLuint uniformEnd; + GLuint uniformSrcOffset; + GLuint uniformDstOffset; + GLuint uniformDuDesc; + GLuint uniformDvDesc; + GLuint uniformDuuDesc; + GLuint uniformDuvDesc; + GLuint uniformDvvDesc; + } _stencilKernel; + + struct _PatchKernel { + _PatchKernel(); + ~_PatchKernel(); + bool Compile(OpenSubdiv::Osd::BufferDescriptor const &srcDesc, + OpenSubdiv::Osd::BufferDescriptor const &dstDesc, + OpenSubdiv::Osd::BufferDescriptor const &duDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvDesc, + OpenSubdiv::Osd::BufferDescriptor const &duuDesc, + OpenSubdiv::Osd::BufferDescriptor const &duvDesc, + OpenSubdiv::Osd::BufferDescriptor const &dvvDesc, + int workGroupSize); + GLuint program; + GLuint uniformSrcOffset; + GLuint uniformDstOffset; + GLuint uniformPatchArray; + GLuint uniformDuDesc; + GLuint uniformDvDesc; + GLuint uniformDuuDesc; + GLuint uniformDuvDesc; + GLuint uniformDvvDesc; + } _patchKernel; + + int _workGroupSize; + GLuint _patchArraysSSBO; + + int GetDispatchSize(int count) const; + + void DispatchCompute(int totalDispatchSize) const; +}; +} // namespace opensubdiv +} // namespace blender + +#endif // OPENSUBDIV_GL_COMPUTE_EVALUATOR_H_ diff --git a/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl b/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl new file mode 100644 index 00000000000..2f60aee0999 --- /dev/null +++ b/intern/opensubdiv/internal/evaluator/shaders/glsl_compute_kernel.glsl @@ -0,0 +1,383 @@ +// +// Copyright 2013 Pixar +// +// Licensed under the Apache License, Version 2.0 (the "Apache License") +// with the following modification; you may not use this file except in +// compliance with the Apache License and the following modification to it: +// Section 6. Trademarks. is deleted and replaced with: +// +// 6. Trademarks. This License does not grant permission to use the trade +// names, trademarks, service marks, or product names of the Licensor +// and its affiliates, except as required to comply with Section 4(c) of +// the License and to reproduce the content of the NOTICE file. +// +// You may obtain a copy of the Apache License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the Apache License with the above modification is +// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the Apache License for the specific +// language governing permissions and limitations under the Apache License. +// + +//------------------------------------------------------------------------------ + +layout(local_size_x = WORK_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in; +layout(std430) buffer; + +// source and destination buffers + +uniform int srcOffset = 0; +uniform int dstOffset = 0; +layout(binding = 0) buffer src_buffer +{ + float srcVertexBuffer[]; +}; +layout(binding = 1) buffer dst_buffer +{ + float dstVertexBuffer[]; +}; + + // derivative buffers (if needed) + +#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES) +uniform ivec3 duDesc; +uniform ivec3 dvDesc; +layout(binding = 2) buffer du_buffer +{ + float duBuffer[]; +}; +layout(binding = 3) buffer dv_buffer +{ + float dvBuffer[]; +}; +#endif + +#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES) +uniform ivec3 duuDesc; +uniform ivec3 duvDesc; +uniform ivec3 dvvDesc; +layout(binding = 10) buffer duu_buffer +{ + float duuBuffer[]; +}; +layout(binding = 11) buffer duv_buffer +{ + float duvBuffer[]; +}; +layout(binding = 12) buffer dvv_buffer +{ + float dvvBuffer[]; +}; +#endif + + // stencil buffers + +#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS) + +uniform int batchStart = 0; +uniform int batchEnd = 0; +layout(binding = 4) buffer stencilSizes +{ + int _sizes[]; +}; +layout(binding = 5) buffer stencilOffsets +{ + int _offsets[]; +}; +layout(binding = 6) buffer stencilIndices +{ + int _indices[]; +}; +layout(binding = 7) buffer stencilWeights +{ + float _weights[]; +}; + +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES) +layout(binding = 8) buffer stencilDuWeights +{ + float _duWeights[]; +}; +layout(binding = 9) buffer stencilDvWeights +{ + float _dvWeights[]; +}; +# endif + +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES) +layout(binding = 13) buffer stencilDuuWeights +{ + float _duuWeights[]; +}; +layout(binding = 14) buffer stencilDuvWeights +{ + float _duvWeights[]; +}; +layout(binding = 15) buffer stencilDvvWeights +{ + float _dvvWeights[]; +}; +# endif + +uint getGlobalInvocationIndex() +{ + uint invocations_per_row = gl_WorkGroupSize.x * gl_NumWorkGroups.x; + return gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * invocations_per_row; +} + +#endif + +// patch buffers + +#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES) + +layout(binding = 4) buffer patchArray_buffer +{ + OsdPatchArray patchArrayBuffer[]; +}; +layout(binding = 5) buffer patchCoord_buffer +{ + OsdPatchCoord patchCoords[]; +}; +layout(binding = 6) buffer patchIndex_buffer +{ + int patchIndexBuffer[]; +}; +layout(binding = 7) buffer patchParam_buffer +{ + OsdPatchParam patchParamBuffer[]; +}; + +OsdPatchCoord GetPatchCoord(int coordIndex) +{ + return patchCoords[coordIndex]; +} + +OsdPatchArray GetPatchArray(int arrayIndex) +{ + return patchArrayBuffer[arrayIndex]; +} + +OsdPatchParam GetPatchParam(int patchIndex) +{ + return patchParamBuffer[patchIndex]; +} + +#endif + +//------------------------------------------------------------------------------ + +struct Vertex { + float vertexData[LENGTH]; +}; + +void clear(out Vertex v) +{ + for (int i = 0; i < LENGTH; ++i) { + v.vertexData[i] = 0; + } +} + +Vertex readVertex(int index) +{ + Vertex v; + int vertexIndex = srcOffset + index * SRC_STRIDE; + for (int i = 0; i < LENGTH; ++i) { + v.vertexData[i] = srcVertexBuffer[vertexIndex + i]; + } + return v; +} + +void writeVertex(int index, Vertex v) +{ + int vertexIndex = dstOffset + index * DST_STRIDE; + for (int i = 0; i < LENGTH; ++i) { + dstVertexBuffer[vertexIndex + i] = v.vertexData[i]; + } +} + +void addWithWeight(inout Vertex v, const Vertex src, float weight) +{ + for (int i = 0; i < LENGTH; ++i) { + v.vertexData[i] += weight * src.vertexData[i]; + } +} + +#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES) +void writeDu(int index, Vertex du) +{ + int duIndex = duDesc.x + index * duDesc.z; + for (int i = 0; i < LENGTH; ++i) { + duBuffer[duIndex + i] = du.vertexData[i]; + } +} + +void writeDv(int index, Vertex dv) +{ + int dvIndex = dvDesc.x + index * dvDesc.z; + for (int i = 0; i < LENGTH; ++i) { + dvBuffer[dvIndex + i] = dv.vertexData[i]; + } +} +#endif + +#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES) +void writeDuu(int index, Vertex duu) +{ + int duuIndex = duuDesc.x + index * duuDesc.z; + for (int i = 0; i < LENGTH; ++i) { + duuBuffer[duuIndex + i] = duu.vertexData[i]; + } +} + +void writeDuv(int index, Vertex duv) +{ + int duvIndex = duvDesc.x + index * duvDesc.z; + for (int i = 0; i < LENGTH; ++i) { + duvBuffer[duvIndex + i] = duv.vertexData[i]; + } +} + +void writeDvv(int index, Vertex dvv) +{ + int dvvIndex = dvvDesc.x + index * dvvDesc.z; + for (int i = 0; i < LENGTH; ++i) { + dvvBuffer[dvvIndex + i] = dvv.vertexData[i]; + } +} +#endif + +//------------------------------------------------------------------------------ +#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS) + +void main() +{ + int current = int(getGlobalInvocationIndex()) + batchStart; + + if (current >= batchEnd) { + return; + } + + Vertex dst; + clear(dst); + + int offset = _offsets[current], size = _sizes[current]; + + for (int stencil = 0; stencil < size; ++stencil) { + int vindex = offset + stencil; + addWithWeight(dst, readVertex(_indices[vindex]), _weights[vindex]); + } + + writeVertex(current, dst); + +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES) + Vertex du, dv; + clear(du); + clear(dv); + for (int i = 0; i < size; ++i) { + // expects the compiler optimizes readVertex out here. + Vertex src = readVertex(_indices[offset + i]); + addWithWeight(du, src, _duWeights[offset + i]); + addWithWeight(dv, src, _dvWeights[offset + i]); + } + + if (duDesc.y > 0) { // length + writeDu(current, du); + } + if (dvDesc.y > 0) { + writeDv(current, dv); + } +# endif +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES) + Vertex duu, duv, dvv; + clear(duu); + clear(duv); + clear(dvv); + for (int i = 0; i < size; ++i) { + // expects the compiler optimizes readVertex out here. + Vertex src = readVertex(_indices[offset + i]); + addWithWeight(duu, src, _duuWeights[offset + i]); + addWithWeight(duv, src, _duvWeights[offset + i]); + addWithWeight(dvv, src, _dvvWeights[offset + i]); + } + + if (duuDesc.y > 0) { // length + writeDuu(current, duu); + } + if (duvDesc.y > 0) { + writeDuv(current, duv); + } + if (dvvDesc.y > 0) { + writeDvv(current, dvv); + } +# endif +} + +#endif + +//------------------------------------------------------------------------------ +#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES) + +// PERFORMANCE: stride could be constant, but not as significant as length + +void main() +{ + + int current = int(gl_GlobalInvocationID.x); + + OsdPatchCoord coord = GetPatchCoord(current); + OsdPatchArray array = GetPatchArray(coord.arrayIndex); + OsdPatchParam param = GetPatchParam(coord.patchIndex); + + int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc; + + float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20]; + int nPoints = OsdEvaluatePatchBasis( + patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv); + + Vertex dst, du, dv, duu, duv, dvv; + clear(dst); + clear(du); + clear(dv); + clear(duu); + clear(duv); + clear(dvv); + + int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase); + + for (int cv = 0; cv < nPoints; ++cv) { + int index = patchIndexBuffer[indexBase + cv]; + addWithWeight(dst, readVertex(index), wP[cv]); + addWithWeight(du, readVertex(index), wDu[cv]); + addWithWeight(dv, readVertex(index), wDv[cv]); + addWithWeight(duu, readVertex(index), wDuu[cv]); + addWithWeight(duv, readVertex(index), wDuv[cv]); + addWithWeight(dvv, readVertex(index), wDvv[cv]); + } + writeVertex(current, dst); + +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES) + if (duDesc.y > 0) { // length + writeDu(current, du); + } + if (dvDesc.y > 0) { + writeDv(current, dv); + } +# endif +# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES) + if (duuDesc.y > 0) { // length + writeDuu(current, duu); + } + if (duvDesc.y > 0) { // length + writeDuv(current, duv); + } + if (dvvDesc.y > 0) { + writeDvv(current, dvv); + } +# endif +} + +#endif diff --git a/release/datafiles/blender_icons_update.py b/release/datafiles/blender_icons_update.py index f4299fc510f..c1cf8d6fcb0 100755 --- a/release/datafiles/blender_icons_update.py +++ b/release/datafiles/blender_icons_update.py @@ -26,8 +26,8 @@ if sys.platform[:3] == "win": env["SystemDrive"] = os.environ.get("SystemDrive", "") env["SystemRoot"] = os.environ.get("SystemRoot", "") -inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape") -blender_bin = os.environ.get("BLENDER_BIN", "blender") +inkscape_bin = "inkscape" +blender_bin = "blender" if sys.platform == 'darwin': inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape' @@ -36,6 +36,11 @@ if sys.platform == 'darwin': blender_app_path = '/Applications/Blender.app/Contents/MacOS/Blender' if os.path.exists(blender_app_path): blender_bin = blender_app_path + else: + blender_bin = "Blender" + +inkscape_bin = os.environ.get("INKSCAPE_BIN", inkscape_bin) +blender_bin = os.environ.get("BLENDER_BIN", blender_bin) cmd = ( inkscape_bin, diff --git a/release/datafiles/colormanagement/config.ocio b/release/datafiles/colormanagement/config.ocio index bd342a0577e..bdb04cbf9ce 100644 --- a/release/datafiles/colormanagement/config.ocio +++ b/release/datafiles/colormanagement/config.ocio @@ -9,7 +9,7 @@ # # See ocio-license.txt for details. -ocio_profile_version: 1 +ocio_profile_version: 2 search_path: "luts:filmic" strictparsing: true @@ -100,8 +100,7 @@ colorspaces: from_reference: !<GroupTransform> children: - !<FileTransform> {src: srgb_to_xyz.spimtx, interpolation: linear} - - !<FileTransform> {src: xyz_D65_to_E.spimtx, interpolation: linear} - - !<FileTransform> {src: xyz_to_aces.spimtx, interpolation: linear} + - !<BuiltinTransform> {style: "UTILITY - ACES-AP0_to_CIE-XYZ-D65_BFD", direction: inverse} - !<ColorSpace> name: nuke_rec709 diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject af8f04db7686d1851d3ac9091472ca21989c8ef +Subproject 2a5095eed3028e91624d27ca93e4c65f572b809 diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index 34168cc0126..7cf8158c42d 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -144,7 +144,7 @@ const UserDef U_default = { * so invert this by default, see: T67579. */ NDOF_ROTX_INVERT_AXIS | NDOF_ROTY_INVERT_AXIS | NDOF_ROTZ_INVERT_AXIS | NDOF_PANX_INVERT_AXIS | NDOF_PANY_INVERT_AXIS | NDOF_PANZ_INVERT_AXIS | - NDOF_ZOOM_INVERT), + NDOF_ZOOM_INVERT | NDOF_CAMERA_PAN_ZOOM), .image_draw_method = IMAGE_DRAW_METHOD_AUTO, .glalphaclip = 0.004, .autokey_mode = (AUTOKEY_MODE_NORMAL & ~AUTOKEY_ON), diff --git a/release/scripts/addons b/release/scripts/addons -Subproject b0274e50da58bc1f0086794a16029ec6e2e9b92 +Subproject 842c215b746f7e14f9299fa8ae50d2fecd870a9 diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 554150de87d..3e823f2b6b7 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -489,12 +489,7 @@ def disable_all(): def _blender_manual_url_prefix(): - if _bpy.app.version_cycle in {"rc", "release"}: - manual_version = "%d.%d" % _bpy.app.version[:2] - else: - manual_version = "dev" - - return "https://docs.blender.org/manual/en/" + manual_version + return "https://docs.blender.org/manual/en/%d.%d" % _bpy.app.version[:2] def module_bl_info(mod, *, info_basis=None): diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py index f34002741c6..6631461eaba 100644 --- a/release/scripts/modules/bl_keymap_utils/io.py +++ b/release/scripts/modules/bl_keymap_utils/io.py @@ -52,6 +52,8 @@ def kmi_args_as_data(kmi): s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True")) if (mod := kmi.key_modifier) and (mod != 'NONE'): s.append(f"\"key_modifier\": '{mod:s}'") + if (direction := kmi.direction) and (direction != 'ANY'): + s.append(f"\"direction\": '{direction:s}'") if kmi.repeat: if ( diff --git a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py index 604d1ffd547..6d41e290512 100644 --- a/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py +++ b/release/scripts/modules/bl_keymap_utils/keymap_from_toolbar.py @@ -189,6 +189,7 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True): 'VERTEX_GPENCIL': "gpencil_vertex_tool", 'SCULPT_GPENCIL': "gpencil_sculpt_tool", 'WEIGHT_GPENCIL': "gpencil_weight_tool", + 'SCULPT_CURVES': "curves_sculpt_tool", }.get(mode, None) else: attr = None diff --git a/release/scripts/modules/bl_keymap_utils/versioning.py b/release/scripts/modules/bl_keymap_utils/versioning.py index ee7cc5daceb..402c21f8ef1 100644 --- a/release/scripts/modules/bl_keymap_utils/versioning.py +++ b/release/scripts/modules/bl_keymap_utils/versioning.py @@ -30,4 +30,22 @@ def keyconfig_update(keyconfig_data, keyconfig_version): # Setting repeat true on other kinds of events is harmless. item_event["repeat"] = True + if keyconfig_version <= (3, 2, 5): + # Only copy once. + if not has_copy: + keyconfig_data = copy.deepcopy(keyconfig_data) + has_copy = True + + for _km_name, _km_parms, km_items_data in keyconfig_data: + for (_item_op, item_event, _item_prop) in km_items_data["items"]: + if ty_new := { + 'EVT_TWEAK_L': 'LEFTMOUSE', + 'EVT_TWEAK_M': 'MIDDLEMOUSE', + 'EVT_TWEAK_R': 'RIGHTMOUSE', + }.get(item_event.get("type")): + item_event["type"] = ty_new + if (value := item_event["value"]) != 'ANY': + item_event["direction"] = value + item_event["value"] = 'CLICK_DRAG' + return keyconfig_data diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py index 2676c00c655..5da98cd783d 100644 --- a/release/scripts/modules/rna_keymap_ui.py +++ b/release/scripts/modules/rna_keymap_ui.py @@ -180,6 +180,10 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level): subrow.prop(kmi, "type", text="") subrow.prop(kmi, "value", text="") + if map_type in {'KEYBOARD', 'MOUSE'} and kmi.value == 'CLICK_DRAG': + subrow = sub.row() + subrow.prop(kmi, "direction") + subrow = sub.row() subrow.scale_x = 0.75 subrow.prop(kmi, "any", toggle=True) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index dc2d0acbbee..4b10c29346e 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -3,10 +3,7 @@ import bpy -if bpy.app.version_cycle in {'rc', 'release'}: - manual_version = '%d.%d' % bpy.app.version[:2] -else: - manual_version = 'dev' +manual_version = '%d.%d' % bpy.app.version[:2] url_manual_prefix = "https://docs.blender.org/manual/en/" + manual_version + "/" @@ -50,7 +47,6 @@ url_manual_mapping = ( ("bpy.types.lineartgpencilmodifier.use_offset_towards_custom_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-offset-towards-custom-camera"), ("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"), ("bpy.types.cyclesobjectsettings.shadow_terminator_geometry_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-geometry-offset"), - ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"), ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"), @@ -114,6 +110,7 @@ url_manual_mapping = ( ("bpy.types.gpencilsculptsettings.intersection_threshold*", "grease_pencil/modes/draw/tools/cutter.html#bpy-types-gpencilsculptsettings-intersection-threshold"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("bpy.types.lineartgpencilmodifier.use_intersection_mask*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection-mask"), + ("bpy.types.lineartgpencilmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-collection"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), ("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"), ("bpy.types.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"), @@ -123,7 +120,6 @@ url_manual_mapping = ( ("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"), ("bpy.types.brushgpencilsettings.eraser_strength_factor*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-eraser-strength-factor"), ("bpy.types.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"), - ("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"), ("bpy.types.cyclesrendersettings.denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-input-passes"), ("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), @@ -358,6 +354,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"), ("bpy.types.viewlayer.use_pass_cryptomatte_object*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-object"), ("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"), + ("bpy.ops.ed.lib_id_generate_preview_from_object*", "editors/asset_browser.html#bpy-ops-ed-lib-id-generate-preview-from-object"), ("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"), ("bpy.types.brushgpencilsettings.random_strength*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-random-strength"), ("bpy.types.brushgpencilsettings.simplify_factor*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-simplify-factor"), @@ -379,6 +376,7 @@ url_manual_mapping = ( ("bpy.types.freestylelinestyle.use_split_pattern*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-pattern"), ("bpy.types.freestylesettings.use_view_map_cache*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-view-map-cache"), ("bpy.types.geometrynodecurvehandletypeselection*", "modeling/geometry_nodes/curve/handle_type_selection.html#bpy-types-geometrynodecurvehandletypeselection"), + ("bpy.types.geometrynodeinputmeshvertexneighbors*", "modeling/geometry_nodes/mesh/vertex_neighbors.html#bpy-types-geometrynodeinputmeshvertexneighbors"), ("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"), ("bpy.types.lineartgpencilmodifier.source_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-source-camera"), ("bpy.types.lineartgpencilmodifier.use_face_mark*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark"), @@ -405,7 +403,6 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_fill_limit*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-use-fill-limit"), ("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"), ("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"), - ("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"), ("bpy.types.cyclesrendersettings.fast_gi_method*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-fast-gi-method"), ("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"), ("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"), @@ -458,6 +455,7 @@ url_manual_mapping = ( ("bpy.types.cyclescamerasettings.panorama_type*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-panorama-type"), ("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-camera"), ("bpy.types.cyclesrendersettings.film_exposure*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-exposure"), + ("bpy.types.cyclesrendersettings.sample_offset*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sample-offset"), ("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-texture-limit"), ("bpy.types.cyclesrendersettings.use_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-denoising"), ("bpy.types.editbone.bbone_custom_handle_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-start"), @@ -483,6 +481,8 @@ url_manual_mapping = ( ("bpy.types.freestylelinestyle.use_dashed_line*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-dashed-line"), ("bpy.types.freestylelinestyle.use_same_object*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-same-object"), ("bpy.types.functionnodeinputspecialcharacters*", "modeling/geometry_nodes/text/special_characters.html#bpy-types-functionnodeinputspecialcharacters"), + ("bpy.types.geometrynodeinputmeshedgeneighbors*", "modeling/geometry_nodes/mesh/edge_neighbors.html#bpy-types-geometrynodeinputmeshedgeneighbors"), + ("bpy.types.geometrynodeinputmeshfaceneighbors*", "modeling/geometry_nodes/mesh/face_neighbors.html#bpy-types-geometrynodeinputmeshfaceneighbors"), ("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"), ("bpy.types.greasepencil.edit_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-edit-curve-resolution"), ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), @@ -495,6 +495,7 @@ url_manual_mapping = ( ("bpy.types.sequencertimelineoverlay.show_grid*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-grid"), ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"), ("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"), + ("bpy.types.spacenodeoverlay.show_context_path*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-context-path"), ("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"), ("bpy.types.spacespreadsheet.object_eval_state*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-object-eval-state"), ("bpy.types.spaceuveditor.display_stretch_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-display-stretch-type"), @@ -522,6 +523,7 @@ url_manual_mapping = ( ("bpy.types.freestylelineset.select_edge_mark*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-edge-mark"), ("bpy.types.freestylelinestyle.use_length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-max"), ("bpy.types.freestylelinestyle.use_length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-min"), + ("bpy.types.geometrynodeinputmeshedgevertices*", "modeling/geometry_nodes/mesh/edge_vertices.html#bpy-types-geometrynodeinputmeshedgevertices"), ("bpy.types.geometrynodeinputsplineresolution*", "modeling/geometry_nodes/curve/spline_resolution.html#bpy-types-geometrynodeinputsplineresolution"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), ("bpy.types.materialgpencilstyle.stroke_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-stroke-style"), @@ -617,6 +619,7 @@ url_manual_mapping = ( ("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"), ("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"), ("bpy.types.brushgpencilsettings.show_lasso*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-show-lasso"), + ("bpy.types.compositornodeconvertcolorspace*", "compositing/types/converter/color_space.html#bpy-types-compositornodeconvertcolorspace"), ("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"), ("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"), ("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"), @@ -713,7 +716,9 @@ url_manual_mapping = ( ("bpy.types.geometrynodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-geometrynodealigneulertovector"), ("bpy.types.geometrynodeattributestatistic*", "modeling/geometry_nodes/attribute/attribute_statistic.html#bpy-types-geometrynodeattributestatistic"), ("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"), + ("bpy.types.geometrynodegeometrytoinstance*", "modeling/geometry_nodes/geometry/geometry_to_instance.html#bpy-types-geometrynodegeometrytoinstance"), ("bpy.types.geometrynodeinputmaterialindex*", "modeling/geometry_nodes/material/material_index.html#bpy-types-geometrynodeinputmaterialindex"), + ("bpy.types.geometrynodeinputmeshedgeangle*", "modeling/geometry_nodes/mesh/edge_angle.html#bpy-types-geometrynodeinputmeshedgeangle"), ("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"), ("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"), ("bpy.types.geometrynodetranslateinstances*", "modeling/geometry_nodes/instances/translate_instances.html#bpy-types-geometrynodetranslateinstances"), @@ -793,6 +798,7 @@ url_manual_mapping = ( ("bpy.types.freestylesettings.use_culling*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-culling"), ("bpy.types.geometrynodeendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodeendpointselection"), ("bpy.types.geometrynodegeometryproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodegeometryproximity"), + ("bpy.types.geometrynodeinputmeshfacearea*", "modeling/geometry_nodes/mesh/face_area.html#bpy-types-geometrynodeinputmeshfacearea"), ("bpy.types.geometrynodeinputsplinecyclic*", "modeling/geometry_nodes/curve/is_spline_cyclic.html#bpy-types-geometrynodeinputsplinecyclic"), ("bpy.types.geometrynodeinstancestopoints*", "modeling/geometry_nodes/instances/instances_to_points.html#bpy-types-geometrynodeinstancestopoints"), ("bpy.types.geometrynodetransferattribute*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodetransferattribute"), @@ -822,6 +828,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.mesh_select_mode*", "modeling/meshes/selecting/introduction.html#bpy-types-toolsettings-mesh-select-mode"), ("bpy.types.toolsettings.use_snap_project*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-project"), ("bpy.types.transformorientationslot.type*", "editors/3dview/controls/orientation.html#bpy-types-transformorientationslot-type"), + ("bpy.types.unitsettings.temperature_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-temperature-unit"), ("bpy.types.vertexweightproximitymodifier*", "modeling/modifiers/modify/weight_proximity.html#bpy-types-vertexweightproximitymodifier"), ("bpy.types.view3doverlay.show_wireframes*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-wireframes"), ("bpy.types.view3dshading.background_type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-background-type"), @@ -879,6 +886,7 @@ url_manual_mapping = ( ("bpy.types.spacetexteditor.use_find_all*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-all"), ("bpy.types.toolsettings.snap_uv_element*", "editors/uv/controls/snapping.html#bpy-types-toolsettings-snap-uv-element"), ("bpy.types.toolsettings.use_snap_rotate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-rotate"), + ("bpy.types.unitsettings.system_rotation*", "scene_layout/scene/properties.html#bpy-types-unitsettings-system-rotation"), ("bpy.types.view3doverlay.display_handle*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-display-handle"), ("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"), ("bpy.ops.anim.channels_editable_toggle*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-editable-toggle"), @@ -923,11 +931,15 @@ url_manual_mapping = ( ("bpy.types.freestylelineset.visibility*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-visibility"), ("bpy.types.freestylelinestyle.chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chaining"), ("bpy.types.freestylelinestyle.sort_key*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-key"), + ("bpy.types.geometrynodeaccumulatefield*", "modeling/geometry_nodes/utilities/accumulate_field.html#bpy-types-geometrynodeaccumulatefield"), ("bpy.types.geometrynodecurvesethandles*", "modeling/geometry_nodes/curve/set_handle_type.html#bpy-types-geometrynodecurvesethandles"), ("bpy.types.geometrynodecurvesplinetype*", "modeling/geometry_nodes/curve/set_spline_type.html#bpy-types-geometrynodecurvesplinetype"), + ("bpy.types.geometrynodeinputmeshisland*", "modeling/geometry_nodes/mesh/mesh_island.html#bpy-types-geometrynodeinputmeshisland"), + ("bpy.types.geometrynodemergebydistance*", "modeling/geometry_nodes/geometry/merge_by_distance.html#bpy-types-geometrynodemergebydistance"), ("bpy.types.geometrynodereplacematerial*", "modeling/geometry_nodes/material/replace_material.html#bpy-types-geometrynodereplacematerial"), ("bpy.types.geometrynoderotateinstances*", "modeling/geometry_nodes/instances/rotate_instances.html#bpy-types-geometrynoderotateinstances"), ("bpy.types.geometrynodesetsplinecyclic*", "modeling/geometry_nodes/curve/set_spline_cyclic.html#bpy-types-geometrynodesetsplinecyclic"), + ("bpy.types.geometrynodesplineparameter*", "modeling/geometry_nodes/curve/spline_parameter.html#bpy-types-geometrynodesplineparameter"), ("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"), ("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"), ("bpy.types.imagepaint.screen_grab_size*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-screen-grab-size"), @@ -997,9 +1009,9 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"), ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"), - ("bpy.types.geometrynodecurveparameter*", "modeling/geometry_nodes/curve/curve_parameter.html#bpy-types-geometrynodecurveparameter"), ("bpy.types.geometrynodedeletegeometry*", "modeling/geometry_nodes/geometry/delete_geometry.html#bpy-types-geometrynodedeletegeometry"), ("bpy.types.geometrynodeinputcurvetilt*", "modeling/geometry_nodes/curve/curve_tilt.html#bpy-types-geometrynodeinputcurvetilt"), + ("bpy.types.geometrynodeinputscenetime*", "modeling/geometry_nodes/input/scene_time.html#bpy-types-geometrynodeinputscenetime"), ("bpy.types.geometrynodepointstovolume*", "modeling/geometry_nodes/point/points_to_volume.html#bpy-types-geometrynodepointstovolume"), ("bpy.types.geometrynodescaleinstances*", "modeling/geometry_nodes/instances/scale_instances.html#bpy-types-geometrynodescaleinstances"), ("bpy.types.geometrynodesetcurveradius*", "modeling/geometry_nodes/curve/set_curve_radius.html#bpy-types-geometrynodesetcurveradius"), @@ -1079,7 +1091,6 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), ("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"), ("bpy.types.freestylelinestyle.rounds*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-rounds"), - ("bpy.types.functionnodecomparefloats*", "modeling/geometry_nodes/utilities/compare_floats.html#bpy-types-functionnodecomparefloats"), ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), ("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"), ("bpy.types.geometrynodeinputposition*", "modeling/geometry_nodes/input/position.html#bpy-types-geometrynodeinputposition"), @@ -1087,6 +1098,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"), ("bpy.types.geometrynodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-geometrynodereplacestring"), ("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"), + ("bpy.types.geometrynodescaleelements*", "modeling/geometry_nodes/mesh/scale_elements.html#bpy-types-geometrynodescaleelements"), ("bpy.types.geometrynodesubdividemesh*", "modeling/geometry_nodes/mesh/subdivide_mesh.html#bpy-types-geometrynodesubdividemesh"), ("bpy.types.geometrynodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-geometrynodevaluetostring"), ("bpy.types.keyframe.handle_left_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left-type"), @@ -1110,6 +1122,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"), ("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"), ("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"), + ("bpy.types.shrinkwrapgpencilmodifier*", "grease_pencil/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapgpencilmodifier"), ("bpy.types.spaceclipeditor.show_grid*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-grid"), ("bpy.types.spaceoutliner.filter_text*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-text"), ("bpy.types.spacetexteditor.find_text*", "editors/text_editor.html#bpy-types-spacetexteditor-find-text"), @@ -1118,6 +1131,8 @@ url_manual_mapping = ( ("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"), ("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"), ("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"), + ("bpy.types.unitsettings.scale_length*", "scene_layout/scene/properties.html#bpy-types-unitsettings-scale-length"), + ("bpy.types.unitsettings.use_separate*", "scene_layout/scene/properties.html#bpy-types-unitsettings-use-separate"), ("bpy.types.viewlayer.use_motion_blur*", "render/layers/introduction.html#bpy-types-viewlayer-use-motion-blur"), ("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"), ("bpy.types.worldmistsettings.falloff*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-falloff"), @@ -1150,13 +1165,14 @@ url_manual_mapping = ( ("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"), ("bpy.types.assetmetadata.active_tag*", "editors/asset_browser.html#bpy-types-assetmetadata-active-tag"), ("bpy.types.bakesettings.cage_object*", "render/cycles/baking.html#bpy-types-bakesettings-cage-object"), + ("bpy.types.bakesettings.margin_type*", "render/cycles/baking.html#bpy-types-bakesettings-margin-type"), ("bpy.types.bone.use_relative_parent*", "animation/armatures/bones/properties/relations.html#bpy-types-bone-use-relative-parent"), ("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"), ("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"), ("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"), ("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"), ("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"), - ("bpy.types.collection.lineart_usage*", "scene_layout/collections/properties.html#bpy-types-collection-lineart-usage"), + ("bpy.types.collection.lineart_usage*", "scene_layout/collections/collections.html#bpy-types-collection-lineart-usage"), ("bpy.types.colormanagedviewsettings*", "render/color_management.html#bpy-types-colormanagedviewsettings"), ("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"), ("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"), @@ -1172,6 +1188,7 @@ url_manual_mapping = ( ("bpy.types.freestylelineset.exclude*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-exclude"), ("bpy.types.freestylelinestyle.alpha*", "render/freestyle/view_layer/line_style/alpha.html#bpy-types-freestylelinestyle-alpha"), ("bpy.types.freestylelinestyle.color*", "render/freestyle/view_layer/line_style/color.html#bpy-types-freestylelinestyle-color"), + ("bpy.types.geometrynodefieldatindex*", "modeling/geometry_nodes/utilities/field_at_index.html#bpy-types-geometrynodefieldatindex"), ("bpy.types.geometrynodeinputtangent*", "modeling/geometry_nodes/curve/curve_tangent.html#bpy-types-geometrynodeinputtangent"), ("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"), ("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"), @@ -1207,6 +1224,7 @@ url_manual_mapping = ( ("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"), ("bpy.types.toolsettings.snap_target*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-snap-target"), ("bpy.types.transformcacheconstraint*", "animation/constraints/transform/transform_cache.html#bpy-types-transformcacheconstraint"), + ("bpy.types.unitsettings.length_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-length-unit"), ("bpy.types.vertexweighteditmodifier*", "modeling/modifiers/modify/weight_edit.html#bpy-types-vertexweighteditmodifier"), ("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"), ("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"), @@ -1229,7 +1247,7 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_clean*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-clean"), ("bpy.ops.poselib.create_pose_asset*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-create-pose-asset"), ("bpy.ops.preferences.theme_install*", "editors/preferences/themes.html#bpy-ops-preferences-theme-install"), - ("bpy.ops.render.play-rendered-anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"), + ("bpy.ops.render.play_rendered_anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"), ("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"), ("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/image.html#bpy-ops-sequencer-image-strip-add"), ("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie.html#bpy-ops-sequencer-movie-strip-add"), @@ -1253,6 +1271,7 @@ url_manual_mapping = ( ("bpy.types.compositornodemovieclip*", "compositing/types/input/movie_clip.html#bpy-types-compositornodemovieclip"), ("bpy.types.compositornodenormalize*", "compositing/types/vector/normalize.html#bpy-types-compositornodenormalize"), ("bpy.types.compositornodepremulkey*", "compositing/types/converter/alpha_convert.html#bpy-types-compositornodepremulkey"), + ("bpy.types.compositornodescenetime*", "compositing/types/input/scene_time.html#bpy-types-compositornodescenetime"), ("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"), ("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"), ("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"), @@ -1273,6 +1292,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"), ("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/curve_spiral.html#bpy-types-geometrynodecurvespiral"), ("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"), + ("bpy.types.geometrynodeextrudemesh*", "modeling/geometry_nodes/mesh/extrude_mesh.html#bpy-types-geometrynodeextrudemesh"), ("bpy.types.geometrynodefilletcurve*", "modeling/geometry_nodes/curve/fillet_curve.html#bpy-types-geometrynodefilletcurve"), ("bpy.types.geometrynodeinputnormal*", "modeling/geometry_nodes/input/normal.html#bpy-types-geometrynodeinputnormal"), ("bpy.types.geometrynodeinputradius*", "modeling/geometry_nodes/input/radius.html#bpy-types-geometrynodeinputradius"), @@ -1332,6 +1352,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.primitive_plane_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-plane-add"), ("bpy.ops.mesh.primitive_torus_add*", "modeling/meshes/primitives.html#bpy-ops-mesh-primitive-torus-add"), ("bpy.ops.mesh.select_non_manifold*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-non-manifold"), + ("bpy.ops.object.attribute_convert*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-convert"), ("bpy.ops.object.constraints_clear*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-clear"), ("bpy.ops.object.quadriflow_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-quadriflow-remesh"), ("bpy.ops.object.vertex_group_copy*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy"), @@ -1388,6 +1409,7 @@ url_manual_mapping = ( ("bpy.types.freestylelinestyle.gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-gap"), ("bpy.types.freestylesettings.mode*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-mode"), ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), + ("bpy.types.geometrynodedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodedomainsize"), ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-geometrynodefloattoint"), ("bpy.types.geometrynodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-geometrynodeinputcolor"), ("bpy.types.geometrynodeinputindex*", "modeling/geometry_nodes/input/input_index.html#bpy-types-geometrynodeinputindex"), @@ -1418,6 +1440,8 @@ url_manual_mapping = ( ("bpy.types.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"), ("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"), ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"), + ("bpy.types.unitsettings.mass_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-mass-unit"), + ("bpy.types.unitsettings.time_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-time-unit"), ("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"), ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), @@ -1435,6 +1459,7 @@ url_manual_mapping = ( ("bpy.ops.gpencil.stroke_caps_set*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-caps-set"), ("bpy.ops.gpencil.stroke_separate*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-stroke-separate"), ("bpy.ops.gpencil.stroke_simplify*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify"), + ("bpy.ops.graph.blend_to_neighbor*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-blend-to-neighbor"), ("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"), ("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"), ("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"), @@ -1447,6 +1472,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.subdivide_edgering*", "modeling/meshes/editing/edge/subdivide_edge_ring.html#bpy-ops-mesh-subdivide-edgering"), ("bpy.ops.node.hide_socket_toggle*", "interface/controls/nodes/editing.html#bpy-ops-node-hide-socket-toggle"), ("bpy.ops.node.tree_socket_remove*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-remove"), + ("bpy.ops.object.attribute_remove*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-remove"), ("bpy.ops.object.constraints_copy*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraints-copy"), ("bpy.ops.object.gpencil_modifier*", "grease_pencil/modifiers/index.html#bpy-ops-object-gpencil-modifier"), ("bpy.ops.object.make_links_scene*", "scene_layout/object/editing/link_transfer/link_scene.html#bpy-ops-object-make-links-scene"), @@ -1509,6 +1535,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"), ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodeedgesplit"), ("bpy.types.geometrynodefillcurve*", "modeling/geometry_nodes/curve/fill_curve.html#bpy-types-geometrynodefillcurve"), + ("bpy.types.geometrynodeflipfaces*", "modeling/geometry_nodes/mesh/flip_faces.html#bpy-types-geometrynodeflipfaces"), ("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"), ("bpy.types.geometrynodetrimcurve*", "modeling/geometry_nodes/curve/trim_curve.html#bpy-types-geometrynodetrimcurve"), ("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"), @@ -1552,6 +1579,7 @@ url_manual_mapping = ( ("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"), ("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"), ("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"), + ("bpy.ops.graph.equalize_handles*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-equalize-handles"), ("bpy.ops.mesh.bridge-edge-loops*", "modeling/meshes/editing/edge/bridge_edge_loops.html#bpy-ops-mesh-bridge-edge-loops"), ("bpy.ops.mesh.intersect_boolean*", "modeling/meshes/editing/face/intersect_boolean.html#bpy-ops-mesh-intersect-boolean"), ("bpy.ops.mesh.loop_multi_select*", "modeling/meshes/selecting/loops.html#bpy-ops-mesh-loop-multi-select"), @@ -1616,6 +1644,7 @@ url_manual_mapping = ( ("bpy.types.motionpath.frame_end*", "animation/motion_paths.html#bpy-types-motionpath-frame-end"), ("bpy.types.noisegpencilmodifier*", "grease_pencil/modifiers/deform/noise.html#bpy-types-noisegpencilmodifier"), ("bpy.types.object.hide_viewport*", "scene_layout/object/properties/visibility.html#bpy-types-object-hide-viewport"), + ("bpy.types.object.rotation_mode*", "scene_layout/object/properties/transforms.html#bpy-types-object-rotation-mode"), ("bpy.types.object.show_in_front*", "scene_layout/object/properties/display.html#bpy-types-object-show-in-front"), ("bpy.types.posebone.rigify_type*", "addons/rigging/rigify/rig_types/index.html#bpy-types-posebone-rigify-type"), ("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"), @@ -1718,6 +1747,7 @@ url_manual_mapping = ( ("bpy.types.cyclesworldsettings*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings"), ("bpy.types.dashgpencilmodifier*", "grease_pencil/modifiers/generate/dash.html#bpy-types-dashgpencilmodifier"), ("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"), + ("bpy.types.functionnodecompare*", "modeling/geometry_nodes/utilities/compare.html#bpy-types-functionnodecompare"), ("bpy.types.geometrynodeboolean*", "modeling/geometry_nodes/input/boolean.html#bpy-types-geometrynodeboolean"), ("bpy.types.geometrynodeinputid*", "modeling/geometry_nodes/input/id.html#bpy-types-geometrynodeinputid"), ("bpy.types.geometrynodeinteger*", "modeling/geometry_nodes/input/integer.html#bpy-types-geometrynodeinteger"), @@ -1744,6 +1774,7 @@ url_manual_mapping = ( ("bpy.types.shadernodelightpath*", "render/shader_nodes/input/light_path.html#bpy-types-shadernodelightpath"), ("bpy.types.shadernodemixshader*", "render/shader_nodes/shader/mix.html#bpy-types-shadernodemixshader"), ("bpy.types.shadernodenormalmap*", "render/shader_nodes/vector/normal_map.html#bpy-types-shadernodenormalmap"), + ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"), ("bpy.types.shadernodewireframe*", "render/shader_nodes/input/wireframe.html#bpy-types-shadernodewireframe"), ("bpy.types.spacesequenceeditor*", "video_editing/index.html#bpy-types-spacesequenceeditor"), ("bpy.types.spline.resolution_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-resolution-u"), @@ -1756,6 +1787,7 @@ url_manual_mapping = ( ("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"), ("bpy.types.transformconstraint*", "animation/constraints/transform/transformation.html#bpy-types-transformconstraint"), ("bpy.types.triangulatemodifier*", "modeling/modifiers/generate/triangulate.html#bpy-types-triangulatemodifier"), + ("bpy.types.unitsettings.system*", "scene_layout/scene/properties.html#bpy-types-unitsettings-system"), ("bpy.types.viewlayer.use_solid*", "render/layers/introduction.html#bpy-types-viewlayer-use-solid"), ("bpy.types.volume.frame_offset*", "modeling/volumes/properties.html#bpy-types-volume-frame-offset"), ("bpy.types.windowmanager.addon*", "editors/preferences/addons.html#bpy-types-windowmanager-addon"), @@ -1788,6 +1820,7 @@ url_manual_mapping = ( ("bpy.ops.node.node_copy_color*", "interface/controls/nodes/sidebar.html#bpy-ops-node-node-copy-color"), ("bpy.ops.node.read_viewlayers*", "interface/controls/nodes/editing.html#bpy-ops-node-read-viewlayers"), ("bpy.ops.node.tree_socket_add*", "interface/controls/nodes/groups.html#bpy-ops-node-tree-socket-add"), + ("bpy.ops.object.attribute_add*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-object-attribute-add"), ("bpy.ops.object.data_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data.html#bpy-ops-object-data-transfer"), ("bpy.ops.object.modifier_copy*", "modeling/modifiers/introduction.html#bpy-ops-object-modifier-copy"), ("bpy.ops.object.select_camera*", "scene_layout/object/selecting.html#bpy-ops-object-select-camera"), @@ -1819,12 +1852,12 @@ url_manual_mapping = ( ("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"), ("bpy.types.armatureconstraint*", "animation/constraints/relationship/armature.html#bpy-types-armatureconstraint"), ("bpy.types.compositornodeblur*", "compositing/types/filter/blur_node.html#bpy-types-compositornodeblur"), - ("bpy.types.compositornodecomb*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodecomb"), + ("bpy.types.compositornodecomb*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodecomb"), ("bpy.types.compositornodecrop*", "compositing/types/distort/crop.html#bpy-types-compositornodecrop"), ("bpy.types.compositornodeflip*", "compositing/types/distort/flip.html#bpy-types-compositornodeflip"), ("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"), ("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"), - ("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"), + ("bpy.types.compositornodetime*", "compositing/types/input/time_curve.html#bpy-types-compositornodetime"), ("bpy.types.constraint.enabled*", "animation/constraints/interface/header.html#bpy-types-constraint-enabled"), ("bpy.types.curve.bevel_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-object"), ("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"), @@ -1860,7 +1893,6 @@ url_manual_mapping = ( ("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"), ("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"), ("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"), - ("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"), ("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"), ("bpy.types.shadernodergbcurve*", "modeling/geometry_nodes/color/rgb_curves.html#bpy-types-shadernodergbcurve"), ("bpy.types.shadernodeseparate*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodeseparate"), @@ -1892,6 +1924,7 @@ url_manual_mapping = ( ("bpy.ops.curve.smooth_weight*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-weight"), ("bpy.ops.file.pack_libraries*", "files/blend/packed_data.html#bpy-ops-file-pack-libraries"), ("bpy.ops.font.change_spacing*", "modeling/texts/editing.html#bpy-ops-font-change-spacing"), + ("bpy.ops.gpencil.layer_merge*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-merge"), ("bpy.ops.gpencil.stroke_flip*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-flip"), ("bpy.ops.gpencil.stroke_join*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-join"), ("bpy.ops.gpencil.stroke_trim*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-trim"), @@ -1949,7 +1982,7 @@ url_manual_mapping = ( ("bpy.types.collisionmodifier*", "physics/collision.html#bpy-types-collisionmodifier"), ("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"), ("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"), - ("bpy.types.compositornodesep*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodesep"), + ("bpy.types.compositornodesep*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodesep"), ("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"), ("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"), ("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"), @@ -2172,6 +2205,7 @@ url_manual_mapping = ( ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"), ("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"), + ("bpy.types.geometrynodearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodearc"), ("bpy.types.imagepaint.mode*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-mode"), ("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"), ("bpy.types.materiallineart*", "render/materials/line_art.html#bpy-types-materiallineart"), @@ -2289,6 +2323,7 @@ url_manual_mapping = ( ("bpy.ops.fluid.free_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-free-mesh"), ("bpy.ops.font.select_all*", "modeling/texts/selecting.html#bpy-ops-font-select-all"), ("bpy.ops.gpencil.convert*", "grease_pencil/modes/object/convert_to_geometry.html#bpy-ops-gpencil-convert"), + ("bpy.ops.graph.breakdown*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-breakdown"), ("bpy.ops.mask.parent_set*", "movie_clip/masking/editing.html#bpy-ops-mask-parent-set"), ("bpy.ops.mask.select_all*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-all"), ("bpy.ops.mask.select_box*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-box"), @@ -2475,6 +2510,7 @@ url_manual_mapping = ( ("bpy.ops.sculpt.expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-expand"), ("bpy.ops.view3d.select*", "editors/3dview/selecting.html#bpy-ops-view3d-select"), ("bpy.ops.wm.debug_menu*", "advanced/operators.html#bpy-ops-wm-debug-menu"), + ("bpy.ops.wm.obj_export*", "files/import_export/obj.html#bpy-ops-wm-obj-export"), ("bpy.ops.wm.properties*", "files/data_blocks.html#bpy-ops-wm-properties"), ("bpy.ops.wm.usd_export*", "files/import_export/usd.html#bpy-ops-wm-usd-export"), ("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"), @@ -2578,6 +2614,7 @@ url_manual_mapping = ( ("bpy.types.node.name*", "interface/controls/nodes/sidebar.html#bpy-types-node-name"), ("bpy.types.nodeframe*", "interface/controls/nodes/frame.html#bpy-types-nodeframe"), ("bpy.types.nodegroup*", "interface/controls/nodes/groups.html#bpy-types-nodegroup"), + ("bpy.types.scene.muv*", "addons/uv/magic_uv.html#bpy-types-scene-muv"), ("bpy.types.spotlight*", "render/lights/light_object.html#bpy-types-spotlight"), ("bpy.types.textcurve*", "modeling/texts/index.html#bpy-types-textcurve"), ("bpy.types.udimtiles*", "modeling/meshes/uv/workflows/udims.html#bpy-types-udimtiles"), @@ -2713,6 +2750,7 @@ url_manual_mapping = ( ("bpy.ops.render*", "render/index.html#bpy-ops-render"), ("bpy.ops.script*", "advanced/scripting/index.html#bpy-ops-script"), ("bpy.ops.sculpt*", "sculpt_paint/sculpting/index.html#bpy-ops-sculpt"), + ("bpy.ops.uv.muv*", "addons/uv/magic_uv.html#bpy-ops-uv-muv"), ("bpy.ops.uv.pin*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pin"), ("bpy.ops.uv.rip*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip"), ("bpy.ops.view3d*", "editors/3dview/index.html#bpy-ops-view3d"), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 8884b835130..6f4f862a3b8 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -20,12 +20,8 @@ class Params: "legacy", "select_mouse", "select_mouse_value", - "select_tweak", "action_mouse", - "action_tweak", "tool_mouse", - "tool_tweak", - "tool_maybe_tweak", "tool_maybe_tweak_value", "context_menu_event", "cursor_set_event", @@ -72,13 +68,13 @@ class Params: "use_fallback_tool_select_mouse", # Shorthand for: `('CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value)`. "select_mouse_value_fallback", - # Shorthand for: `{"type": params.select_tweak, "value": 'ANY'}`. + # Shorthand for: `{"type": params.select_mouse, "value": 'CLICK_DRAG'}`. "select_tweak_event", # Shorthand for: `('CLICK_DRAG' if params.use_pie_click_drag else 'PRESS')` "pie_value", - # Shorthand for: `{"type": params.tool_tweak, "value": 'ANY'}`. + # Shorthand for: `{"type": params.tool_mouse, "value": 'CLICK_DRAG'}`. "tool_tweak_event", - # Shorthand for: `{"type": params.tool_maybe_tweak, "value": params.tool_maybe_tweak_value}`. + # Shorthand for: `{"type": params.tool_mouse, "value": params.tool_maybe_tweak_value}`. # # NOTE: This is typically used for active tool key-map items however it should never # be used for selection tools (the default box-select tool for example). @@ -122,24 +118,19 @@ class Params: # Right mouse select. self.select_mouse = 'RIGHTMOUSE' self.select_mouse_value = 'PRESS' - self.select_tweak = 'EVT_TWEAK_R' self.action_mouse = 'LEFTMOUSE' - self.action_tweak = 'EVT_TWEAK_L' self.tool_mouse = 'LEFTMOUSE' - self.tool_tweak = 'EVT_TWEAK_L' if use_alt_tool_or_cursor: - self.tool_maybe_tweak = 'LEFTMOUSE' self.tool_maybe_tweak_value = 'PRESS' else: - self.tool_maybe_tweak = 'EVT_TWEAK_L' - self.tool_maybe_tweak_value = 'ANY' + self.tool_maybe_tweak_value = 'CLICK_DRAG' self.context_menu_event = {"type": 'W', "value": 'PRESS'} # Use the "cursor" functionality for RMB select. if use_alt_tool_or_cursor: self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True} - self.cursor_tweak_event = {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True} + self.cursor_tweak_event = {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "alt": True} else: self.cursor_set_event = {"type": 'LEFTMOUSE', "value": 'CLICK'} self.cursor_tweak_event = None @@ -151,13 +142,9 @@ class Params: # events on the same mouse buttons. self.select_mouse = 'LEFTMOUSE' self.select_mouse_value = 'CLICK' - self.select_tweak = 'EVT_TWEAK_L' self.action_mouse = 'RIGHTMOUSE' - self.action_tweak = 'EVT_TWEAK_R' self.tool_mouse = 'LEFTMOUSE' - self.tool_tweak = 'EVT_TWEAK_L' - self.tool_maybe_tweak = 'EVT_TWEAK_L' - self.tool_maybe_tweak_value = 'ANY' + self.tool_maybe_tweak_value = 'CLICK_DRAG' if self.legacy: self.context_menu_event = {"type": 'W', "value": 'PRESS'} @@ -165,7 +152,7 @@ class Params: self.context_menu_event = {"type": 'RIGHTMOUSE', "value": 'PRESS'} self.cursor_set_event = {"type": 'RIGHTMOUSE', "value": 'PRESS', "shift": True} - self.cursor_tweak_event = {"type": 'EVT_TWEAK_R', "value": 'ANY', "shift": True} + self.cursor_tweak_event = {"type": 'RIGHTMOUSE', "value": 'CLICK_DRAG', "shift": True} # Use the "tool" functionality for LMB select. if use_alt_tool_or_cursor: @@ -199,15 +186,18 @@ class Params: # Convenience variables: self.use_fallback_tool_select_mouse = True if (select_mouse == 'LEFT') else self.use_fallback_tool_rmb self.select_mouse_value_fallback = 'CLICK' if self.use_fallback_tool_rmb else self.select_mouse_value - self.select_tweak_event = {"type": self.select_tweak, "value": 'ANY'} + self.select_tweak_event = {"type": self.select_mouse, "value": 'CLICK_DRAG'} self.pie_value = 'CLICK_DRAG' if use_pie_click_drag else 'PRESS' - self.tool_tweak_event = {"type": self.tool_tweak, "value": 'ANY'} - self.tool_maybe_tweak_event = {"type": self.tool_maybe_tweak, "value": self.tool_maybe_tweak_value} + self.tool_tweak_event = {"type": self.tool_mouse, "value": 'CLICK_DRAG'} + self.tool_maybe_tweak_event = {"type": self.tool_mouse, "value": self.tool_maybe_tweak_value} # ------------------------------------------------------------------------------ # Constants +from math import pi +pi_2 = pi / 2.0 + # Physical layout. NUMBERS_1 = ('ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE', 'ZERO') # Numeric order. @@ -352,13 +342,13 @@ def _template_items_gizmo_tweak_value_click_drag(): ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK', **any_except("alt")}, None), ("gizmogroup.gizmo_tweak", - {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None), + {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', **any_except("alt")}, None), ] def _template_items_gizmo_tweak_value_drag(): return [ - ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY', **any_except("alt")}, None), + ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', **any_except("alt")}, None), ] @@ -387,6 +377,11 @@ def _template_items_uv_select_mode(params): ] else: return [ + # TODO(@campbellbarton): should this be kept? + # Seems it was included in the new key-map by accident, check on removing + # although it's not currently used for anything else. + op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}), + *_template_items_editmode_mesh_select_mode(params), # Hack to prevent fall-through, when sync select isn't enabled (and the island button isn't visible). ("mesh.select_mode", {"type": 'FOUR', "value": 'PRESS'}, None), @@ -439,7 +434,7 @@ def _template_items_tool_select(params, operator, cursor_operator, *, extend): # For right mouse, set the cursor. return [ (cursor_operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("release_confirm", True), ("cursor_transform", True)]}), ] @@ -861,7 +856,6 @@ def km_mask_editing(params): items.extend([ ("mask.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, {"properties": [("deselect_all", not params.legacy)]}), - ("transform.translate", {"type": 'EVT_TWEAK_R', "value": 'ANY'}, None), ]) items.extend([ @@ -883,9 +877,9 @@ def km_mask_editing(params): {"properties": [("deselect", True)]}), ("mask.select_box", {"type": 'B', "value": 'PRESS'}, None), ("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), ("mask.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("mask.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), @@ -905,7 +899,7 @@ def km_mask_editing(params): ("mask.copy_splines", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("mask.paste_splines", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), ("transform.tosphere", {"type": 'S', "value": 'PRESS', "shift": True, "alt": True}, None), @@ -939,7 +933,7 @@ def km_markers(params): items.extend([ ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.move", {"type": params.select_tweak, "value": 'ANY'}, + ("marker.move", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), ("marker.duplicate", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("marker.select", {"type": params.select_mouse, "value": 'PRESS'}, None), @@ -949,7 +943,7 @@ def km_markers(params): {"properties": [("camera", True)]}), ("marker.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("extend", True), ("camera", True)]}), - ("marker.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("marker.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), ("marker.select_box", {"type": 'B', "value": 'PRESS'}, None), *_template_items_select_actions(params, "marker.select_all"), @@ -1066,10 +1060,10 @@ def km_outliner(params): ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True}, {"properties": [("extend", True), ("extend_range", True), ("deselect_all", not params.legacy)]}), ("outliner.select_box", {"type": 'B', "value": 'PRESS'}, None), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, {"properties": [("direction", 'UP')]}), @@ -1091,14 +1085,14 @@ def km_outliner(params): {"properties": [("all", False)]}), ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("all", True)]}), - ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("all", False)]}), # Fall through to generic context menu if the item(s) selected have no type specific actions. ("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), op_menu("OUTLINER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), op_menu_pie("OUTLINER_MT_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}), - ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None), + ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None), ("outliner.show_hierarchy", {"type": 'HOME', "value": 'PRESS'}, None), ("outliner.show_active", {"type": 'PERIOD', "value": 'PRESS'}, None), ("outliner.show_active", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None), @@ -1176,9 +1170,9 @@ def km_uv_editor(params): op_tool_optional( ("uv.select_circle", {"type": 'C', "value": 'PRESS'}, None), (op_tool, "builtin.select_circle"), params), - ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("uv.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("uv.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("uv.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), ("uv.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), ("uv.select_linked_pick", {"type": 'L', "value": 'PRESS'}, @@ -1205,10 +1199,9 @@ def km_uv_editor(params): if not params.legacy else op_menu("IMAGE_MT_uvs_snap", {"type": 'S', "value": 'PRESS', "shift": True}) ), - op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}), *_template_items_proportional_editing( params, connected=False, toggle_data_path='tool_settings.use_proportional_edit'), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), (op_tool_cycle, "builtin.move"), params), @@ -1370,7 +1363,6 @@ def km_view3d(params): *(() if not params.use_pie_click_drag else (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)), ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), - ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'}, @@ -1407,7 +1399,7 @@ def km_view3d(params): ("view3d.view_roll", {"type": 'NUMPAD_6', "value": 'PRESS', "shift": True, "repeat": True}, {"properties": [("type", 'RIGHT')]}), ("view3d.view_orbit", {"type": 'NUMPAD_9', "value": 'PRESS'}, - {"properties": [("angle", 3.1415927), ("type", 'ORBITRIGHT')]}), + {"properties": [("angle", pi), ("type", 'ORBITRIGHT')]}), ("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS', "shift": True}, {"properties": [("type", 'FRONT'), ("align_active", True)]}), ("view3d.view_axis", {"type": 'NUMPAD_3', "value": 'PRESS', "shift": True}, @@ -1421,28 +1413,28 @@ def km_view3d(params): ("view3d.view_axis", {"type": 'NUMPAD_7', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("type", 'BOTTOM'), ("align_active", True)]}), *(( - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH', "alt": True}, {"properties": [("type", 'TOP'), ("relative", True)]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'SOUTH', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'SOUTH', "alt": True}, {"properties": [("type", 'BOTTOM'), ("relative", True)]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'EAST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'EAST', "alt": True}, {"properties": [("type", 'RIGHT'), ("relative", True)]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'WEST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'WEST', "alt": True}, {"properties": [("type", 'LEFT'), ("relative", True)]}), ) if params.v3d_alt_mmb_drag_action == 'RELATIVE' else ( - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH', "alt": True}, {"properties": [("type", 'TOP')]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'SOUTH', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'SOUTH', "alt": True}, {"properties": [("type", 'BOTTOM')]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'EAST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'EAST', "alt": True}, {"properties": [("type", 'RIGHT')]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'WEST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'WEST', "alt": True}, {"properties": [("type", 'LEFT')]}), - # Match the pie menu. - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH_WEST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH_WEST', "alt": True}, {"properties": [("type", 'FRONT')]}), - ("view3d.view_axis", {"type": 'EVT_TWEAK_M', "value": 'NORTH_EAST', "alt": True}, + ("view3d.view_axis", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG', "direction": 'NORTH_EAST', "alt": True}, {"properties": [("type", 'BACK')]}), + # Match the pie menu. )), ("view3d.view_center_pick", {"type": 'MIDDLEMOUSE', "value": 'CLICK', "alt": True}, None), ("view3d.ndof_orbit_zoom", {"type": 'NDOF_MOTION', "value": 'ANY'}, None), @@ -1451,10 +1443,10 @@ def km_view3d(params): ("view3d.ndof_all", {"type": 'NDOF_MOTION', "value": 'ANY', "shift": True, "ctrl": True}, None), ("view3d.view_selected", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, {"properties": [("use_all_regions", False)]}), + ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CW', "value": 'PRESS'}, + {"properties": [("angle", pi_2)]}), ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'}, - {"properties": [("type", 'LEFT')]}), - ("view3d.view_roll", {"type": 'NDOF_BUTTON_ROLL_CCW', "value": 'PRESS'}, - {"properties": [("type", 'RIGHT')]}), + {"properties": [("angle", -pi_2)]}), ("view3d.view_axis", {"type": 'NDOF_BUTTON_FRONT', "value": 'PRESS'}, {"properties": [("type", 'FRONT')]}), ("view3d.view_axis", {"type": 'NDOF_BUTTON_BACK', "value": 'PRESS'}, @@ -1482,9 +1474,9 @@ def km_view3d(params): op_tool_optional( ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), (op_tool, "builtin.select_box"), params), - ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), op_tool_optional( ("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None), @@ -1501,7 +1493,7 @@ def km_view3d(params): ("view3d.copybuffer", {"type": 'C', "value": 'PRESS', "ctrl": True}, None), ("view3d.pastebuffer", {"type": 'V', "value": 'PRESS', "ctrl": True}, None), # Transform. - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), (op_tool_cycle, "builtin.move"), params), @@ -1693,15 +1685,15 @@ def km_graph_editor(params): ("graph.select_box", {"type": 'B', "value": 'PRESS'}, None), ("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("graph.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True), ("mode", 'SET')]}), - ("graph.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True}, + ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("graph.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True}, + ("graph.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), - ("graph.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("graph.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("graph.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("graph.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), ("graph.select_circle", {"type": 'C', "value": 'PRESS'}, None), ("graph.select_column", {"type": 'K', "value": 'PRESS'}, @@ -1749,7 +1741,7 @@ def km_graph_editor(params): {"properties": [("only_active", False)]}), ("anim.channels_editable_toggle", {"type": 'TAB', "value": 'PRESS'}, None), ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), @@ -1977,11 +1969,11 @@ def km_node_editor(params): ]) items.extend([ - ("node.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("node.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), - ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True}, + ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), op_tool_optional( ("node.select_box", {"type": 'B', "value": 'PRESS'}, @@ -1990,16 +1982,16 @@ def km_node_editor(params): op_tool_optional( ("node.select_circle", {"type": 'C', "value": 'PRESS'}, None), (op_tool, "builtin.select_circle"), params), - ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("detach", False)]}), - ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("detach", True)]}), - ("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("node.resize", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("node.add_reroute", - {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None), + {"type": 'LEFTMOUSE' if params.legacy else 'RIGHTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None), ("node.links_cut", - {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None), - ("node.links_mute", {"type": 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True, "alt": True}, None), + {"type": 'LEFTMOUSE' if params.legacy else 'RIGHTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, None), + ("node.links_mute", {"type": 'RIGHTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("node.backimage_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None), ("node.backimage_zoom", {"type": 'V', "value": 'PRESS', "repeat": True}, @@ -2060,26 +2052,31 @@ def km_node_editor(params): {"type": 'G', "value": 'PRESS'}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("node.translate_attach", - {"type": 'EVT_TWEAK_L', "value": 'ANY'}, - {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), - ("node.translate_attach", - {"type": params.select_tweak, "value": 'ANY'}, + {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + # Avoid duplicating the previous item. + *([] if params.select_mouse == 'LEFTMOUSE' else ( + ("node.translate_attach", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, + {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), + )), ("transform.translate", {"type": 'G', "value": 'PRESS'}, {"properties": [("view2d_edge_pan", True)]}), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, - {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}), + # Avoid duplicating the previous item. + *([] if params.select_mouse == 'LEFTMOUSE' else ( + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, + {"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}), + )), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), ("node.move_detach_links", {"type": 'D', "value": 'PRESS', "alt": True}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("node.move_detach_links_release", - {"type": params.action_tweak, "value": 'ANY', "alt": True}, + {"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True}, {"properties": [("NODE_OT_translate_attach", [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])])]}), ("node.move_detach_links", - {"type": params.select_tweak, "value": 'ANY', "alt": True}, + {"type": params.select_mouse, "value": 'CLICK_DRAG', "alt": True}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, {"properties": [("data_path", 'tool_settings.use_snap')]}), @@ -2108,7 +2105,7 @@ def km_info(params): ("info.select_pick", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), ("info.select_pick", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("extend", True)]}), - ("info.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("info.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("wait_for_input", False)]}), *_template_items_select_actions(params, "info.select_all"), ("info.select_box", {"type": 'B', "value": 'PRESS'}, None), @@ -2237,10 +2234,10 @@ def km_file_browser_main(params): ("file.next", {"type": 'BUTTON5MOUSE', "value": 'CLICK'}, None), *_template_items_select_actions(params, "file.select_all"), ("file.select_box", {"type": 'B', "value": 'PRESS'}, None), - ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("mode", 'ADD')]}), - ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'SUB')]}), ("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), ("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None), @@ -2342,15 +2339,15 @@ def km_dopesheet(params): {"properties": [("axis_range", False)]}), ("action.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("action.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True), ("mode", 'SET')]}), - ("action.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True}, + ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("action.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True}, + ("action.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), - ("action.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("action.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("action.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("action.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), ("action.select_circle", {"type": 'C', "value": 'PRESS'}, None), ("action.select_column", {"type": 'K', "value": 'PRESS'}, @@ -2395,7 +2392,7 @@ def km_dopesheet(params): ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("transform.transform", {"type": 'G', "value": 'PRESS'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), - ("transform.transform", {"type": params.select_tweak, "value": 'ANY'}, + ("transform.transform", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), @@ -2494,11 +2491,11 @@ def km_nla_editor(params): {"properties": [("axis_range", False)]}), ("nla.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("nla.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True), ("mode", 'SET')]}), - ("nla.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True}, + ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("nla.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True}, + ("nla.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), ("nla.previewrange_set", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("nla.view_all", {"type": 'HOME', "value": 'PRESS'}, None), @@ -2533,7 +2530,7 @@ def km_nla_editor(params): ("nla.fmodifier_add", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("transform.transform", {"type": 'G', "value": 'PRESS'}, {"properties": [("mode", 'TRANSLATION')]}), - ("transform.transform", {"type": params.select_tweak, "value": 'ANY'}, + ("transform.transform", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TRANSLATION')]}), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), @@ -2695,7 +2692,7 @@ def km_text(params): ("text.scroll_bar", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None), ("text.scroll", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None), ("text.scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None), - ("text.selection_set", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("text.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None), ("text.scroll", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, @@ -2826,11 +2823,11 @@ def km_sequencer(params): ("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), ("sequencer.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), - ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY'}, + ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, {"properties": [("tweak", True), ("mode", 'SET')]}), - ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY', "shift": True}, + ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("sequencer.select_box", {"type": params.select_tweak, "value": 'ANY', "ctrl": True}, + ("sequencer.select_box", {"type": params.select_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), ("sequencer.select_box", {"type": 'B', "value": 'PRESS'}, None), ("sequencer.select_box", {"type": 'B', "value": 'PRESS', "ctrl": True}, @@ -2843,7 +2840,7 @@ def km_sequencer(params): ("wm.context_set_int", {"type": 'O', "value": 'PRESS'}, {"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}), ("transform.seq_slide", {"type": 'G', "value": 'PRESS'}, None), - ("transform.seq_slide", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.seq_slide", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), @@ -2899,7 +2896,7 @@ def km_sequencerpreview(params): op_menu_pie("SEQUENCER_MT_preview_view_pie", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}), # Edit. - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), (op_tool_cycle, "builtin.move"), params), @@ -3115,9 +3112,9 @@ def km_clip_editor(params): ("clip.select_box", {"type": 'B', "value": 'PRESS'}, None), ("clip.select_circle", {"type": 'C', "value": 'PRESS'}, None), op_menu("CLIP_MT_select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}), - ("clip.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + ("clip.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("clip.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("clip.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), ("clip.add_marker_slide", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None), ("clip.delete_marker", {"type": 'X', "value": 'PRESS', "shift": True}, None), @@ -3144,7 +3141,7 @@ def km_clip_editor(params): ("wm.context_toggle", {"type": 'M', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.use_mute_footage')]}), ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True}, @@ -3213,7 +3210,7 @@ def km_clip_graph_editor(params): ("clip.graph_disable_markers", {"type": 'D', "value": 'PRESS', "shift": True}, {"properties": [("action", 'TOGGLE')]}), ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ]) @@ -3395,10 +3392,10 @@ def km_animation_channels(params): # Selection. *_template_items_select_actions(params, "anim.channels_select_all"), ("anim.channels_select_box", {"type": 'B', "value": 'PRESS'}, None), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("extend", True)]}), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("deselect", True)]}), # Delete. ("anim.channels_delete", {"type": 'X', "value": 'PRESS'}, None), @@ -3488,9 +3485,9 @@ def _grease_pencil_selection(params, use_select_mouse=True): ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), (op_tool, "builtin.select_box"), params), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), # In the Node Editor, lasso select needs ALT modifier too # (as somehow CTRL+LMB drag gets taken for "cut" quite early). @@ -3498,10 +3495,10 @@ def _grease_pencil_selection(params, use_select_mouse=True): # as part of standard GP editing keymap. This hotkey combo doesn't seem # to see much use under standard scenarios? ("gpencil.select_lasso", - {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), ("gpencil.select_lasso", - {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), *_template_view3d_gpencil_select( type=params.select_mouse, @@ -3593,7 +3590,7 @@ def km_grease_pencil_stroke_edit_mode(params): # Merge Layer ("gpencil.layer_merge", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), # Transform tools - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), op_tool_optional( ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), (op_tool_cycle, "builtin.move"), params), @@ -3765,7 +3762,7 @@ def km_grease_pencil_stroke_paint_draw_brush(params): # Box select ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]) return keymap @@ -3788,7 +3785,7 @@ def km_grease_pencil_stroke_paint_erase(params): # Box select (used by eraser) ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]) return keymap @@ -4336,9 +4333,9 @@ def km_weight_paint_vertex_selection(params): items.extend([ *_template_items_select_actions(params, "paint.vert_select_all"), ("view3d.select_box", {"type": 'B', "value": 'PRESS'}, None), - ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, + ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("mode", 'ADD')]}), - ("view3d.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True}, + ("view3d.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True}, {"properties": [("mode", 'SUB')]}), ("view3d.select_circle", {"type": 'C', "value": 'PRESS'}, None), ]) @@ -4536,7 +4533,7 @@ def km_paint_curve(params): ("paintcurve.draw", {"type": 'RET', "value": 'PRESS'}, None), ("paintcurve.draw", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None), ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), - ("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None), + ("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None), ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), ]) @@ -4652,7 +4649,9 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal return items -def _template_view3d_select(*, type, value, legacy): +def _template_view3d_select(*, type, value, legacy, exclude_mod=None): + # NOTE: `exclude_mod` is needed since we don't want this tool to exclude Control-RMB actions when this is used + # as a tool key-map with RMB-select and `use_fallback_tool_rmb` is enabled. See T92467. return [( "view3d.select", {"type": type, "value": value, **{m: True for m in mods}}, @@ -4666,7 +4665,7 @@ def _template_view3d_select(*, type, value, legacy): (("center", "enumerate"), ("ctrl", "alt")), (("toggle", "enumerate"), ("shift", "alt")), (("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")), - )] + ) if exclude_mod is None or exclude_mod not in mods] def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=True): @@ -5450,6 +5449,22 @@ def km_font(params): return keymap +def km_sculpt_curves(params): + items = [] + keymap = ( + "Sculpt Curves", + {"space_type": 'EMPTY', "region_type": 'WINDOW'}, + {"items": items}, + ) + + items.extend([ + ("sculpt_curves.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), + *_template_paint_radial_control("curves_sculpt"), + ]) + + return keymap + + def km_object_non_modal(params): items = [] keymap = ( @@ -6433,8 +6448,8 @@ def km_node_editor_tool_select_circle(params, *, fallback): "node.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, - value='ANY' if fallback else 'PRESS', + type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, + value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS', properties=[("wait_for_input", False)])), ]}, ) @@ -6474,7 +6489,7 @@ def km_3d_view_tool_select(params, *, fallback): *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( params, "view3d.select", "view3d.cursor3d", extend="toggle")), *([] if (not params.use_fallback_tool_rmb) else _template_view3d_select( - type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), + type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy, exclude_mod="ctrl")), ]}, ) @@ -6502,8 +6517,8 @@ def km_3d_view_tool_select_circle(params, *, fallback): "view3d.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, - value='ANY' if fallback else 'PRESS', + type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, + value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS', properties=[("wait_for_input", False)])), ]}, ) @@ -6572,15 +6587,15 @@ def km_3d_view_tool_shear(params): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ ("transform.shear", - {"type": params.tool_tweak, "value": 'NORTH', **params.tool_modifier}, + {"type": params.tool_mouse, "value": 'CLICK_DRAG', "direction": 'NORTH', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}), ("transform.shear", - {"type": params.tool_tweak, "value": 'SOUTH', **params.tool_modifier}, + {"type": params.tool_mouse, "value": 'CLICK_DRAG', "direction": 'SOUTH', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'Y')]}), # Use as fallback to catch diagonals too. ("transform.shear", - {"type": params.tool_tweak, "value": 'ANY', **params.tool_modifier}, + {"type": params.tool_mouse, "value": 'CLICK_DRAG', **params.tool_modifier}, {"properties": [("release_confirm", True), ("orient_axis_ortho", 'X')]}), ]}, ) @@ -7253,7 +7268,7 @@ def km_3d_view_tool_paint_gpencil_line(params): ("gpencil.primitive_line", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, {"properties": [("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7268,7 +7283,7 @@ def km_3d_view_tool_paint_gpencil_polyline(params): ("gpencil.primitive_polyline", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7285,7 +7300,7 @@ def km_3d_view_tool_paint_gpencil_box(params): ("gpencil.primitive_box", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, {"properties": [("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7302,7 +7317,7 @@ def km_3d_view_tool_paint_gpencil_circle(params): ("gpencil.primitive_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, {"properties": [("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7319,7 +7334,7 @@ def km_3d_view_tool_paint_gpencil_arc(params): ("gpencil.primitive_curve", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, {"properties": [("type", 'ARC'), ("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7332,7 +7347,7 @@ def km_3d_view_tool_paint_gpencil_curve(params): ("gpencil.primitive_curve", params.tool_maybe_tweak_event, {"properties": [("type", 'CURVE'), ("wait_for_input", False)]}), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7344,7 +7359,7 @@ def km_3d_view_tool_paint_gpencil_cutter(params): {"items": [ ("gpencil.stroke_cutter", {"type": params.tool_mouse, "value": 'PRESS'}, None), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]}, ) @@ -7414,8 +7429,8 @@ def km_3d_view_tool_edit_gpencil_select_circle(params, *, fallback): "gpencil.select_circle", # Why circle select should be used on tweak? # So that RMB or Shift-RMB is still able to set an element as active. - type=params.select_tweak if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, - value='ANY' if fallback else 'PRESS', + type=params.select_mouse if (fallback and params.use_fallback_tool_select_mouse) else params.tool_mouse, + value='CLICK_DRAG' if (fallback and params.use_fallback_tool_select_mouse) else 'PRESS', properties=[("wait_for_input", False)])), ]}, ) @@ -7771,6 +7786,7 @@ def generate_keymaps(params=None): km_lattice(params), km_particle(params), km_font(params), + km_sculpt_curves(params), km_object_non_modal(params), # Modal maps. diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index c9dea8ab2e7..e65ac32d088 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -7,11 +7,8 @@ class Params: __slots__ = ( "select_mouse", "select_mouse_value", - "select_tweak", "action_mouse", - "action_tweak", "tool_mouse", - "tool_tweak", "use_mouse_emulate_3_button", ) @@ -24,9 +21,7 @@ class Params: self.tool_mouse = 'LEFTMOUSE' self.select_mouse = 'LEFTMOUSE' self.select_mouse_value = 'CLICK' - self.select_tweak = 'EVT_TWEAK_L' - self.tool_tweak = 'EVT_TWEAK_L' - self.action_tweak = 'EVT_TWEAK_R' + self.action_mouse = 'RIGHTMOUSE' self.use_mouse_emulate_3_button = use_mouse_emulate_3_button @@ -103,7 +98,7 @@ def _template_items_animation(): def _template_items_gizmo_tweak_value_drag(): return [ - ("gizmogroup.gizmo_tweak", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("gizmogroup.gizmo_tweak", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ] @@ -479,10 +474,10 @@ def km_outliner(params): {"properties": [("extend", False), ("extend_range", True), ("deselect_all", True)]}), ("outliner.item_activate", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True, "shift": True}, {"properties": [("extend", True), ("extend_range", True), ("deselect_all", True)]}), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, {"properties": [("tweak", True)]}), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("tweak", True), ("mode", 'ADD')]}), - ("outliner.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("outliner.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("tweak", True), ("mode", 'SUB')]}), ("outliner.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, {"properties": [("direction", 'UP')]}), @@ -504,13 +499,13 @@ def km_outliner(params): {"properties": [("all", False)]}), ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("all", True)]}), - ("outliner.item_openclose", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("outliner.item_openclose", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("all", False)]}), # Fall through to generic context menu if the item(s) selected have no type specific actions. ("outliner.operation", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), op_menu("OUTLINER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), - ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("outliner.item_drag_drop", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, None), + ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("outliner.item_drag_drop", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, None), ("outliner.show_hierarchy", {"type": 'A', "value": 'PRESS'}, None), ("outliner.show_active", {"type": 'PERIOD', "value": 'PRESS'}, None), ("outliner.show_active", {"type": 'F', "value": 'PRESS'}, None), @@ -573,7 +568,7 @@ def km_uv_editor(params): ("uv.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True}, {"properties": [("extend", True), ("deselect_all", False)]}), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "shift": True}, {"properties": [("extend", True)]}), ("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, @@ -742,7 +737,7 @@ def km_view3d(params): # Menus. op_menu_pie("VIEW3D_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}), # Transform. - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), op_menu_pie("VIEW3D_MT_pivot_pie", {"type": 'PERIOD', "value": 'PRESS'}), op_menu_pie("VIEW3D_MT_orientations_pie", {"type": 'COMMA', "value": 'PRESS'}), ("view3d.toggle_xray", {"type": 'X', "value": 'PRESS', "alt": True}, None), @@ -782,9 +777,9 @@ def km_mask_editing(params): {"properties": [("deselect", True)]}), ("mask.select_box", {"type": 'Q', "value": 'PRESS'}, None), ("mask.select_circle", {"type": 'C', "value": 'PRESS'}, None), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, + ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("mask.select_lasso", {"type": params.action_tweak, "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("mask.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), ("mask.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None), ("mask.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None), @@ -826,7 +821,7 @@ def km_markers(params): items.extend([ ("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None), ("marker.add", {"type": 'M', "value": 'PRESS'}, None), - ("marker.move", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("marker.move", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("marker.duplicate", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("marker.select", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, @@ -906,11 +901,11 @@ def km_graph_editor(params): {"properties": [("axis_range", False)]}), ("graph.select_box", {"type": 'Q', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties":[("tweak", True), ("axis_range", False), ("mode", 'SET')]}), - ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties":[("tweak", True), ("axis_range", False), ("mode", 'ADD')]}), - ("graph.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("graph.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties":[("tweak", True), ("axis_range", False), ("mode", 'SUB')]}), ("graph.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None), ("graph.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None), @@ -931,8 +926,8 @@ def km_graph_editor(params): ("graph.view_frame", {"type": 'A', "value": 'PRESS', "shift": True}, None), ("anim.channels_editable_toggle", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("transform.translate", {"type": 'W', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("transform.translate", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.transform", {"type": 'Y', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None), @@ -1087,20 +1082,20 @@ def km_node_editor(params): items.extend(node_select_ops('LEFTMOUSE')) items.extend([ - ("node.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("node.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("tweak", True)]}), - ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True, "alt": True}, + ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, {"properties": [("mode", 'ADD')]}), - ("node.select_lasso", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True, "ctrl": True, "alt": True}, + ("node.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True, "ctrl": True, "alt": True}, {"properties": [("mode", 'SUB')]}), - ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("detach", False)]}), - ("node.link", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("node.link", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("detach", True)]}), - ("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("node.add_reroute", {"type": params.action_tweak, "value": 'ANY', "shift": True}, None), - ("node.links_cut", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, None), - ("node.links_mute", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("node.resize", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("node.add_reroute", {"type": params.action_mouse, "value": 'CLICK_DRAG', "shift": True}, None), + ("node.links_cut", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True}, None), + ("node.links_mute", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None), ("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None), @@ -1147,15 +1142,15 @@ def km_node_editor(params): ("node.viewer_border", {"type": 'Z', "value": 'PRESS'}, None), ("node.clear_viewer_border", {"type": 'Z', "value": 'PRESS', "alt": True}, None), ("node.translate_attach", {"type": 'W', "value": 'PRESS'}, None), - ("node.translate_attach", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("node.translate_attach", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None), + ("node.translate_attach", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("node.translate_attach", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.translate", {"type": 'W', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("release_confirm", True)]}), ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None), ("transform.resize", {"type": 'R', "value": 'PRESS'}, None), - ("node.move_detach_links_release", {"type": params.action_tweak, "value": 'ANY', "alt": True}, None), - ("node.move_detach_links", {"type": 'EVT_TWEAK_L', "value": 'ANY', "alt": True}, None), + ("node.move_detach_links_release", {"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True}, None), + ("node.move_detach_links", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "alt": True}, None), ("wm.context_toggle", {"type": 'X', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_snap')]}), ]) @@ -1177,7 +1172,7 @@ def km_info(params): ("info.select_pick", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("info.select_pick", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), - ("info.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("info.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("wait_for_input", False)]}), ("info.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}), @@ -1300,8 +1295,8 @@ def km_file_browser_main(params): ("file.next", {"type": 'BUTTON5MOUSE', "value": 'CLICK'}, None), ("file.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, None), ("file.select_box", {"type": 'Q', "value": 'PRESS'}, None), - ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("file.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties": [("mode", 'ADD')]}), ("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None), ("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None), @@ -1391,11 +1386,11 @@ def km_dopesheet(params): {"properties": [("axis_range", False)]}), ("action.select_box", {"type": 'Q', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'SET')]}), - ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'ADD')]}), - ("action.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("action.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties":[("tweak", True), ("axis_range", False), ("wait_for_input", False), ("mode", 'SUB')]}), ("action.select_column", {"type": 'K', "value": 'PRESS'}, {"properties": [("mode", 'KEYS')]}), @@ -1430,9 +1425,9 @@ def km_dopesheet(params): ("anim.channels_select_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("transform.transform", {"type": 'W', "value": 'PRESS'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), - ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("transform.transform", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), - ("transform.transform", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, + ("transform.transform", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TIME_TRANSLATE')]}), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), @@ -1518,11 +1513,11 @@ def km_nla_editor(params): {"properties": [("axis_range", False)]}), ("nla.select_box", {"type": 'Q', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), - ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties":[("tweak", True), ("mode", 'SET')]}), - ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties":[("tweak", True), ("mode", 'ADD')]}), - ("nla.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("nla.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties":[("tweak", True), ("mode", 'SUB')]}), ("nla.view_all", {"type": 'A', "value": 'PRESS'}, None), ("nla.view_all", {"type": 'NDOF_BUTTON_FIT', "value": 'PRESS'}, None), @@ -1542,9 +1537,9 @@ def km_nla_editor(params): ("nla.move_down", {"type": 'PAGE_DOWN', "value": 'PRESS'}, None), ("transform.transform", {"type": 'W', "value": 'PRESS'}, {"properties": [("mode", 'TRANSLATION')]}), - ("transform.transform", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("transform.transform", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TRANSLATION')]}), - ("transform.transform", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, + ("transform.transform", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, {"properties": [("mode", 'TRANSLATION')]}), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), @@ -1701,7 +1696,7 @@ def km_text(params): ("text.scroll_bar", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None), ("text.scroll", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None), ("text.scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None), - ("text.selection_set", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("text.cursor_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), ("text.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None), ("text.scroll", {"type": 'WHEELUPMOUSE', "value": 'PRESS'}, @@ -1823,19 +1818,19 @@ def km_sequencer(params): ("sequencer.select_linked_pick", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True}, {"properties": [("extend", True)]}), ("sequencer.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True}, None), - ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, + ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, {"properties":[("tweak", True), ("mode", 'SET')]}), - ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True}, + ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True}, {"properties":[("tweak", True), ("mode", 'ADD')]}), - ("sequencer.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True}, + ("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True}, {"properties":[("tweak", True), ("mode", 'SUB')]}), ("sequencer.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("sequencer.slip", {"type": 'R', "value": 'PRESS'}, None), ("wm.context_set_int", {"type": 'O', "value": 'PRESS'}, {"properties": [("data_path", 'scene.sequence_editor.overlay_frame'), ("value", 0)]}), ("transform.seq_slide", {"type": 'W', "value": 'PRESS'}, None), - ("transform.seq_slide", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("transform.seq_slide", {"type": 'EVT_TWEAK_M', "value": 'ANY'}, None), + ("transform.seq_slide", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("transform.seq_slide", {"type": 'MIDDLEMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.transform", {"type": 'E', "value": 'PRESS'}, {"properties": [("mode", 'TIME_EXTEND')]}), *_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}), @@ -2059,7 +2054,7 @@ def km_clip_editor(params): ("wm.context_toggle", {"type": 'M', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.use_mute_footage')]}), ("transform.translate", {"type": 'W', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.resize", {"type": 'R', "value": 'PRESS'}, None), ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None), ("clip.clear_track_path", {"type": 'T', "value": 'PRESS', "alt": True}, @@ -2113,7 +2108,7 @@ def km_clip_graph_editor(params): ("clip.graph_disable_markers", {"type": 'D', "value": 'PRESS', "shift": True}, {"properties": [("action", 'TOGGLE')]}), ("transform.translate", {"type": 'W', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.resize", {"type": 'R', "value": 'PRESS'}, None), ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None), ]) @@ -2227,10 +2222,10 @@ def km_animation_channels(params): ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}), ("anim.channels_select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), ("anim.channels_select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True,}, + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True,}, {"properties": [("extend", True)]}), - ("anim.channels_select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True,}, + ("anim.channels_select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True,}, {"properties": [("deselect", True)]}), # Delete. ("anim.channels_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), @@ -2359,7 +2354,7 @@ def km_grease_pencil_stroke_edit_mode(params): # Isolate layer ("gpencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None), # Transform tools - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("wm.context_toggle", {"type": 'B', "value": 'PRESS'}, {"properties": [("data_path", 'tool_settings.use_proportional_edit')]}), # Vertex group menu @@ -2482,7 +2477,7 @@ def km_grease_pencil_stroke_paint_erase(params): # Box select (used by eraser) ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), # Lasso select - ("gpencil.select_lasso", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None), + ("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ]) return keymap @@ -3125,7 +3120,7 @@ def km_paint_curve(params): ("paintcurve.draw", {"type": 'RET', "value": 'PRESS'}, None), ("paintcurve.draw", {"type": 'NUMPAD_ENTER', "value": 'PRESS'}, None), ("transform.translate", {"type": 'W', "value": 'PRESS'}, None), - ("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None), + ("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'}, None), ("transform.rotate", {"type": 'E', "value": 'PRESS'}, None), ("transform.resize", {"type": 'R', "value": 'PRESS'}, None), ]) @@ -3408,9 +3403,6 @@ def km_sculpt(params): ("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True, "alt": True}, None), # Remesh ("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), - ("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None), - # Remesh - ("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), ("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None), ("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None), # Brush properties @@ -4021,9 +4013,9 @@ def km_3d_view_tool_interactive_add(params): "3D View Tool: Object, Add Primitive", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ - ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY'}, + ("view3d.interactive_add", {"type": params.tool_mouse, "value": 'CLICK_DRAG'}, {"properties": [("wait_for_input", False)]}), - ("view3d.interactive_add", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + ("view3d.interactive_add", {"type": params.tool_mouse, "value": 'CLICK_DRAG', "ctrl": True}, {"properties": [("wait_for_input", False)]}), ]}, ) @@ -4187,29 +4179,28 @@ def keymap_transform_tool_mmb(keymap): km_items_new = [] for kmi in km_items: ty = kmi[1]["type"] + value = kmi[1]["value"] if km_name.endswith(" (fallback)"): if ty == 'RIGHTMOUSE': kmi = (kmi[0], kmi[1].copy(), kmi[2]) kmi[1]["type"] = 'LEFTMOUSE' km_items_new.append(kmi) - elif ty == 'EVT_TWEAK_R': - kmi = (kmi[0], kmi[1].copy(), kmi[2]) - kmi[1]["type"] = 'EVT_TWEAK_L' - km_items_new.append(kmi) else: if ty == 'LEFTMOUSE': - kmi = (kmi[0], kmi[1].copy(), kmi[2]) - kmi[1]["type"] = 'MIDDLEMOUSE' - km_items_new.append(kmi) - elif ty == 'EVT_TWEAK_L': - kmi = (kmi[0], kmi[1].copy(), kmi[2]) - if kmi[1]["value"] == 'ANY': + if value == 'CLICK_DRAG': + kmi = (kmi[0], kmi[1].copy(), kmi[2]) + if kmi[1].get("direction", 'ANY') == 'ANY': + kmi[1]["type"] = 'MIDDLEMOUSE' + kmi[1]["value"] = 'PRESS' + else: + # Directional tweaking can't be replaced by middle-mouse. + kmi[1]["type"] = 'MIDDLEMOUSE' + km_items_new.append(kmi) + else: + kmi = (kmi[0], kmi[1].copy(), kmi[2]) kmi[1]["type"] = 'MIDDLEMOUSE' kmi[1]["value"] = 'PRESS' - else: - # Directional tweaking can't be replaced by middle-mouse. - kmi[1]["type"] = 'EVT_TWEAK_M' - km_items_new.append(kmi) + km_items_new.append(kmi) km_items.extend(km_items_new) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 9ba5480cf5a..a7f401afad1 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -963,21 +963,19 @@ class WM_OT_url_open(Operator): return {'FINISHED'} -# NOTE: needed for Python 3.10 since there are name-space issues with annotations. -# This can be moved into the class as a static-method once Python 3.9x is dropped. -def _wm_url_open_preset_type_items(_self, _context): - return [item for (item, _) in WM_OT_url_open_preset.preset_items] - - class WM_OT_url_open_preset(Operator): """Open a preset website in the web browser""" bl_idname = "wm.url_open_preset" bl_label = "Open Preset Website" bl_options = {'INTERNAL'} + @staticmethod + def _wm_url_open_preset_type_items(_self, _context): + return [item for (item, _) in WM_OT_url_open_preset.preset_items] + type: EnumProperty( name="Site", - items=_wm_url_open_preset_type_items, + items=WM_OT_url_open_preset._wm_url_open_preset_type_items, ) id: StringProperty( @@ -997,11 +995,10 @@ class WM_OT_url_open_preset(Operator): return "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2] def _url_from_manual(self, _context): - if bpy.app.version_cycle in {"rc", "release"}: - manual_version = "%d.%d" % bpy.app.version[:2] - else: - manual_version = "dev" - return "https://docs.blender.org/manual/en/" + manual_version + "/" + return "https://docs.blender.org/manual/en/%d.%d/" % bpy.app.version[:2] + + def _url_from_api(self, _context): + return "https://docs.blender.org/api/%d.%d/" % bpy.app.version[:2] # This list is: (enum_item, url) pairs. # Allow dynamically extending. @@ -1016,9 +1013,12 @@ class WM_OT_url_open_preset(Operator): (('RELEASE_NOTES', "Release Notes", "Read about what's new in this version of Blender"), _url_from_release_notes), - (('MANUAL', "Manual", + (('MANUAL', "User Manual", "The reference manual for this version of Blender"), _url_from_manual), + (('API', "Python API Reference", + "The API reference manual for this version of Blender"), + _url_from_api), # Static URL's. (('FUND', "Development Fund", @@ -1233,11 +1233,7 @@ class WM_OT_doc_view(Operator): bl_label = "View Documentation" doc_id: doc_id - if bpy.app.version_cycle in {"release", "rc", "beta"}: - _prefix = ("https://docs.blender.org/api/%d.%d" % - (bpy.app.version[0], bpy.app.version[1])) - else: - _prefix = ("https://docs.blender.org/api/master") + _prefix = "https://docs.blender.org/api/%d.%d" % bpy.app.version[:2] def execute(self, _context): url = _wm_doc_get_id(self.doc_id, do_url=True, url_prefix=self._prefix, report=self.report) @@ -1283,12 +1279,6 @@ rna_vector_subtype_items = ( ) -# NOTE: needed for Python 3.10 since there are name-space issues with annotations. -# This can be moved into the class as a static-method once Python 3.9x is dropped. -def _wm_properties_edit_subtype_items(_self, _context): - return WM_OT_properties_edit.subtype_items - - class WM_OT_properties_edit(Operator): """Change a custom property's type, or adjust how it is displayed in the interface""" bl_idname = "wm.properties_edit" @@ -1395,7 +1385,7 @@ class WM_OT_properties_edit(Operator): ) subtype: EnumProperty( name="Subtype", - items=_wm_properties_edit_subtype_items, + items=WM_OT_properties_edit.subtype_items, ) # String properties. diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index 548ce72c429..629399084ba 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -24,55 +24,55 @@ class MotionPathButtonsPanel: layout.use_property_split = True layout.use_property_decorate = False - row = layout.row(align=True) - row.prop(mps, "type") - if mps.type == 'RANGE': - if bones: - row.operator("pose.paths_range_update", text="", icon='TIME') - else: - row.operator("object.paths_range_update", text="", icon='TIME') - + # Display Range + col = layout.column(align=True) + col.prop(mps, "type") + col = layout.column(align=True) if mps.type == 'CURRENT_FRAME': - col = layout.column(align=True) col.prop(mps, "frame_before", text="Frame Range Before") col.prop(mps, "frame_after", text="After") - col.prop(mps, "frame_step", text="Step") - elif mps.type == 'RANGE': - col = layout.column(align=True) - col.prop(mps, "frame_start", text="Frame Range Start") - col.prop(mps, "frame_end", text="End") - col.prop(mps, "frame_step", text="Step") + col.prop(mps, "frame_step", text="Step") + + # Calculation Range + col = layout.column(align=True) + row = col.row(align=True) + row.prop(mps, "range", text="Calculation Range") if mpath: col = layout.column(align=True) - col.enabled = False - if bones: - col.prop(mpath, "frame_start", text="Bone Cache From") - else: - col.prop(mpath, "frame_start", text="Cache From") - col.prop(mpath, "frame_end", text="To") + row = col.row(align=True) + row.enabled = False + row.prop(mpath, "frame_start", text="Cached Range") + row.prop(mpath, "frame_end", text="") col = layout.column(align=True) - + row = col.row(align=True) if bones: - col.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA') + row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA') + row.operator("pose.paths_clear", text="", icon='X').only_selected = True + row = col.row(align=True) + row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') + row.operator("pose.paths_clear", text="", icon='X').only_selected = False else: - col.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA') + row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA') + row.operator("object.paths_clear", text="", icon='X').only_selected = True + row = col.row(align=True) + row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') + row.operator("object.paths_clear", text="", icon='X').only_selected = False else: col = layout.column(align=True) - col.label(text="Nothing to show yet...", icon='ERROR') + col.label(text="No Motion Path generated yet", icon='ERROR') + # Don't invoke settings popup because settings are right above + col.operator_context = 'EXEC_REGION_WIN' if bones: - col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA') + col.operator( + "pose.paths_calculate", text="Generate for selected bones", icon='BONE_DATA') else: - col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA') - - row = col.row(align=True) - row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') - if bones: - row.operator("pose.paths_clear", text="", icon='X') - else: - row.operator("object.paths_clear", text="", icon='X') + col.operator("object.paths_calculate", text="Generate", icon='OBJECT_DATA') + row = col.row(align=True) + row.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') + row.operator("object.paths_clear", text="", icon='X').only_selected = False class MotionPathButtonsPanel_display: diff --git a/release/scripts/startup/bl_ui/properties_data_curves.py b/release/scripts/startup/bl_ui/properties_data_curves.py index 3e350575bc8..1bb5fc9afbe 100644 --- a/release/scripts/startup/bl_ui/properties_data_curves.py +++ b/release/scripts/startup/bl_ui/properties_data_curves.py @@ -35,6 +35,17 @@ class DATA_PT_context_curves(DataButtonsPanel, Panel): layout.template_ID(space, "pin_id") +class DATA_PT_curves_surface(DataButtonsPanel, Panel): + bl_label = "Surface" + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + def draw(self, context): + layout = self.layout + ob = context.object + + layout.prop(ob.data, "surface") + + class CURVES_MT_add_attribute(Menu): bl_label = "Add Attribute" @@ -115,6 +126,7 @@ class DATA_PT_custom_props_curves(DataButtonsPanel, PropertyPanel, Panel): classes = ( DATA_PT_context_curves, DATA_PT_CURVES_attributes, + DATA_PT_curves_surface, DATA_PT_custom_props_curves, CURVES_MT_add_attribute, CURVES_UL_attributes, diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index ca623797c49..9e40a8d364a 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -79,6 +79,8 @@ class UnifiedPaintPanel: return tool_settings.gpencil_weight_paint elif mode == 'VERTEX_GPENCIL': return tool_settings.gpencil_vertex_paint + elif mode == 'SCULPT_CURVES': + return tool_settings.curves_sculpt return None @staticmethod diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 8e328e7cf2b..7b7fc9dcf77 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -663,6 +663,10 @@ class DOPESHEET_MT_context_menu(Menu): layout.operator_menu_enum("action.mirror", "type", text="Mirror") layout.operator_menu_enum("action.snap", "type", text="Snap") + if st.mode == 'DOPESHEET': + layout.separator() + layout.menu("VIEW3D_MT_motion_path") + class DOPESHEET_MT_channel_context_menu(Menu): bl_label = "Dope Sheet Channel Context Menu" diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index 6f9ef12c3b7..6bc11d51ca0 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -390,6 +390,9 @@ class GRAPH_MT_context_menu(Menu): layout.operator_menu_enum("graph.mirror", "type", text="Mirror") layout.operator_menu_enum("graph.snap", "type", text="Snap") + layout.separator() + layout.menu("VIEW3D_MT_motion_path") + class GRAPH_MT_pivot_pie(Menu): bl_label = "Pivot Point" diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 411db8781e1..3e4f7f6fbcb 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -1038,9 +1038,6 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False): if props is None: print("Error:", gizmo_group, "could not access properties!") else: - for key in props.bl_rna.properties.keys(): - props.property_unset(key) - gizmo_properties = item.widget_properties if gizmo_properties is not None: if not isinstance(gizmo_properties, list): diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 338bf5a9d5d..f4aa4c50d76 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2306,6 +2306,19 @@ class _defs_gpencil_weight: ) +class _defs_curves_sculpt: + + @staticmethod + def generate_from_brushes(context): + return generate_from_enum_ex( + context, + idname_prefix="builtin_brush.", + icon_prefix="ops.curves.sculpt_", + type= bpy.types.Brush, + attr="curves_sculpt_tool", + ) + + class _defs_gpencil_vertex: @staticmethod @@ -3065,6 +3078,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], + 'SCULPT_CURVES': [ + _defs_curves_sculpt.generate_from_brushes, + ], } diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 493cad6d2db..3b6bdd01efa 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -448,8 +448,7 @@ class TOPBAR_MT_file_import(Menu): def draw(self, _context): if bpy.app.build_options.collada: - self.layout.operator("wm.collada_import", - text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_import", text="Collada (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_import", text="Alembic (.abc)") if bpy.app.build_options.usd: @@ -467,8 +466,7 @@ class TOPBAR_MT_file_export(Menu): def draw(self, _context): self.layout.operator("wm.obj_export", text="Wavefront OBJ (.obj)") if bpy.app.build_options.collada: - self.layout.operator("wm.collada_export", - text="Collada (Default) (.dae)") + self.layout.operator("wm.collada_export", text="Collada (.dae)") if bpy.app.build_options.alembic: self.layout.operator("wm.alembic_export", text="Alembic (.abc)") if bpy.app.build_options.usd: @@ -690,8 +688,8 @@ class TOPBAR_MT_help(Menu): layout.separator() layout.operator( - "wm.url_open", text="Python API Reference", icon='URL', - ).url = bpy.types.WM_OT_doc_view._prefix + "wm.url_open_preset", text="Python API Reference", icon='URL', + ).type = 'API' if show_developer: layout.operator( diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index fc1911910c4..f80ad378b3c 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -1718,6 +1718,7 @@ class USERPREF_PT_ndof_settings(Panel): if show_3dview_settings: col.prop(props, "ndof_show_guide") col.prop(props, "ndof_zoom_invert") + col.prop(props, "ndof_lock_camera_pan_zoom") row = col.row(heading="Pan") row.prop(props, "ndof_pan_yz_swap_axis", text="Swap Y and Z Axes") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 61ba6189be4..cd0306d31fd 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -468,6 +468,38 @@ class _draw_tool_settings_context_mode: return True + @staticmethod + def SCULPT_CURVES(context, layout, tool): + if (tool is None) or (not tool.has_datablock): + return False + + paint = context.tool_settings.curves_sculpt + layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True) + + brush = paint.brush + if brush is None: + return False + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "size", + unified_name="use_unified_size", + text="Radius", + slider=True, + header=True + ) + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "strength", + unified_name="use_unified_strength", + header=True + ) + class VIEW3D_HT_header(Header): bl_space_type = 'VIEW_3D' @@ -1165,23 +1197,24 @@ class VIEW3D_MT_view_viewpoint(Menu): def draw(self, _context): layout = self.layout + i18n_text_ctxt = bpy.app.translations.contexts_C_to_py['BLT_I18NCONTEXT_EDITOR_VIEW3D'] - layout.operator("view3d.view_camera", text="Camera") + layout.operator("view3d.view_camera", text="Camera", text_ctxt=i18n_text_ctxt) layout.separator() - layout.operator("view3d.view_axis", text="Top").type = 'TOP' - layout.operator("view3d.view_axis", text="Bottom").type = 'BOTTOM' + layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt).type = 'TOP' + layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt).type = 'BOTTOM' layout.separator() - layout.operator("view3d.view_axis", text="Front").type = 'FRONT' - layout.operator("view3d.view_axis", text="Back").type = 'BACK' + layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt).type = 'FRONT' + layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt).type = 'BACK' layout.separator() - layout.operator("view3d.view_axis", text="Right").type = 'RIGHT' - layout.operator("view3d.view_axis", text="Left").type = 'LEFT' + layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt).type = 'RIGHT' + layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt).type = 'LEFT' class VIEW3D_MT_view_navigation(Menu): @@ -1248,31 +1281,31 @@ class VIEW3D_MT_view_align_selected(Menu): def draw(self, _context): layout = self.layout - props = layout.operator("view3d.view_axis", text="Top") + props = layout.operator("view3d.view_axis", text="Top", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'TOP' - props = layout.operator("view3d.view_axis", text="Bottom") + props = layout.operator("view3d.view_axis", text="Bottom", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'BOTTOM' layout.separator() - props = layout.operator("view3d.view_axis", text="Front") + props = layout.operator("view3d.view_axis", text="Front", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'FRONT' - props = layout.operator("view3d.view_axis", text="Back") + props = layout.operator("view3d.view_axis", text="Back", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'BACK' layout.separator() - props = layout.operator("view3d.view_axis", text="Right") + props = layout.operator("view3d.view_axis", text="Right", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'RIGHT' - props = layout.operator("view3d.view_axis", text="Left") + props = layout.operator("view3d.view_axis", text="Left", text_ctxt=i18n_text_ctxt) props.align_active = True props.type = 'LEFT' @@ -1899,6 +1932,13 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu): layout.operator("paint.vert_select_ungrouped", text="Ungrouped Vertices") +class VIEW3D_MT_select_edit_curves(Menu): + bl_label = "Select" + + def draw(self, _context): + pass + + class VIEW3D_MT_angle_control(Menu): bl_label = "Angle Control" @@ -2344,6 +2384,25 @@ class VIEW3D_MT_object_clear(Menu): layout.operator("object.origin_clear", text="Origin") +class VIEW3D_MT_motion_path(Menu): + bl_label = "Motion Paths" + + def draw(self, _context): + layout = self.layout + ob = _context.object + if ob.mode == 'OBJECT': + layout.operator("object.paths_calculate") + layout.operator("object.paths_update") + layout.operator("object.paths_update_visible") + layout.operator("object.paths_clear", text="Clear all").only_selected = False + layout.operator("object.paths_clear", text="Clear selected").only_selected = True + elif ob.mode == 'POSE': + layout.operator("pose.paths_calculate") + layout.operator("pose.paths_update") + layout.operator("pose.paths_clear", text="Clear all").only_selected = False + layout.operator("pose.paths_clear", text="Clear selected").only_selected = True + + class VIEW3D_MT_object_context_menu(Menu): bl_label = "Object Context Menu" @@ -2545,6 +2604,7 @@ class VIEW3D_MT_object_context_menu(Menu): layout.menu("VIEW3D_MT_mirror") layout.menu("VIEW3D_MT_snap") layout.menu("VIEW3D_MT_object_parent") + layout.menu("VIEW3D_MT_motion_path") layout.operator_context = 'INVOKE_REGION_WIN' if view and view.local_view: @@ -3592,10 +3652,10 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.separator() - layout.operator("pose.paths_calculate", text="Calculate Motion Paths") - layout.operator("pose.paths_clear", text="Clear Motion Paths") - layout.operator("pose.paths_update", text="Update Armature Motion Paths") - layout.operator("object.paths_update_visible", text="Update All Motion Paths") + layout.operator("pose.paths_calculate") + layout.operator("pose.paths_update") + layout.operator("pose.paths_clear", text="Clear all").only_selected = False + layout.operator("pose.paths_clear", text="Clear selected").only_selected = True layout.separator() @@ -5123,6 +5183,13 @@ class VIEW3D_MT_edit_gpencil_showhide(Menu): layout.operator("gpencil.hide", text="Hide Inactive Layers").unselected = True +class VIEW3D_MT_edit_curves(Menu): + bl_label = "Curves" + + def draw(self, _context): + pass + + class VIEW3D_MT_object_mode_pie(Menu): bl_label = "Mode" @@ -7544,6 +7611,7 @@ classes = ( VIEW3D_MT_select_gpencil, VIEW3D_MT_select_paint_mask, VIEW3D_MT_select_paint_mask_vertex, + VIEW3D_MT_select_edit_curves, VIEW3D_MT_angle_control, VIEW3D_MT_mesh_add, VIEW3D_MT_curve_add, @@ -7576,6 +7644,7 @@ classes = ( VIEW3D_MT_object_quick_effects, VIEW3D_MT_object_showhide, VIEW3D_MT_object_cleanup, + VIEW3D_MT_motion_path, VIEW3D_MT_make_single_user, VIEW3D_MT_make_links, VIEW3D_MT_brush_paint_modes, @@ -7666,6 +7735,7 @@ classes = ( VIEW3D_MT_edit_armature_names, VIEW3D_MT_edit_armature_delete, VIEW3D_MT_edit_gpencil_transform, + VIEW3D_MT_edit_curves, VIEW3D_MT_object_mode_pie, VIEW3D_MT_view_pie, VIEW3D_MT_transform_gizmo_pie, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 21e20c3b734..a0205a2bcb1 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -142,6 +142,7 @@ def mesh_node_items(context): yield NodeItem("GeometryNodeInputMeshEdgeVertices") yield NodeItem("GeometryNodeInputMeshFaceArea") yield NodeItem("GeometryNodeInputMeshFaceNeighbors") + yield NodeItem("GeometryNodeInputMeshFaceIsPlanar") yield NodeItem("GeometryNodeInputMeshIsland") yield NodeItem("GeometryNodeInputShadeSmooth") yield NodeItem("GeometryNodeInputMeshVertexNeighbors") @@ -164,6 +165,7 @@ def geometry_node_items(context): yield NodeItem("GeometryNodeBoundBox") yield NodeItem("GeometryNodeConvexHull") yield NodeItem("GeometryNodeDeleteGeometry") + yield NodeItem("GeometryNodeDuplicateElements") yield NodeItem("GeometryNodeGeometryToInstance") yield NodeItem("GeometryNodeMergeByDistance") yield NodeItem("GeometryNodeProximity") diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 0d63bb88de1..500f386dcc0 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -227,7 +227,7 @@ struct WriteAttributeLookup { * - An output attribute can live side by side with an existing attribute with a different domain * or data type. The old attribute will only be overwritten when the #save function is called. * - * \note The lifetime of an output attribute should not be longer than the the lifetime of the + * \note The lifetime of an output attribute should not be longer than the lifetime of the * geometry component it comes from, since it can keep a reference to the component for use in * the #save method. */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 21bbb4ce9ad..a8a851bb228 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,13 +25,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 5 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file * was written with too new a version. */ #define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 42 +#define BLENDER_FILE_MIN_SUBVERSION 43 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index c3a2aba18b5..4b84c0cfe23 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -8,13 +8,13 @@ * General operations for brushes. */ +#include "DNA_color_types.h" #include "DNA_object_enums.h" #ifdef __cplusplus extern "C" { #endif -enum eCurveMappingPreset; struct Brush; struct ImBuf; struct ImagePool; diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 52de39f3ed9..584568b4a5e 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -106,6 +106,7 @@ typedef enum eContextObjectMode { CTX_MODE_EDIT_ARMATURE, CTX_MODE_EDIT_METABALL, CTX_MODE_EDIT_LATTICE, + CTX_MODE_EDIT_CURVES, CTX_MODE_POSE, CTX_MODE_SCULPT, CTX_MODE_PAINT_WEIGHT, diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h index 2cce15fbfd6..88bb1c67fd1 100644 --- a/source/blender/blenkernel/BKE_curves.h +++ b/source/blender/blenkernel/BKE_curves.h @@ -2,9 +2,11 @@ #pragma once +#include "DNA_curves_types.h" + /** \file * \ingroup bke - * \brief Low-level operations for curves. + * \brief Low-level operations for curves that cannot be defined in the C++ header yet. */ #ifdef __cplusplus @@ -23,14 +25,10 @@ void *BKE_curves_add(struct Main *bmain, const char *name); struct BoundBox *BKE_curves_boundbox_get(struct Object *ob); -void BKE_curves_update_customdata_pointers(struct Curves *curves); bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer); /* Depsgraph */ -struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src, - int totpoint, - int totcurve); struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference); void BKE_curves_data_update(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh new file mode 100644 index 00000000000..f3d9090d16b --- /dev/null +++ b/source/blender/blenkernel/BKE_curves.hh @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.h" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +#include <mutex> + +#include "BLI_float4x4.hh" +#include "BLI_index_mask.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_attribute_access.hh" + +#include "FN_generic_virtual_array.hh" + +namespace blender::bke { + +/** + * Contains derived data, caches, and other information not saved in files, besides a few pointers + * to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are + * accessed. + */ +class CurvesGeometryRuntime { + public: + /** Cache of evaluated positions. */ + mutable Vector<float3> evaluated_position_cache; + mutable std::mutex position_cache_mutex; + mutable bool position_cache_dirty = true; + + /** Direction of the spline at each evaluated point. */ + mutable Vector<float3> evaluated_tangents_cache; + mutable std::mutex tangent_cache_mutex; + mutable bool tangent_cache_dirty = true; + + /** Normal direction vectors for each evaluated point. */ + mutable Vector<float3> evaluated_normals_cache; + mutable std::mutex normal_cache_mutex; + mutable bool normal_cache_dirty = true; +}; + +/** + * A C++ class that wraps the DNA struct for better encapsulation and ease of use. It inherits + * directly from the struct rather than storing a pointer to avoid more complicated ownership + * handling. + */ +class CurvesGeometry : public ::CurvesGeometry { + public: + CurvesGeometry(); + /** + * Create curves with the given size. Only the position attribute is created, along with the + * offsets. + */ + CurvesGeometry(int point_size, int curve_size); + CurvesGeometry(const CurvesGeometry &other); + CurvesGeometry &operator=(const CurvesGeometry &other); + ~CurvesGeometry(); + + static CurvesGeometry &wrap(::CurvesGeometry &dna_struct) + { + CurvesGeometry *geometry = reinterpret_cast<CurvesGeometry *>(&dna_struct); + return *geometry; + } + static const CurvesGeometry &wrap(const ::CurvesGeometry &dna_struct) + { + const CurvesGeometry *geometry = reinterpret_cast<const CurvesGeometry *>(&dna_struct); + return *geometry; + } + + /* -------------------------------------------------------------------- + * Accessors. + */ + + int points_size() const; + int curves_size() const; + IndexRange points_range() const; + IndexRange curves_range() const; + + /** + * The total number of points in the evaluated poly curve. + * This can depend on the resolution attribute if it exists. + */ + int evaluated_points_size() const; + + /** + * Access a range of indices of point data for a specific curve. + */ + IndexRange range_for_curve(int index) const; + + /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */ + VArray<int8_t> curve_types() const; + /** Mutable access to curve types. Call #tag_topology_changed after changing any type. */ + MutableSpan<int8_t> curve_types(); + + MutableSpan<float3> positions(); + Span<float3> positions() const; + + /** + * Calculate the largest and smallest position values, only including control points + * (rather than evaluated points). The existing values of `min` and `max` are taken into account. + * + * \return Whether there are any points. If the curve is empty, the inputs will be unaffected. + */ + bool bounds_min_max(float3 &min, float3 &max) const; + + /** + * The index of the first point in every curve. The size of this span is one larger than the + * number of curves. Consider using #range_for_curve rather than using the offsets directly. + */ + Span<int> offsets() const; + MutableSpan<int> offsets(); + + VArray<bool> cyclic() const; + MutableSpan<bool> cyclic(); + + /* -------------------------------------------------------------------- + * Operations. + */ + + /** + * Change the number of elements. New values for existing attributes should be properly + * initialized afterwards. + */ + void resize(int point_size, int curve_size); + + /** Call after deforming the position attribute. */ + void tag_positions_changed(); + /** + * Call after any operation that changes the topology + * (number of points, evaluated points, or the total count). + */ + void tag_topology_changed(); + /** Call after changing the "tilt" or "up" attributes. */ + void tag_normals_changed(); + + void translate(const float3 &translation); + void transform(const float4x4 &matrix); + + void update_customdata_pointers(); + + /* -------------------------------------------------------------------- + * Attributes. + */ + + fn::GVArray adapt_domain(const fn::GVArray &varray, + AttributeDomain from, + AttributeDomain to) const; +}; + +Curves *curves_new_nomain(int point_size, int curves_size); + +/** + * Create a new curves data-block containing a single curve with the given length and type. + */ +Curves *curves_new_nomain_single(int point_size, CurveType type); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 7c55c53ed66..f7767cc2a60 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -24,6 +24,7 @@ #include "FN_field.hh" +struct Curves; struct Collection; struct Curve; struct CurveEval; @@ -412,10 +413,10 @@ struct GeometrySet { static GeometrySet create_with_pointcloud( PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /** - * Create a new geometry set that only contains the given curve. + * Create a new geometry set that only contains the given curves. */ - static GeometrySet create_with_curve( - CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + static GeometrySet create_with_curves( + Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /* Utility methods for access. */ /** @@ -435,9 +436,9 @@ struct GeometrySet { */ bool has_volume() const; /** - * Returns true when the geometry set has a curve component that has a curve. + * Returns true when the geometry set has a curves component that has a curves data-block. */ - bool has_curve() const; + bool has_curves() const; /** * Returns true when the geometry set has any data that is not an instance. */ @@ -460,9 +461,9 @@ struct GeometrySet { */ const Volume *get_volume_for_read() const; /** - * Returns a read-only curve or null. + * Returns a read-only curves data-block or null. */ - const CurveEval *get_curve_for_read() const; + const Curves *get_curves_for_read() const; /** * Returns a mutable mesh or null. No ownership is transferred. @@ -477,9 +478,9 @@ struct GeometrySet { */ Volume *get_volume_for_write(); /** - * Returns a mutable curve or null. No ownership is transferred. + * Returns a mutable curves data-block or null. No ownership is transferred. */ - CurveEval *get_curve_for_write(); + Curves *get_curves_for_write(); /* Utility methods for replacement. */ /** @@ -497,9 +498,9 @@ struct GeometrySet { void replace_volume(Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /** - * Clear the existing curve and replace it with the given one. + * Clear the existing curves data-block and replace it with the given one. */ - void replace_curve(CurveEval *curve, + void replace_curve(Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); private: @@ -632,17 +633,59 @@ class PointCloudComponent : public GeometryComponent { }; /** - * A geometry component that stores curve data, in other words, a group of splines. - * Curves are stored differently than other geometry components, because the data structure used - * here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored here - * instead, though the component does give access to a #Curve for interfacing with render engines - * and other areas of Blender that expect to use a data-block with an #ID. + * Legacy runtime-only curves type. + * These curves are stored differently than other geometry components, because the data structure + * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored + * here instead, though the component does give access to a #Curve for interfacing with render + * engines and other areas of Blender that expect to use a data-block with an #ID. */ -class CurveComponent : public GeometryComponent { +class CurveComponentLegacy : public GeometryComponent { private: CurveEval *curve_ = nullptr; GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + public: + CurveComponentLegacy(); + ~CurveComponentLegacy(); + GeometryComponent *copy() const override; + + void clear(); + bool has_curve() const; + /** + * Clear the component and replace it with the new curve. + */ + void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + CurveEval *release(); + + const CurveEval *get_for_read() const; + CurveEval *get_for_write(); + + int attribute_domain_size(AttributeDomain domain) const final; + + bool is_empty() const final; + + bool owns_direct_data() const override; + void ensure_owns_direct_data() override; + + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; + + private: + const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; + + blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const final; +}; + +/** + * A geometry component that stores a group of curves, corresponding the the #Curves and + * #CurvesGeometry types. + */ +class CurveComponent : public GeometryComponent { + private: + Curves *curves_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + /** * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws. * This is necessary because Blender assumes that objects evaluate to an object data type, and @@ -658,15 +701,15 @@ class CurveComponent : public GeometryComponent { GeometryComponent *copy() const override; void clear(); - bool has_curve() const; + bool has_curves() const; /** * Clear the component and replace it with the new curve. */ - void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); - CurveEval *release(); + void replace(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + Curves *release(); - const CurveEval *get_for_read() const; - CurveEval *get_for_write(); + const Curves *get_for_read() const; + Curves *get_for_write(); int attribute_domain_size(AttributeDomain domain) const final; @@ -693,7 +736,7 @@ class CurveComponent : public GeometryComponent { /** * Holds a reference to conceptually unique geometry or a pointer to object/collection data - * that is is instanced with a transform in #InstancesComponent. + * that is instanced with a transform in #InstancesComponent. */ class InstanceReference { public: diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index eb61bd32eee..4127030e96f 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -208,11 +208,13 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd, * \param gpd: Grease pencil data-block * \param gps: Stroke to sample * \param dist: Distance of one segment + * \param sharp_threshold: Threshold for preserving sharp corners */ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, - float dist, - bool select); + const float dist, + const bool select, + const float sharp_threshold); /** * Apply smooth position to stroke point. * \param gps: Stroke to smooth diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index b4dda3689e6..3f7d9498e39 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -169,7 +169,7 @@ struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop, /*-------- Main Functions --------*/ /** - * Get the Group property that contains the id properties for ID id. + * Get the Group property that contains the id properties for ID `id`. * * \param create_if_needed: Set to create the group property and attach it to id if it doesn't * exist; otherwise the function will return NULL if there's no Group property attached to the ID. diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index e9abbb4575e..bd64b03cc7d 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -230,7 +230,7 @@ extern IDTypeInfo IDType_ID_SCE; extern IDTypeInfo IDType_ID_LI; extern IDTypeInfo IDType_ID_OB; extern IDTypeInfo IDType_ID_ME; -extern IDTypeInfo IDType_ID_CU; +extern IDTypeInfo IDType_ID_CU_LEGACY; extern IDTypeInfo IDType_ID_MB; extern IDTypeInfo IDType_ID_MA; extern IDTypeInfo IDType_ID_TE; diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 858bc2f5625..98301ca7a70 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -92,7 +92,13 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * * \param id_hierarchy_root: the override ID that is the root of the hierarchy. May be NULL, in * which case it is assumed that the given `id_root_reference` is tagged for override, and its - * newly created override will be used as hierarchy root. + * newly created override will be used as hierarchy root. Must be NULL if + * `id_hierarchy_root_reference` is not NULL. + * + * \param id_hierarchy_root_reference: the linked ID that is the root of the hierarchy. Must be + * tagged for override. May be NULL, in which case it is assumed that the given `id_root_reference` + * is tagged for override, and its newly created override will be used as hierarchy root. Must be + * NULL if `id_hierarchy_root` is not NULL. * * \param do_no_main: Create the new override data outside of Main database. * Used for resyncing of linked overrides. @@ -103,6 +109,7 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, struct Library *owner_library, const struct ID *id_root_reference, struct ID *id_hierarchy_root, + const struct ID *id_hierarchy_root_reference, bool do_no_main); /** * Advanced 'smart' function to create fully functional overrides. @@ -119,11 +126,18 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * \param owner_library: the library in which the overrides should be created. Besides versioning * and resync code path, this should always be NULL (i.e. the local .blend file). * - * \param id_root: The root ID to create an override from. + * \param id_root_reference: The linked root ID to create an override from. May be a sub-root of + * the overall hierarchy, in which case calling code is expected to have already tagged required + * 'path' of IDs leading from the given `id_hierarchy_root` to the given `id_root`. + * + * \param id_hierarchy_root_reference: The ID to be used a hierarchy root of the overrides to be + * created. Can be either the linked root ID of the whole override hierarchy, (typically the same + * as `id_root`, unless a sub-part only of the hierarchy is overridden), or the already existing + * override hierarchy root if part of the hierarchy is already overridden. * - * \param id_reference: Some reference ID used to do some post-processing after overrides have been - * created, may be NULL. Typically, the Empty object instantiating the linked collection we - * override, currently. + * \param id_instance_hint: Some ID used as hint/reference to do some post-processing after + * overrides have been created, may be NULL. Typically, the Empty object instantiating the linked + * collection we override, currently. * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * @@ -133,8 +147,9 @@ bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Library *owner_library, - struct ID *id_root, - struct ID *id_reference, + struct ID *id_root_reference, + struct ID *id_hierarchy_root_reference, + struct ID *id_instance_hint, struct ID **r_id_root_override); /** * Create a library override template. diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 94b94303ec9..fd7d39fc250 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -220,7 +220,7 @@ IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper * Use this function when `ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF`. In this case * the #id_self parameter is required. Otherwise the #BKE_id_remapper_apply can be used. * - * \param id_self required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF. + * \param id_self: required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF. * When remapping to id_self it will then be remapped to NULL. */ IDRemapperApplyResult BKE_id_remapper_apply_ex(const struct IDRemapper *id_remapper, diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index a84f6058d67..2373bb289cd 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -70,6 +70,13 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm, const struct Mesh *me_settings); /** + * Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used + * when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the + * evaluated vertices to the originals. + */ +void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh); + +/** * Find the index of the loop in 'poly' which references vertex, * returns -1 if not found */ @@ -332,8 +339,7 @@ int BKE_mesh_tessface_calc_ex(struct CustomData *fdata, struct MVert *mvert, int totface, int totloop, - int totpoly, - bool do_face_nor_copy); + int totpoly); void BKE_mesh_tessface_calc(struct Mesh *mesh); /** @@ -399,6 +405,9 @@ void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh); * \note In order to clear the dirty flag, this function should be followed by a call to * #BKE_mesh_vertex_normals_clear_dirty. This is separate so that normals are still tagged dirty * while they are being assigned. + * + * \warning The memory returned by this function is not initialized if it was not previously + * allocated. */ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]; @@ -409,10 +418,24 @@ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]; * \note In order to clear the dirty flag, this function should be followed by a call to * #BKE_mesh_poly_normals_clear_dirty. This is separate so that normals are still tagged dirty * while they are being assigned. + * + * \warning The memory returned by this function is not initialized if it was not previously + * allocated. */ float (*BKE_mesh_poly_normals_for_write(struct Mesh *mesh))[3]; /** + * Free any cached vertex or poly normals. Face corner (loop) normals are also derived data, + * but are not handled with the same method yet, so they are not included. It's important that this + * is called after the mesh changes size, since otherwise cached normal arrays might not be large + * enough (though it may be called indirectly by other functions). + * + * \note Normally it's preferred to call #BKE_mesh_normals_tag_dirty instead, + * but this can be used in specific situations to reset a mesh or reduce memory usage. + */ +void BKE_mesh_clear_derived_normals(struct Mesh *mesh); + +/** * Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually. */ void BKE_mesh_vertex_normals_clear_dirty(struct Mesh *mesh); @@ -919,7 +942,7 @@ void BKE_mesh_calc_relative_deform(const struct MPoly *mpoly, const float (*vert_cos_org)[3], float (*vert_cos_new)[3]); -/* *** mesh_validate.c *** */ +/* *** mesh_validate.cc *** */ /** * Validates and corrects a Mesh. diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 9a212cb1275..acdca23b21c 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -409,14 +409,18 @@ void BKE_modifier_session_uuid_generate(struct ModifierData *md); bool BKE_modifier_unique_name(struct ListBase *modifiers, struct ModifierData *md); +struct ModifierData *BKE_modifier_copy_ex(const struct ModifierData *md, int flag); + /** * Callback's can use this to avoid copying every member. */ void BKE_modifier_copydata_generic(const struct ModifierData *md, struct ModifierData *md_dst, int flag); -void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target); -void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, int flag); +void BKE_modifier_copydata(const struct ModifierData *md, struct ModifierData *target); +void BKE_modifier_copydata_ex(const struct ModifierData *md, + struct ModifierData *target, + int flag); bool BKE_modifier_depends_ontime(struct Scene *scene, struct ModifierData *md, int dag_eval_mode); bool BKE_modifier_supports_mapping(struct ModifierData *md); bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 933585b1f8f..315e24485fa 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -681,6 +681,7 @@ void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link); void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock); void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link); bool nodeLinkIsHidden(const struct bNodeLink *link); +bool nodeLinkIsSelected(const struct bNodeLink *link); void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node); void nodeToView(const struct bNode *node, float x, float y, float *rx, float *ry); @@ -1514,6 +1515,8 @@ struct TexResult; #define GEO_NODE_SCALE_ELEMENTS 1151 #define GEO_NODE_EXTRUDE_MESH 1152 #define GEO_NODE_MERGE_BY_DISTANCE 1153 +#define GEO_NODE_DUPLICATE_ELEMENTS 1154 +#define GEO_NODE_INPUT_MESH_FACE_IS_PLANAR 1155 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 1f37e95a023..8ab89b6c244 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -74,9 +74,11 @@ typedef enum ePaintMode { PAINT_MODE_VERTEX_GPENCIL = 7, PAINT_MODE_SCULPT_GPENCIL = 8, PAINT_MODE_WEIGHT_GPENCIL = 9, + /** Curves. */ + PAINT_MODE_SCULPT_CURVES = 10, /** Keep last. */ - PAINT_MODE_INVALID = 10, + PAINT_MODE_INVALID = 11, } ePaintMode; #define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PAINT_MODE_SCULPT_UV) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 846dcd3ca8e..42b4702ee44 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -10,6 +10,8 @@ #include "FN_generic_virtual_array.hh" +#include "DNA_curves_types.h" + #include "BLI_float4x4.hh" #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" @@ -18,6 +20,7 @@ #include "BKE_attribute_math.hh" struct Curve; +struct Curves; struct ListBase; class Spline; @@ -49,12 +52,6 @@ using SplinePtr = std::unique_ptr<Spline>; */ class Spline { public: - enum class Type { - Bezier, - NURBS, - Poly, - }; - enum NormalCalculationMode { ZUp, Minimum, @@ -65,7 +62,7 @@ class Spline { blender::bke::CustomDataAttributes attributes; protected: - Type type_; + CurveType type_; bool is_cyclic_ = false; /** Direction of the spline at each evaluated point. */ @@ -85,7 +82,7 @@ class Spline { public: virtual ~Spline() = default; - Spline(const Type type) : type_(type) + Spline(const CurveType type) : type_(type) { } Spline(Spline &other) : attributes(other.attributes), type_(other.type_) @@ -107,7 +104,7 @@ class Spline { SplinePtr copy_without_attributes() const; static void copy_base_settings(const Spline &src, Spline &dst); - Spline::Type type() const; + CurveType type() const; /** Return the number of control points. */ virtual int size() const = 0; @@ -252,26 +249,13 @@ class Spline { * factors and indices in a list of floats, which is then used to interpolate any other data. */ class BezierSpline final : public Spline { - public: - enum class HandleType { - /** The handle can be moved anywhere, and doesn't influence the point's other handle. */ - Free, - /** The location is automatically calculated to be smooth. */ - Auto, - /** The location is calculated to point to the next/previous control point. */ - Vector, - /** The location is constrained to point in the opposite direction as the other handle. */ - Align, - }; - - private: blender::Vector<blender::float3> positions_; blender::Vector<float> radii_; blender::Vector<float> tilts_; int resolution_; - blender::Vector<HandleType> handle_types_left_; - blender::Vector<HandleType> handle_types_right_; + blender::Vector<int8_t> handle_types_left_; + blender::Vector<int8_t> handle_types_right_; /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */ mutable blender::Vector<blender::float3> handle_positions_left_; @@ -296,7 +280,7 @@ class BezierSpline final : public Spline { mutable bool mapping_cache_dirty_ = true; public: - BezierSpline() : Spline(Type::Bezier) + BezierSpline() : Spline(CURVE_TYPE_BEZIER) { } BezierSpline(const BezierSpline &other) @@ -323,8 +307,8 @@ class BezierSpline final : public Spline { blender::Span<float> radii() const final; blender::MutableSpan<float> tilts() final; blender::Span<float> tilts() const final; - blender::Span<HandleType> handle_types_left() const; - blender::MutableSpan<HandleType> handle_types_left(); + blender::Span<int8_t> handle_types_left() const; + blender::MutableSpan<int8_t> handle_types_left(); blender::Span<blender::float3> handle_positions_left() const; /** * Get writable access to the handle position. @@ -333,8 +317,8 @@ class BezierSpline final : public Spline { * uninitialized memory while auto-generating handles. */ blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false); - blender::Span<HandleType> handle_types_right() const; - blender::MutableSpan<HandleType> handle_types_right(); + blender::Span<int8_t> handle_types_right() const; + blender::MutableSpan<int8_t> handle_types_right(); blender::Span<blender::float3> handle_positions_right() const; /** * Get writable access to the handle position. @@ -519,7 +503,7 @@ class NURBSpline final : public Spline { mutable bool position_cache_dirty_ = true; public: - NURBSpline() : Spline(Type::NURBS) + NURBSpline() : Spline(CURVE_TYPE_NURBS) { } NURBSpline(const NURBSpline &other) @@ -586,7 +570,7 @@ class PolySpline final : public Spline { blender::Vector<float> tilts_; public: - PolySpline() : Spline(Type::Poly) + PolySpline() : Spline(CURVE_TYPE_POLY) { } PolySpline(const PolySpline &other) @@ -658,7 +642,7 @@ struct CurveEval { * \note If you are looping over all of the splines in the same scope anyway, * it's better to avoid calling this function, in case there are many splines. */ - bool has_spline_with_type(const Spline::Type type) const; + bool has_spline_with_type(const CurveType type) const; void resize(int size); /** @@ -708,3 +692,5 @@ struct CurveEval { std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve, const ListBase &nurbs_list); std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve); +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves); +Curves *curve_eval_to_curves(const CurveEval &curve_eval); diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index a9ab5b91e1a..40e8ee2f999 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -24,18 +24,30 @@ void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings, const struct SubsurfModifierData *smd, bool use_render_params); +bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierData *smd, + const struct Mesh *mesh); + +/** + * Return true if GPU subdivision evaluation is disabled by force due to incompatible mesh or + * modifier settings. This will only return true if GPU subdivision is enabled in the preferences + * and supported by the GPU. It is mainly useful for showing UI messages. + */ +bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh( + const struct SubsurfModifierData *smd, const struct Mesh *mesh); /** * \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled * modifier in the stack. */ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const struct Scene *scene, const struct Object *ob, + const struct Mesh *mesh, const struct SubsurfModifierData *smd, int required_mode, bool skip_check_is_last); bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene, const struct Object *ob, + const struct Mesh *mesh, int required_mode); extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv); diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index c73417d7740..77f01e7919d 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -90,7 +90,6 @@ typedef enum VolumeGridType { VOLUME_GRID_INT, VOLUME_GRID_INT64, VOLUME_GRID_MASK, - VOLUME_GRID_STRING, VOLUME_GRID_VECTOR_FLOAT, VOLUME_GRID_VECTOR_DOUBLE, VOLUME_GRID_VECTOR_INT, @@ -204,8 +203,6 @@ auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op) return op.template operator()<openvdb::Vec3IGrid>(); case VOLUME_GRID_VECTOR_DOUBLE: return op.template operator()<openvdb::Vec3dGrid>(); - case VOLUME_GRID_STRING: - return op.template operator()<openvdb::StringGrid>(); case VOLUME_GRID_MASK: return op.template operator()<openvdb::MaskGrid>(); case VOLUME_GRID_POINTS: diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index cf4a8137638..3f92d6fa117 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -69,12 +69,8 @@ void BKE_ffmpeg_filepath_get(char *string, void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); -void BKE_ffmpeg_codec_settings_verify(struct RenderData *rd); bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd); -int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); -void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); - void *BKE_ffmpeg_context_create(void); void BKE_ffmpeg_context_free(void *context_v); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index bf720fa1341..a12a956cbf5 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -103,6 +103,7 @@ set(SRC intern/cryptomatte.cc intern/curve.cc intern/curves.cc + intern/curves_geometry.cc intern/curve_bevel.c intern/curve_convert.c intern/curve_decimate.c @@ -130,6 +131,7 @@ set(SRC intern/fmodifier.c intern/freestyle.c intern/geometry_component_curve.cc + intern/geometry_component_curves.cc intern/geometry_component_instances.cc intern/geometry_component_mesh.cc intern/geometry_component_pointcloud.cc @@ -199,7 +201,7 @@ set(SRC intern/mesh_sample.cc intern/mesh_tangent.c intern/mesh_tessellate.c - intern/mesh_validate.c + intern/mesh_validate.cc intern/mesh_wrapper.c intern/modifier.c intern/movieclip.c @@ -341,6 +343,7 @@ set(SRC BKE_cryptomatte.hh BKE_curve.h BKE_curves.h + BKE_curves.hh BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h @@ -375,6 +378,7 @@ set(SRC BKE_idprop.hh BKE_idtype.h BKE_image.h + BKE_image_partial_update.hh BKE_image_save.h BKE_ipo.h BKE_kelvinlet.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 542be4027bc..39074a5c75f 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -546,6 +546,7 @@ static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer) if (em) { mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, me); + BKE_mesh_ensure_default_orig_index_customdata(mesh); } else { mesh = BKE_mesh_copy_for_eval(me, true); @@ -1142,12 +1143,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, * we need to apply these back onto the Mesh. If we have no * Mesh then we need to build one. */ if (mesh_final == nullptr) { - /* NOTE: this check on cdmask is a bit dodgy, it handles the issue at stake here (see T68211), - * but other cases might require similar handling? - * Could be a good idea to define a proper CustomData_MeshMask for that then. */ - if (deformed_verts == nullptr && allow_shared_mesh && - (final_datamask.lmask & CD_MASK_NORMAL) == 0 && - (final_datamask.pmask & CD_MASK_NORMAL) == 0) { + if (deformed_verts == nullptr && allow_shared_mesh) { mesh_final = mesh_input; } else { @@ -1364,6 +1360,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, em_input, &final_datamask, nullptr, mesh_input); } + /* The mesh from edit mode should not have any original index layers already, since those + * are added during evaluation when necessary and are redundant on an original mesh. */ + BLI_assert(CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr && + CustomData_get_layer(&em_input->bm->edata, CD_ORIGINDEX) == nullptr && + CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr); + /* Clear errors before evaluation. */ BKE_modifiers_clear_errors(ob); @@ -1402,6 +1404,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) { if (mesh_final == nullptr) { mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input); + BKE_mesh_ensure_default_orig_index_customdata(mesh_final); ASSERT_IS_VALID_MESH(mesh_final); } BLI_assert(deformed_verts != nullptr); @@ -1510,7 +1513,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } else { Mesh *me_orig = mesh_input; - if (me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) { + /* Modifying the input mesh is weak, however as there can only be one object in edit mode + * even if multiple are sharing the same mesh this should be thread safe. */ + if ((me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) && (ob->mode & OB_MODE_EDIT)) { if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) { BKE_mesh_runtime_reset_edit_data(me_orig); } @@ -1819,9 +1824,8 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, mesh_eval = BKE_object_get_evaluated_mesh(ob); } - if (mesh_eval != nullptr) { - BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL)); - } + BKE_mesh_assert_normals_dirty_or_calculated(mesh_eval); + return mesh_eval; } diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 6a999cde6eb..55aba1d22c3 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1244,7 +1244,7 @@ void BKE_pose_update_constraint_flags(bPose *pose) /* if we have a valid target, make sure that this will get updated on frame-change * (needed for when there is no anim-data for this pose) */ - if ((data->tar) && (data->tar->type == OB_CURVE)) { + if ((data->tar) && (data->tar->type == OB_CURVES_LEGACY)) { pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND; } } diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 0c9202400ad..861a89ea9d7 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -763,7 +763,7 @@ static bool fcurves_path_rename_fix(ID *owner_id, if (fcu->rna_path != old_path) { bActionGroup *agrp = fcu->grp; is_changed = true; - if ((agrp != NULL) && STREQ(oldName, agrp->name)) { + if (oldName != NULL && (agrp != NULL) && STREQ(oldName, agrp->name)) { BLI_strncpy(agrp->name, newName, sizeof(agrp->name)); } } diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c index 57f64d7a0f8..1f8c6df6147 100644 --- a/source/blender/blenkernel/intern/anim_path.c +++ b/source/blender/blenkernel/intern/anim_path.c @@ -55,7 +55,7 @@ float BKE_anim_path_get_length(const CurveCache *curve_cache) void BKE_anim_path_calc_data(Object *ob) { - if (ob == NULL || ob->type != OB_CURVE) { + if (ob == NULL || ob->type != OB_CURVES_LEGACY) { return; } if (ob->runtime.curve_cache == NULL) { @@ -222,7 +222,7 @@ bool BKE_where_on_path(const Object *ob, float *r_radius, float *r_weight) { - if (ob == NULL || ob->type != OB_CURVE) { + if (ob == NULL || ob->type != OB_CURVES_LEGACY) { return false; } Curve *cu = ob->data; diff --git a/source/blender/blenkernel/intern/anim_visualization.c b/source/blender/blenkernel/intern/anim_visualization.c index 53a3a7e3712..f4c6a29c252 100644 --- a/source/blender/blenkernel/intern/anim_visualization.c +++ b/source/blender/blenkernel/intern/anim_visualization.c @@ -153,6 +153,11 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports, if ((mpath->start_frame != mpath->end_frame) && (mpath->length > 0)) { /* outer check ensures that we have some curve data for this path */ if (mpath->length == expected_length) { + /* The length might be the same, but the start and end could be different */ + if (mpath->start_frame != avs->path_sf) { + mpath->start_frame = avs->path_sf; + mpath->end_frame = avs->path_ef; + } /* return/use this as it is already valid length */ return mpath; } diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 099588a0e14..361ab176abd 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -70,7 +70,7 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), ik_data = con->data; /* Target can only be a curve. */ - if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) { + if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVES_LEGACY)) { continue; } /* Skip if disabled. */ diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index 45889099567..1d862e5e4d4 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -52,7 +52,7 @@ TEST_F(AssetLibraryTest, bke_asset_library_load) ASSERT_NE(nullptr, service); /* Check that the catalogs defined in the library are actually loaded. This just tests one single - * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for + * catalog, as that indicates the file has been loaded. Testing that loading went OK is for * the asset catalog service tests. */ const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78"); AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 4eff878778a..bfc4c8fcde0 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -207,6 +207,16 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; }; +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size) +{ + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); +} + +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +{ + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); +} + /** * This provider is used to provide access to builtin attributes. It supports making internal types * available as different types. For example, the vertex position attribute is stored as part of diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index f06274c34d7..ce36bfe81be 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -927,8 +927,13 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d * processed, so we need to recursively deal with them here. */ /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of - * shape-key referencing the shape-key itself). */ - if (id != cb_data->id_self) { + * shape-key referencing the shape-key itself). + * NOTE: in case both IDs (owner and 'used' ones) are non-linkable, we can assume we can break + * the dependency here. Indeed, either they are both linked in another way (through their own + * meshes for shape keys e.g.), or this is an unsupported case (two shape-keys depending on + * each-other need to be also 'linked' in by their respective meshes, independent shape-keys + * are not allowed). ref T96048. */ + if (id != cb_data->id_self && BKE_idtype_idcode_is_linkable(GS(cb_data->id_self->name))) { BKE_library_foreach_ID_link( cb_data->bmain, id, foreach_libblock_link_append_callback, data, IDWALK_NOP); } @@ -1449,7 +1454,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, BlendfileLinkAppendContextItem *item; /* We remove it from current Main, and add it to items to link... */ - /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */ + /* Note that non-linkable IDs (like e.g. shape-keys) are also explicitly linked here... */ BLI_remlink(lbarray[lba_idx], id); /* Usual special code for ShapeKeys snowflakes... */ Key *old_key = BKE_key_from_id(id); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index f091ebe1e32..6ee6ff7f41d 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -144,10 +144,10 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) /* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided * in IDType callbacks. Higher-level ID management code usually does not expect such things and * does not deal properly with it. */ - /* NOTE: assert below ensures that the comment above is valid, and that that exception is + /* NOTE: assert below ensures that the comment above is valid, and that exception is * acceptable for the time being. */ BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); - BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); + BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == NULL); } if (force_local) { diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 57a95891a92..b840fb1e665 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -550,9 +550,8 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 typedef struct CameraViewFrameData { - float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes */ - float normal_tx[CAMERA_VIEWFRAME_NUM_PLANES][3]; - float dist_vals_sq[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance squared (signed) */ + float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */ + float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */ unsigned int tot; /* Ortho camera only. */ @@ -569,8 +568,8 @@ static void camera_to_frame_view_cb(const float co[3], void *user_data) CameraViewFrameData *data = (CameraViewFrameData *)user_data; for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { - const float nd = dist_signed_squared_to_plane_v3(co, data->plane_tx[i]); - CLAMP_MAX(data->dist_vals_sq[i], nd); + const float nd = plane_point_side_v3(data->plane_tx[i], co); + CLAMP_MAX(data->dist_vals[i], nd); } if (data->is_ortho) { @@ -625,10 +624,11 @@ static void camera_frame_fit_data_init(const Scene *scene, /* Rotate planes and get normals from them */ for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { mul_m4_v4(camera_rotmat_transposed_inversed, data->plane_tx[i]); - normalize_v3_v3(data->normal_tx[i], data->plane_tx[i]); + /* Normalize. */ + data->plane_tx[i][3] /= normalize_v3(data->plane_tx[i]); } - copy_v4_fl(data->dist_vals_sq, FLT_MAX); + copy_v4_fl(data->dist_vals, FLT_MAX); data->tot = 0; data->is_ortho = params->is_ortho; if (params->is_ortho) { @@ -653,14 +653,9 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *cam_axis_x = data->camera_rotmat[0]; const float *cam_axis_y = data->camera_rotmat[1]; const float *cam_axis_z = data->camera_rotmat[2]; - float dists[CAMERA_VIEWFRAME_NUM_PLANES]; + const float *dists = data->dist_vals; float scale_diff; - /* apply the dist-from-plane's to the transformed plane points */ - for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { - dists[i] = sqrtf_signed(data->dist_vals_sq[i]); - } - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { scale_diff = (dists[1] + dists[3]) * (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); @@ -687,8 +682,8 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, /* apply the dist-from-plane's to the transformed plane points */ for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { float co[3]; - mul_v3_v3fl(co, data->normal_tx[i], sqrtf_signed(data->dist_vals_sq[i])); - plane_from_point_normal_v3(plane_tx[i], co, data->normal_tx[i]); + mul_v3_v3fl(co, data->plane_tx[i], data->dist_vals[i]); + plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]); } if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) || diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 69002a71f1d..2afe4dda35c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -529,8 +529,8 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[ float vec[3] = {0.0f, 0.0f, 0.0f}; float normal[3] = {0.0f, 0.0f, 0.0f}; float weightsum = 0.0f; - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval); if (me_eval) { + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval); const MDeformVert *dvert = CustomData_get_layer(&me_eval->vdata, CD_MDEFORMVERT); int numVerts = me_eval->totvert; @@ -1493,7 +1493,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph), { bFollowPathConstraint *data = con->data; - if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) { + if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) { Curve *cu = ct->tar->data; float vec[4], dir[3], radius; float curvetime; @@ -2479,7 +2479,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), #endif if (VALID_CONS_TARGET(ct)) { - if (ct->tar->type == OB_CURVE && ct->tar->runtime.curve_cache == NULL) { + if (ct->tar->type == OB_CURVES_LEGACY && ct->tar->runtime.curve_cache == NULL) { unit_m4(ct->matrix); return; } @@ -3867,7 +3867,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar bConstraintTarget *ct = targets->first; /* only evaluate if there is a target and it is a curve */ - if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) { + if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) { float obmat[4][4], ownLoc[3]; float curveMin[3], curveMax[3]; float targetMatrix[4][4]; diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index d1374958763..28bcd961e26 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1148,7 +1148,7 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, switch (obedit->type) { case OB_MESH: return CTX_MODE_EDIT_MESH; - case OB_CURVE: + case OB_CURVES_LEGACY: return CTX_MODE_EDIT_CURVE; case OB_SURF: return CTX_MODE_EDIT_SURFACE; @@ -1160,6 +1160,8 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, return CTX_MODE_EDIT_METABALL; case OB_LATTICE: return CTX_MODE_EDIT_LATTICE; + case OB_CURVES: + return CTX_MODE_EDIT_CURVES; } } else { @@ -1227,6 +1229,7 @@ static const char *data_mode_strings[] = { "armature_edit", "mball_edit", "lattice_edit", + "curves_edit", "posemode", "sculpt_mode", "weightpaint", diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index c6c0111780e..6be04b79761 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -115,6 +115,8 @@ static void curve_free_data(ID *id) MEM_SAFE_FREE(curve->str); MEM_SAFE_FREE(curve->strinfo); MEM_SAFE_FREE(curve->tb); + + delete curve->curve_eval; } static void curve_foreach_id(ID *id, LibraryForeachIDData *data) @@ -293,14 +295,14 @@ static void curve_blend_read_expand(BlendExpander *expander, ID *id) BLO_expand(expander, cu->textoncurve); } -IDTypeInfo IDType_ID_CU = { - /* id_code */ ID_CU, - /* id_filter */ FILTER_ID_CU, - /* main_listbase_index */ INDEX_ID_CU, +IDTypeInfo IDType_ID_CU_LEGACY = { + /* id_code */ ID_CU_LEGACY, + /* id_filter */ FILTER_ID_CU_LEGACY, + /* main_listbase_index */ INDEX_ID_CU_LEGACY, /* struct_size */ sizeof(Curve), /* name */ "Curve", /* name_plural */ "curves", - /* translation_context */ BLT_I18NCONTEXT_ID_CURVE, + /* translation_context */ BLT_I18NCONTEXT_ID_CURVE_LEGACY, /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* asset_type_info */ nullptr, @@ -406,7 +408,7 @@ Curve *BKE_curve_add(Main *bmain, const char *name, int type) Curve *cu; /* We cannot use #BKE_id_new here as we need some custom initialization code. */ - cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU, name, 0); + cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU_LEGACY, name, 0); BKE_curve_init(cu, type); @@ -440,7 +442,7 @@ short BKE_curve_type_get(const Curve *cu) } if (!cu->type) { - type = OB_CURVE; + type = OB_CURVES_LEGACY; LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { if (nu->pntsv > 1) { @@ -473,7 +475,7 @@ void BKE_curve_type_test(Object *ob) { ob->type = BKE_curve_type_get((Curve *)ob->data); - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; if (CU_IS_2D(cu)) { BKE_curve_dimension_update(cu); diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index ffef22fb498..6f32f0f5e6f 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -228,7 +228,7 @@ static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp) if (cu->bevobj == NULL) { return; } - if (cu->bevobj->type != OB_CURVE) { + if (cu->bevobj->type != OB_CURVES_LEGACY) { return; } diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c index 285e6978522..129e930a21e 100644 --- a/source/blender/blenkernel/intern/curve_convert.c +++ b/source/blender/blenkernel/intern/curve_convert.c @@ -27,7 +27,7 @@ static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph) Object *evaluated_object = DEG_get_evaluated_object(depsgraph, object); BKE_vfont_to_curve_nubase(evaluated_object, FO_EDIT, &new_curve->nurb); - new_curve->type = OB_CURVE; + new_curve->type = OB_CURVES_LEGACY; new_curve->flag &= ~CU_3D; BKE_curve_dimension_update(new_curve); @@ -55,7 +55,7 @@ static Curve *curve_from_curve_object(Object *object, Depsgraph *depsgraph, bool Curve *BKE_curve_new_from_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers) { - if (!ELEM(object->type, OB_FONT, OB_CURVE)) { + if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) { return NULL; } diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index f76e4202994..fb082fccc0b 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -211,7 +211,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, bool use_dverts = false; int cd_dvert_offset; - if (ob_curve->type != OB_CURVE) { + if (ob_curve->type != OB_CURVES_LEGACY) { return; } @@ -404,7 +404,7 @@ void BKE_curve_deform_co(const Object *ob_curve, CurveDeform cd; float quat[4]; - if (ob_curve->type != OB_CURVE) { + if (ob_curve->type != OB_CURVES_LEGACY) { unit_m3(r_mat); return; } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 7fb833e67f8..d6525a11cff 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -13,6 +13,8 @@ #include "BKE_anonymous_attribute.hh" #include "BKE_curve.h" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_spline.hh" using blender::Array; @@ -23,8 +25,15 @@ using blender::Map; using blender::MutableSpan; using blender::Span; using blender::StringRefNull; +using blender::VArray; +using blender::VArray_Span; using blender::Vector; using blender::bke::AttributeIDRef; +using blender::bke::OutputAttribute; +using blender::bke::OutputAttribute_Typed; +using blender::bke::ReadAttributeLookup; +using blender::fn::GVArray; +using blender::fn::GVArray_GSpan; blender::Span<SplinePtr> CurveEval::splines() const { @@ -36,7 +45,7 @@ blender::MutableSpan<SplinePtr> CurveEval::splines() return splines_; } -bool CurveEval::has_spline_with_type(const Spline::Type type) const +bool CurveEval::has_spline_with_type(const CurveType type) const { for (const SplinePtr &spline : this->splines()) { if (spline->type() == type) { @@ -160,24 +169,24 @@ void CurveEval::mark_cache_invalid() } } -static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) +static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { case HD_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case HD_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case HD_VECT: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; case HD_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case HD_AUTO_ANIM: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case HD_ALIGN_DOUBLESIDE: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode) @@ -220,8 +229,8 @@ static SplinePtr spline_from_dna_bezier(const Nurb &nurb) MutableSpan<float3> positions = spline->positions(); MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true); MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true); - MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left(); - MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right(); + MutableSpan<int8_t> handle_types_left = spline->handle_types_left(); + MutableSpan<int8_t> handle_types_right = spline->handle_types_right(); MutableSpan<float> radii = spline->radii(); MutableSpan<float> tilts = spline->tilts(); @@ -336,6 +345,186 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve)); } +static void copy_attributes_between_components(const GeometryComponent &src_component, + GeometryComponent &dst_component, + Span<std::string> skip) +{ + src_component.attribute_foreach( + [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (id.is_named() && skip.contains(id.name())) { + return true; + } + + GVArray src_attribute = src_component.attribute_try_get_for_read( + id, meta_data.domain, meta_data.data_type); + if (!src_attribute) { + return true; + } + GVArray_GSpan src_attribute_data{src_attribute}; + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + id, meta_data.domain, meta_data.data_type); + if (!dst_attribute) { + return true; + } + dst_attribute.varray().set_all(src_attribute_data.data()); + dst_attribute.save(); + return true; + }); +} + +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) +{ + CurveComponent src_component; + src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly); + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves.geometry); + + VArray_Span<float> nurbs_weights{ + src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; + VArray_Span<int> nurbs_orders{ + src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; + VArray_Span<int8_t> nurbs_knots_modes{ + src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; + + VArray_Span<int8_t> handle_types_right{ + src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; + VArray_Span<int8_t> handle_types_left{ + src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; + + /* Create splines with the correct size and type. */ + VArray<int8_t> curve_types = geometry.curve_types(); + std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>(); + for (const int curve_index : curve_types.index_range()) { + const IndexRange point_range = geometry.range_for_curve(curve_index); + + std::unique_ptr<Spline> spline; + switch (curve_types[curve_index]) { + case CURVE_TYPE_POLY: { + spline = std::make_unique<PolySpline>(); + spline->resize(point_range.size()); + break; + } + case CURVE_TYPE_BEZIER: { + std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>(); + bezier_spline->resize(point_range.size()); + bezier_spline->handle_types_left().copy_from(handle_types_left.slice(point_range)); + bezier_spline->handle_types_right().copy_from(handle_types_right.slice(point_range)); + + spline = std::move(bezier_spline); + break; + } + case CURVE_TYPE_NURBS: { + std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>(); + nurb_spline->resize(point_range.size()); + nurb_spline->weights().copy_from(nurbs_weights.slice(point_range)); + nurb_spline->set_order(nurbs_orders[curve_index]); + nurb_spline->knots_mode = static_cast<NURBSpline::KnotsMode>( + nurbs_knots_modes[curve_index]); + + spline = std::move(nurb_spline); + break; + } + case CURVE_TYPE_CATMULL_ROM: + /* Not supported yet. */ + BLI_assert_unreachable(); + continue; + } + spline->positions().fill(float3(0)); + spline->tilts().fill(0.0f); + spline->radii().fill(1.0f); + curve_eval->add_spline(std::move(spline)); + } + + CurveComponentLegacy dst_component; + dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable); + + copy_attributes_between_components(src_component, + dst_component, + {"curve_type", + "nurbs_weight", + "nurbs_order", + "knots_mode", + "handle_type_right", + "handle_type_left"}); + + return curve_eval; +} + +Curves *curve_eval_to_curves(const CurveEval &curve_eval) +{ + Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(), + curve_eval.splines().size()); + CurveComponent dst_component; + dst_component.replace(curves, GeometryOwnershipType::Editable); + + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + geometry.offsets().copy_from(curve_eval.control_point_offsets()); + MutableSpan<int8_t> curve_types = geometry.curve_types(); + + OutputAttribute_Typed<float> nurbs_weight; + OutputAttribute_Typed<int> nurbs_order; + OutputAttribute_Typed<int8_t> nurbs_knots_mode; + if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) { + nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight", + ATTR_DOMAIN_POINT); + nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order", + ATTR_DOMAIN_CURVE); + nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode", + ATTR_DOMAIN_CURVE); + } + OutputAttribute_Typed<int8_t> handle_type_right; + OutputAttribute_Typed<int8_t> handle_type_left; + if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) { + handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>( + "handle_type_right", ATTR_DOMAIN_POINT); + handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left", + ATTR_DOMAIN_POINT); + } + + for (const int curve_index : curve_eval.splines().index_range()) { + const Spline &spline = *curve_eval.splines()[curve_index]; + curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); + + const IndexRange point_range = geometry.range_for_curve(curve_index); + + switch (spline.type()) { + case CURVE_TYPE_POLY: + break; + case CURVE_TYPE_BEZIER: { + const BezierSpline &src = static_cast<const BezierSpline &>(spline); + handle_type_right.as_span().slice(point_range).copy_from(src.handle_types_right()); + handle_type_left.as_span().slice(point_range).copy_from(src.handle_types_left()); + break; + } + case CURVE_TYPE_NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(spline); + nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode); + nurbs_order.as_span()[curve_index] = src.order(); + nurbs_weight.as_span().slice(point_range).copy_from(src.weights()); + break; + } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } + } + } + + nurbs_weight.save(); + nurbs_order.save(); + nurbs_knots_mode.save(); + handle_type_right.save(); + handle_type_left.save(); + + CurveComponentLegacy src_component; + src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly); + + copy_attributes_between_components(src_component, dst_component, {}); + + return curves; +} + void CurveEval::assert_valid_point_attributes() const { #ifdef DEBUG diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 51edf4a6591..5d80ef47908 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -172,7 +172,8 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } } - if (fill_caps && profile.is_cyclic()) { + const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic(); + if (has_caps) { const int poly_size = info.spline_edge_len * info.profile_edge_len; const int cap_loop_offset = info.loop_offset + poly_size * 4; const int cap_poly_offset = info.poly_offset + poly_size; @@ -225,7 +226,7 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } /* Mark edge loops from sharp vector control points sharp. */ - if (profile.type() == Spline::Type::Bezier) { + if (profile.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile); Span<int> control_point_offsets = bezier_spline.control_point_offsets(); for (const int i : IndexRange(bezier_spline.size())) { @@ -256,7 +257,8 @@ static inline int spline_extrude_loop_size(const Spline &curve, const bool fill_caps) { const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; - const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); + const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0; return tube + caps; } @@ -265,7 +267,8 @@ static inline int spline_extrude_poly_size(const Spline &curve, const bool fill_caps) { const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); - const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); + const int caps = has_caps ? 2 : 0; return tube + caps; } diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index ccc20d5118a..838f7f28e93 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -14,16 +14,18 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" +#include "BLI_bounds.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_vector.hh" #include "BLI_rand.hh" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_anim_data.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -44,11 +46,11 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::RandomNumberGenerator; +using blender::Span; static const char *ATTR_POSITION = "position"; -static const char *ATTR_RADIUS = "radius"; -static void curves_random(Curves *curves); +static void update_custom_data_pointers(Curves &curves); static void curves_init_data(ID *id) { @@ -57,50 +59,36 @@ static void curves_init_data(ID *id) MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id); - CustomData_reset(&curves->geometry.point_data); - CustomData_reset(&curves->geometry.curve_data); - - CustomData_add_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT3, - CD_CALLOC, - nullptr, - curves->geometry.point_size, - ATTR_POSITION); - CustomData_add_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT, - CD_CALLOC, - nullptr, - curves->geometry.point_size, - ATTR_RADIUS); - - BKE_curves_update_customdata_pointers(curves); - - curves_random(curves); + new (&curves->geometry) blender::bke::CurvesGeometry(); } static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) { + using namespace blender; + Curves *curves_dst = (Curves *)id_dst; const Curves *curves_src = (const Curves *)id_src; curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); - curves_dst->geometry.point_size = curves_src->geometry.point_size; - curves_dst->geometry.curve_size = curves_src->geometry.curve_size; + const bke::CurvesGeometry &src = bke::CurvesGeometry::wrap(curves_src->geometry); + bke::CurvesGeometry &dst = bke::CurvesGeometry::wrap(curves_dst->geometry); + + /* We need special handling here because the generic ID management code has already done a + * shallow copy from the source to the destination, and because the copy-on-write functionality + * isn't supported more generically yet. */ + + dst.point_size = src.point_size; + dst.curve_size = src.curve_size; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&curves_src->geometry.point_data, - &curves_dst->geometry.point_data, - CD_MASK_ALL, - alloc_type, - curves_dst->geometry.point_size); - CustomData_copy(&curves_src->geometry.curve_data, - &curves_dst->geometry.curve_data, - CD_MASK_ALL, - alloc_type, - curves_dst->geometry.curve_size); - BKE_curves_update_customdata_pointers(curves_dst); - - curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets)); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size); + + dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); + + dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__); + + dst.update_customdata_pointers(); curves_dst->batch_cache = nullptr; } @@ -110,12 +98,9 @@ static void curves_free_data(ID *id) Curves *curves = (Curves *)id; BKE_animdata_free(&curves->id, false); - BKE_curves_batch_cache_free(curves); - - CustomData_free(&curves->geometry.point_data, curves->geometry.point_size); - CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size); + blender::bke::CurvesGeometry::wrap(curves->geometry).~CurvesGeometry(); - MEM_SAFE_FREE(curves->geometry.offsets); + BKE_curves_batch_cache_free(curves); MEM_SAFE_FREE(curves->mat); } @@ -126,6 +111,7 @@ static void curves_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < curves->totcol; i++) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER); } + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->surface, IDWALK_CB_NOP); } static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -157,7 +143,7 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets); + BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { @@ -182,9 +168,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size); - BKE_curves_update_customdata_pointers(curves); + update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets); + BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + + curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); /* Materials */ BLO_read_pointer_array(reader, (void **)&curves->mat); @@ -196,6 +184,7 @@ static void curves_blend_read_lib(BlendLibReader *reader, ID *id) for (int a = 0; a < curves->totcol; a++) { BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]); } + BLO_read_id_address(reader, curves->id.lib, &curves->surface); } static void curves_blend_read_expand(BlendExpander *expander, ID *id) @@ -204,6 +193,7 @@ static void curves_blend_read_expand(BlendExpander *expander, ID *id) for (int a = 0; a < curves->totcol; a++) { BLO_expand(expander, curves->mat[a]); } + BLO_expand(expander, curves->surface); } IDTypeInfo IDType_ID_CV = { @@ -236,53 +226,9 @@ IDTypeInfo IDType_ID_CV = { /*lib_override_apply_post */ nullptr, }; -static void curves_random(Curves *curves) +static void update_custom_data_pointers(Curves &curves) { - CurvesGeometry &geometry = curves->geometry; - const int numpoints = 8; - - geometry.curve_size = 500; - - geometry.curve_size = 500; - geometry.point_size = geometry.curve_size * numpoints; - - curves->geometry.offsets = (int *)MEM_calloc_arrayN( - curves->geometry.curve_size + 1, sizeof(int), __func__); - CustomData_realloc(&geometry.point_data, geometry.point_size); - CustomData_realloc(&geometry.curve_data, geometry.curve_size); - BKE_curves_update_customdata_pointers(curves); - - MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1}; - MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size}; - MutableSpan<float> radii{geometry.radius, geometry.point_size}; - - for (const int i : offsets.index_range()) { - geometry.offsets[i] = numpoints * i; - } - - RandomNumberGenerator rng; - - for (int i = 0; i < geometry.curve_size; i++) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); - MutableSpan<float3> curve_positions = positions.slice(curve_range); - MutableSpan<float> curve_radii = radii.slice(curve_range); - - const float theta = 2.0f * M_PI * rng.get_float(); - const float phi = saacosf(2.0f * rng.get_float() - 1.0f); - - float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; - no = blender::math::normalize(no); - - float3 co = no; - for (int key = 0; key < numpoints; key++) { - float t = key / (float)(numpoints - 1); - curve_positions[key] = co; - curve_radii[key] = 0.02f * (1.0f - t); - - float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; - co += (offset + no) / numpoints; - } - } + blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers(); } void *BKE_curves_add(Main *bmain, const char *name) @@ -304,18 +250,13 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) if (ob->runtime.bb == nullptr) { ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - float min[3], max[3]; - INIT_MINMAX(min, max); - - float(*curves_co)[3] = curves->geometry.position; - float *curves_radius = curves->geometry.radius; - for (int a = 0; a < curves->geometry.point_size; a++) { - float *co = curves_co[a]; - float radius = (curves_radius) ? curves_radius[a] : 0.0f; - const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; - const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; - DO_MIN(co_min, min); - DO_MAX(co_max, max); + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + + float3 min(FLT_MAX); + float3 max(-FLT_MAX); + if (!geometry.bounds_min_max(min, max)) { + min = float3(-1); + max = float3(1); } BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); @@ -324,46 +265,11 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_curves_update_customdata_pointers(Curves *curves) -{ - curves->geometry.position = (float(*)[3])CustomData_get_layer_named( - &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION); - curves->geometry.radius = (float *)CustomData_get_layer_named( - &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS); -} - bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer) { return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION); } -/* Dependency Graph */ - -Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve) -{ - Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); - - STRNCPY(curves_dst->id.name, curves_src->id.name); - curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); - curves_dst->totcol = curves_src->totcol; - - curves_dst->geometry.point_size = totpoint; - curves_dst->geometry.curve_size = totcurve; - CustomData_copy(&curves_src->geometry.point_data, - &curves_dst->geometry.point_data, - CD_MASK_ALL, - CD_CALLOC, - totpoint); - CustomData_copy(&curves_src->geometry.curve_data, - &curves_dst->geometry.curve_data, - CD_MASK_ALL, - CD_CALLOC, - totcurve); - BKE_curves_update_customdata_pointers(curves_dst); - - return curves_dst; -} - Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference) { int flags = LIB_ID_COPY_LOCALIZE; @@ -409,16 +315,16 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, curves = BKE_curves_copy_for_eval(curves, true); } - /* Ensure we are not overwriting referenced data. */ - CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT3, - ATTR_POSITION, - curves->geometry.point_size); - BKE_curves_update_customdata_pointers(curves); - /* Created deformed coordinates array on demand. */ - mti->deformVerts( - md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size); + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); + MutableSpan<float3> positions = geometry.positions(); + + mti->deformVerts(md, + &mectx, + nullptr, + reinterpret_cast<float(*)[3]>(positions.data()), + curves->geometry.point_size); } } @@ -457,3 +363,24 @@ void BKE_curves_batch_cache_free(Curves *curves) BKE_curves_batch_cache_free_cb(curves); } } + +namespace blender::bke { + +Curves *curves_new_nomain(const int point_size, const int curves_size) +{ + Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); + geometry.resize(point_size, curves_size); + return curves; +} + +Curves *curves_new_nomain_single(const int point_size, const CurveType type) +{ + Curves *curves = curves_new_nomain(point_size, 1); + CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); + geometry.offsets().last() = point_size; + geometry.curve_types().first() = type; + return curves; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc new file mode 100644 index 00000000000..3eea579230a --- /dev/null +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bounds.hh" + +#include "DNA_curves_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" + +namespace blender::bke { + +static const std::string ATTR_POSITION = "position"; +static const std::string ATTR_RADIUS = "radius"; +static const std::string ATTR_CURVE_TYPE = "curve_type"; +static const std::string ATTR_CYCLIC = "cyclic"; + +/* -------------------------------------------------------------------- */ +/** \name Constructors/Destructor + * \{ */ + +CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) +{ +} + +CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +{ + this->point_size = point_size; + this->curve_size = curve_size; + CustomData_reset(&this->point_data); + CustomData_reset(&this->curve_data); + + CustomData_add_layer_named(&this->point_data, + CD_PROP_FLOAT3, + CD_DEFAULT, + nullptr, + this->point_size, + ATTR_POSITION.c_str()); + + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + + this->update_customdata_pointers(); + + this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); +} + +/** + * \note Expects `dst` to be initialized, since the original attributes must be freed. + */ +static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) +{ + CustomData_free(&dst.point_data, dst.point_size); + CustomData_free(&dst.curve_data, dst.curve_size); + dst.point_size = src.point_size; + dst.curve_size = src.curve_size; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size); + + MEM_SAFE_FREE(dst.curve_offsets); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.offsets().copy_from(src.offsets()); + + dst.tag_topology_changed(); + + dst.update_customdata_pointers(); +} + +CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) + : CurvesGeometry(other.point_size, other.curve_size) +{ + copy_curves_geometry(*this, other); +} + +CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) +{ + if (this != &other) { + copy_curves_geometry(*this, other); + } + return *this; +} + +CurvesGeometry::~CurvesGeometry() +{ + CustomData_free(&this->point_data, this->point_size); + CustomData_free(&this->curve_data, this->curve_size); + MEM_SAFE_FREE(this->curve_offsets); + MEM_delete(this->runtime); + this->runtime = nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accessors + * \{ */ + +int CurvesGeometry::points_size() const +{ + return this->point_size; +} +int CurvesGeometry::curves_size() const +{ + return this->curve_size; +} +IndexRange CurvesGeometry::points_range() const +{ + return IndexRange(this->points_size()); +} +IndexRange CurvesGeometry::curves_range() const +{ + return IndexRange(this->curves_size()); +} + +int CurvesGeometry::evaluated_points_size() const +{ + /* TODO: Implement when there are evaluated points. */ + return 0; +} + +IndexRange CurvesGeometry::range_for_curve(const int index) const +{ + const int offset = this->curve_offsets[index]; + const int offset_next = this->curve_offsets[index + 1]; + return {offset, offset_next - offset}; +} + +VArray<int8_t> CurvesGeometry::curve_types() const +{ + if (const int8_t *data = (const int8_t *)CustomData_get_layer_named( + &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str())) { + return VArray<int8_t>::ForSpan({data, this->curve_size}); + } + return VArray<int8_t>::ForSingle(CURVE_TYPE_CATMULL_ROM, this->curve_size); +} + +MutableSpan<int8_t> CurvesGeometry::curve_types() +{ + int8_t *data = (int8_t *)CustomData_add_layer_named(&this->curve_data, + CD_PROP_INT8, + CD_CALLOC, + nullptr, + this->curve_size, + ATTR_CURVE_TYPE.c_str()); + return {data, this->curve_size}; +} + +MutableSpan<float3> CurvesGeometry::positions() +{ + this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size); + return {(float3 *)this->position, this->point_size}; +} +Span<float3> CurvesGeometry::positions() const +{ + return {(const float3 *)this->position, this->point_size}; +} + +MutableSpan<int> CurvesGeometry::offsets() +{ + return {this->curve_offsets, this->curve_size + 1}; +} +Span<int> CurvesGeometry::offsets() const +{ + return {this->curve_offsets, this->curve_size + 1}; +} + +VArray<bool> CurvesGeometry::cyclic() const +{ + const bool *data = (const bool *)CustomData_get_layer_named( + &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); + if (data != nullptr) { + return VArray<bool>::ForSpan(Span(data, this->curve_size)); + } + return VArray<bool>::ForSingle(false, this->curve_size); +} + +MutableSpan<bool> CurvesGeometry::cyclic() +{ + bool *data = (bool *)CustomData_add_layer_named( + &this->curve_data, CD_PROP_BOOL, CD_CALLOC, nullptr, this->curve_size, ATTR_CYCLIC.c_str()); + return {data, this->curve_size}; +} + +void CurvesGeometry::resize(const int point_size, const int curve_size) +{ + if (point_size != this->point_size) { + CustomData_realloc(&this->point_data, point_size); + this->point_size = point_size; + } + if (curve_size != this->curve_size) { + CustomData_realloc(&this->curve_data, curve_size); + this->curve_size = curve_size; + this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curve_size + 1)); + } + this->tag_topology_changed(); + this->update_customdata_pointers(); +} + +void CurvesGeometry::tag_positions_changed() +{ + this->runtime->position_cache_dirty = true; + this->runtime->tangent_cache_dirty = true; + this->runtime->normal_cache_dirty = true; +} +void CurvesGeometry::tag_topology_changed() +{ + this->runtime->position_cache_dirty = true; + this->runtime->tangent_cache_dirty = true; + this->runtime->normal_cache_dirty = true; +} +void CurvesGeometry::tag_normals_changed() +{ + this->runtime->normal_cache_dirty = true; +} + +void CurvesGeometry::translate(const float3 &translation) +{ + MutableSpan<float3> positions = this->positions(); + threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) { + for (float3 &position : positions.slice(range)) { + position += translation; + } + }); +} + +void CurvesGeometry::transform(const float4x4 &matrix) +{ + MutableSpan<float3> positions = this->positions(); + threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) { + for (float3 &position : positions.slice(range)) { + position = matrix * position; + } + }); +} + +static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves) +{ + Span<float3> positions = curves.positions(); + if (curves.radius) { + Span<float> radii{curves.radius, curves.points_size()}; + return bounds::min_max_with_radii(positions, radii); + } + return bounds::min_max(positions); +} + +bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const +{ + const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this); + if (!bounds) { + return false; + } + min = math::min(bounds->min, min); + max = math::max(bounds->max, max); + return true; +} + +void CurvesGeometry::update_customdata_pointers() +{ + this->position = (float(*)[3])CustomData_get_layer_named( + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str()); + this->radius = (float *)CustomData_get_layer_named( + &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str()); + this->curve_type = (int8_t *)CustomData_get_layer_named( + &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Domain Interpolation + * \{ */ + +/** + * Mix together all of a curve's control point values. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<typename T> +static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + attribute_math::DefaultMixer<T> mixer(r_values); + for (const int i_curve : IndexRange(curves.curves_size())) { + for (const int i_point : curves.range_for_curve(i_curve)) { + mixer.mix_in(i_curve, old_values[i_point]); + } + } + mixer.finalize(); +} + +/** + * A curve is selected if all of its control points were selected. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<> +void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + r_values.fill(true); + for (const int i_curve : IndexRange(curves.curves_size())) { + for (const int i_point : curves.range_for_curve(i_curve)) { + if (!old_values[i_point]) { + r_values[i_curve] = false; + break; + } + } + } +} + +static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, + const GVArray &varray) +{ + GVArray new_varray; + attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { + Array<T> values(curves.curves_size()); + adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values); + new_varray = VArray<T>::ForContainer(std::move(values)); + } + }); + return new_varray; +} + +/** + * Copy the value from a curve to all of its points. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<typename T> +static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + for (const int i_curve : IndexRange(curves.curves_size())) { + r_values.slice(curves.range_for_curve(i_curve)).fill(old_values[i_curve]); + } +} + +static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves, + const GVArray &varray) +{ + GVArray new_varray; + attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { + using T = decltype(dummy); + Array<T> values(curves.points_size()); + adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values); + new_varray = VArray<T>::ForContainer(std::move(values)); + }); + return new_varray; +} + +fn::GVArray CurvesGeometry::adapt_domain(const fn::GVArray &varray, + const AttributeDomain from, + const AttributeDomain to) const +{ + if (!varray) { + return {}; + } + if (varray.is_empty()) { + return {}; + } + if (from == to) { + return varray; + } + + if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) { + return adapt_curve_domain_point_to_curve(*this, varray); + } + if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) { + return adapt_curve_domain_curve_to_point(*this, varray); + } + + BLI_assert_unreachable(); + return {}; +} + +/** \} */ + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 867bdcd06bd..4492f8bbc64 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1777,7 +1777,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 45: CD_PROP_INT8 */ - {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + {sizeof(int8_t), "MInt8Property", 1, N_("Int8"), nullptr, nullptr, nullptr, nullptr, nullptr}, /* 46: CD_HAIRMAPPING */ /* UNUSED */ {-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 47: CD_PROP_COLOR */ @@ -1959,10 +1959,10 @@ const CustomData_MeshMasks CD_MASK_BMESH = { CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), }; const CustomData_MeshMasks CD_MASK_EVERYTHING = { - /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | - CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | - CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | - CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), + /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | + CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | + CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | + CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), /* emask */ (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), @@ -1971,7 +1971,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = { CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PREVIEW_MCOL | CD_MASK_PROP_ALL), /* pmask */ - (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_FACEMAP | + (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ (CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV | diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6f1026f170f..1818e5a9490 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -263,7 +263,6 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, /* This should be ensured by cddata_masks we pass to code generating/giving us me_src now. */ BLI_assert(CustomData_get_layer(&me_src->ldata, CD_NORMAL) != NULL); - BLI_assert(CustomData_get_layer(&me_src->pdata, CD_NORMAL) != NULL); (void)me_src; float(*loop_nors_dst)[3]; @@ -318,15 +317,12 @@ static void data_transfer_dtdata_type_postprocess(Object *UNUSED(ob_src), const int num_polys_dst = me_dst->totpoly; MLoop *loops_dst = me_dst->mloop; const int num_loops_dst = me_dst->totloop; - CustomData *pdata_dst = &me_dst->pdata; CustomData *ldata_dst = &me_dst->ldata; - const float(*poly_nors_dst)[3] = CustomData_get_layer(pdata_dst, CD_NORMAL); + const float(*poly_nors_dst)[3] = BKE_mesh_poly_normals_ensure(me_dst); float(*loop_nors_dst)[3] = CustomData_get_layer(ldata_dst, CD_NORMAL); short(*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); - BLI_assert(poly_nors_dst); - if (!custom_nors_dst) { custom_nors_dst = CustomData_add_layer( ldata_dst, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, num_loops_dst); @@ -1379,7 +1375,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); if (me_dst) { - dirty_nors_dst = (me_dst->runtime.cd_dirty_vert & CD_NORMAL) != 0; + dirty_nors_dst = BKE_mesh_vertex_normals_are_dirty(me_dst); /* Never create needed custom layers on passed destination mesh * (assumed to *not* be ob_dst->data, aka modifier case). */ use_create = false; diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 793af80ec74..f0894ee04e2 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -590,7 +590,7 @@ static float displist_calc_taper(Depsgraph *depsgraph, Object *taperobj, float fac) { - if (taperobj == nullptr || taperobj->type != OB_CURVE) { + if (taperobj == nullptr || taperobj->type != OB_CURVES_LEGACY) { return 1.0; } @@ -865,7 +865,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, else { std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve( *cu, ob->runtime.curve_cache->deformed_nurbs); - geometry_set.replace_curve(curve_eval.release()); + geometry_set.replace_curve(curve_eval_to_curves(*curve_eval)); } for (; md; md = md->next) { @@ -1263,7 +1263,7 @@ static GeometrySet evaluate_curve_type_object(Depsgraph *depsgraph, const bool for_render, ListBase *r_dispbase) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT)); const Curve *cu = (const Curve *)ob->data; ListBase *deformed_nurbs = &ob->runtime.curve_cache->deformed_nurbs; @@ -1473,7 +1473,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, Object *ob, const bool for_render) { - BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)); Curve &cow_curve = *(Curve *)ob->data; BKE_object_free_derived_caches(ob); @@ -1490,14 +1490,14 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, else { GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase); - if (geometry.has_curve()) { + if (geometry.has_curves()) { /* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval * added to the curve here, it will also contain a copy of the original curve's data. This is * essential, because it maintains the expected behavior for evaluated curve data from before * the CurveEval data type was introduced, when an evaluated object's curve data was just a * copy of the original curve and everything else ended up in #CurveCache. */ CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>(); - cow_curve.curve_eval = curve_component.get_for_write(); + cow_curve.curve_eval = curves_to_curve_eval(*curve_component.get_for_read()).release(); BKE_object_eval_assign_data(ob, &cow_curve.id, false); } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 2f760597e1a..f2915a97746 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -143,7 +143,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef BLI_rng_srandom(eff->pd->rng, eff->pd->seed + cfra); } - if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) { + if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVES_LEGACY) { Curve *cu = eff->ob->data; if (cu->flag & CU_PATH) { if (eff->ob->runtime.curve_cache == NULL || @@ -161,7 +161,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef } else if (eff->pd->shape == PFIELD_SHAPE_SURFACE) { eff->surmd = (SurfaceModifierData *)BKE_modifiers_findby_type(eff->ob, eModifierType_Surface); - if (eff->ob->type == OB_CURVE) { + if (eff->ob->type == OB_CURVES_LEGACY) { eff->flag |= PE_USE_NORMAL_DATA; } } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index ef2fa2266c4..6f2760e91a6 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3292,7 +3292,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, mpolys = me->mpoly; mloops = me->mloop; - /* Get size (dimension) but considering scaling scaling. */ + /* Get size (dimension) but considering scaling. */ copy_v3_v3(cell_size_scaled, fds->cell_size); mul_v3_v3(cell_size_scaled, ob->scale); madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 5921f853389..0926d65b306 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -23,18 +23,18 @@ using blender::fn::GVArray_GSpan; /** \name Geometry Component Implementation * \{ */ -CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) { } -CurveComponent::~CurveComponent() +CurveComponentLegacy::~CurveComponentLegacy() { this->clear(); } -GeometryComponent *CurveComponent::copy() const +GeometryComponent *CurveComponentLegacy::copy() const { - CurveComponent *new_component = new CurveComponent(); + CurveComponentLegacy *new_component = new CurveComponentLegacy(); if (curve_ != nullptr) { new_component->curve_ = new CurveEval(*curve_); new_component->ownership_ = GeometryOwnershipType::Owned; @@ -42,30 +42,23 @@ GeometryComponent *CurveComponent::copy() const return new_component; } -void CurveComponent::clear() +void CurveComponentLegacy::clear() { BLI_assert(this->is_mutable()); if (curve_ != nullptr) { if (ownership_ == GeometryOwnershipType::Owned) { delete curve_; } - if (curve_for_render_ != nullptr) { - /* The curve created by this component should not have any edit mode data. */ - BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); - BKE_id_free(nullptr, curve_for_render_); - curve_for_render_ = nullptr; - } - curve_ = nullptr; } } -bool CurveComponent::has_curve() const +bool CurveComponentLegacy::has_curve() const { return curve_ != nullptr; } -void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) +void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership) { BLI_assert(this->is_mutable()); this->clear(); @@ -73,7 +66,7 @@ void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) ownership_ = ownership; } -CurveEval *CurveComponent::release() +CurveEval *CurveComponentLegacy::release() { BLI_assert(this->is_mutable()); CurveEval *curve = curve_; @@ -81,12 +74,12 @@ CurveEval *CurveComponent::release() return curve; } -const CurveEval *CurveComponent::get_for_read() const +const CurveEval *CurveComponentLegacy::get_for_read() const { return curve_; } -CurveEval *CurveComponent::get_for_write() +CurveEval *CurveComponentLegacy::get_for_write() { BLI_assert(this->is_mutable()); if (ownership_ == GeometryOwnershipType::ReadOnly) { @@ -96,17 +89,17 @@ CurveEval *CurveComponent::get_for_write() return curve_; } -bool CurveComponent::is_empty() const +bool CurveComponentLegacy::is_empty() const { return curve_ == nullptr; } -bool CurveComponent::owns_direct_data() const +bool CurveComponentLegacy::owns_direct_data() const { return ownership_ == GeometryOwnershipType::Owned; } -void CurveComponent::ensure_owns_direct_data() +void CurveComponentLegacy::ensure_owns_direct_data() { BLI_assert(this->is_mutable()); if (ownership_ != GeometryOwnershipType::Owned) { @@ -115,32 +108,13 @@ void CurveComponent::ensure_owns_direct_data() } } -const Curve *CurveComponent::get_curve_for_render() const -{ - if (curve_ == nullptr) { - return nullptr; - } - if (curve_for_render_ != nullptr) { - return curve_for_render_; - } - std::lock_guard lock{curve_for_render_mutex_}; - if (curve_for_render_ != nullptr) { - return curve_for_render_; - } - - curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr); - curve_for_render_->curve_eval = curve_; - - return curve_for_render_; -} - /** \} */ /* -------------------------------------------------------------------- */ /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -334,9 +308,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra } // namespace blender::bke -GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, - const AttributeDomain from_domain, - const AttributeDomain to_domain) const +GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl( + const GVArray &varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { if (!varray) { return {}; @@ -361,14 +336,15 @@ GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponent &curve_component = static_cast<CurveComponent &>(component); + CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component); return curve_component.get_for_write(); } static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>( + component); return curve_component.get_for_read(); } @@ -377,98 +353,6 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen namespace blender::bke { /* -------------------------------------------------------------------- */ -/** \name Curve Normals Access - * \{ */ - -static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals) -{ - Span<int> offsets = spline.control_point_offsets(); - Span<float3> evaluated_normals = spline.evaluated_normals(); - for (const int i : IndexRange(spline.size())) { - normals[i] = evaluated_normals[offsets[i]]; - } -} - -static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals) -{ - normals.copy_from(spline.evaluated_normals()); -} - -/** - * Because NURBS control points are not necessarily on the path, the normal at the control points - * is not well defined, so create a temporary poly spline to find the normals. This requires extra - * copying currently, but may be more efficient in the future if attributes have some form of CoW. - */ -static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals) -{ - PolySpline poly_spline; - poly_spline.resize(spline.size()); - poly_spline.positions().copy_from(spline.positions()); - poly_spline.tilts().copy_from(spline.tilts()); - normals.copy_from(poly_spline.evaluated_normals()); -} - -static Array<float3> curve_normal_point_domain(const CurveEval &curve) -{ - Span<SplinePtr> splines = curve.splines(); - Array<int> offsets = curve.control_point_offsets(); - const int total_size = offsets.last(); - Array<float3> normals(total_size); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; - switch (splines[i]->type()) { - case Spline::Type::Bezier: - calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals); - break; - case Spline::Type::Poly: - calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals); - break; - case Spline::Type::NURBS: - calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals); - break; - } - } - }); - return normals; -} - -VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain) -{ - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; - } - - if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated normals if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return VArray<float3>::ForSpan(spline.evaluated_normals()); - } - - Array<float3> normals = curve_normal_point_domain(*curve); - return VArray<float3>::ForContainer(std::move(normals)); - } - - if (domain == ATTR_DOMAIN_CURVE) { - Array<float3> point_normals = curve_normal_point_domain(*curve); - VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals)); - return component.attribute_try_adapt_domain<float3>( - std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); - } - - return nullptr; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Builtin Spline Attributes * * Attributes with a value for every spline, stored contiguously or in every spline separately. @@ -877,15 +761,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); Spline &spline = *splines_[indices.spline_index]; - if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { - const float3 delta = value - bezier_spline->positions()[indices.point_index]; - bezier_spline->handle_positions_left()[indices.point_index] += delta; - bezier_spline->handle_positions_right()[indices.point_index] += delta; - bezier_spline->positions()[indices.point_index] = value; - } - else { - spline.positions()[indices.point_index] = value; - } + spline.positions()[indices.point_index] = value; } void set_all(Span<float3> src) final @@ -894,20 +770,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> { Spline &spline = *splines_[spline_index]; const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; - if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { - MutableSpan<float3> positions = bezier_spline->positions(); - MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left(); - MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right(); - for (const int i : IndexRange(next_offset - offset)) { - const float3 delta = src[offset + i] - positions[i]; - handle_positions_left[i] += delta; - handle_positions_right[i] += delta; - positions[i] = src[offset + i]; - } - } - else { - spline.positions().copy_from(src.slice(offset, next_offset - offset)); - } + spline.positions().copy_from(src.slice(offset, next_offset - offset)); } } @@ -955,7 +818,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); const Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] : bezier_spline.handle_positions_left()[indices.point_index]; @@ -967,13 +830,13 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); if (is_right_) { - bezier_spline.set_handle_position_right(indices.point_index, value); + bezier_spline.handle_positions_right()[indices.point_index] = value; } else { - bezier_spline.set_handle_position_left(indices.point_index, value); + bezier_spline.handle_positions_left()[indices.point_index] = value; } bezier_spline.mark_cache_invalid(); } @@ -983,18 +846,18 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { for (const int spline_index : splines_.index_range()) { Spline &spline = *splines_[spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const int offset = offsets_[spline_index]; BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); if (is_right_) { for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.set_handle_position_right(i, src[offset + i]); + bezier_spline.handle_positions_right()[i] = src[offset + i]; } } else { for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.set_handle_position_left(i, src[offset + i]); + bezier_spline.handle_positions_left()[i] = src[offset + i]; } } bezier_spline.mark_cache_invalid(); @@ -1024,7 +887,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { Array<Span<float3>> spans(splines.size()); for (const int i : spans.index_range()) { - if (splines[i]->type() == Spline::Type::Bezier) { + if (splines[i]->type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]); spans[i] = is_right ? bezier_spline.handle_positions_right() : bezier_spline.handle_positions_left(); @@ -1214,7 +1077,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo /* Use the regular position virtual array when there aren't any Bezier splines * to avoid the overhead of checking the spline type for every point. */ - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); } @@ -1255,7 +1118,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return {}; } @@ -1273,7 +1136,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return {}; } @@ -1304,7 +1167,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return false; } - return curve->has_spline_with_type(Spline::Type::Bezier) && + return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; } }; @@ -1324,7 +1187,8 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { private: static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL; + CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | + CD_MASK_PROP_INT8; public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, @@ -1569,7 +1433,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers() + const { static blender::bke::ComponentAttributeProviders providers = blender::bke::create_attribute_providers_for_curve(); diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc new file mode 100644 index 00000000000..5723d110aa0 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "DNA_ID_enums.h" +#include "DNA_curve_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_curve.h" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" +#include "BKE_spline.hh" + +#include "attribute_access_intern.hh" + +using blender::fn::GVArray; + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component Implementation + * \{ */ + +CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +{ +} + +CurveComponent::~CurveComponent() +{ + this->clear(); +} + +GeometryComponent *CurveComponent::copy() const +{ + CurveComponent *new_component = new CurveComponent(); + if (curves_ != nullptr) { + new_component->curves_ = BKE_curves_copy_for_eval(curves_, false); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void CurveComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (curves_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + BKE_id_free(nullptr, curves_); + } + if (curve_for_render_ != nullptr) { + /* The curve created by this component should not have any edit mode data. */ + BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); + BKE_id_free(nullptr, curve_for_render_); + curve_for_render_ = nullptr; + } + + curves_ = nullptr; + } +} + +bool CurveComponent::has_curves() const +{ + return curves_ != nullptr; +} + +void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + curves_ = curves; + ownership_ = ownership; +} + +Curves *CurveComponent::release() +{ + BLI_assert(this->is_mutable()); + Curves *curves = curves_; + curves_ = nullptr; + return curves; +} + +const Curves *CurveComponent::get_for_read() const +{ + return curves_; +} + +Curves *CurveComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + curves_ = BKE_curves_copy_for_eval(curves_, false); + ownership_ = GeometryOwnershipType::Owned; + } + return curves_; +} + +bool CurveComponent::is_empty() const +{ + return curves_ == nullptr; +} + +bool CurveComponent::owns_direct_data() const +{ + return ownership_ == GeometryOwnershipType::Owned; +} + +void CurveComponent::ensure_owns_direct_data() +{ + BLI_assert(this->is_mutable()); + if (ownership_ != GeometryOwnershipType::Owned) { + curves_ = BKE_curves_copy_for_eval(curves_, false); + ownership_ = GeometryOwnershipType::Owned; + } +} + +const Curve *CurveComponent::get_curve_for_render() const +{ + if (curves_ == nullptr) { + return nullptr; + } + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + std::lock_guard lock{curve_for_render_mutex_}; + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + + curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr); + curve_for_render_->curve_eval = curves_to_curve_eval(*curves_).release(); + + return curve_for_render_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Normals Access + * \{ */ + +namespace blender::bke { + +static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals) +{ + Span<int> offsets = spline.control_point_offsets(); + Span<float3> evaluated_normals = spline.evaluated_normals(); + for (const int i : IndexRange(spline.size())) { + normals[i] = evaluated_normals[offsets[i]]; + } +} + +static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals) +{ + normals.copy_from(spline.evaluated_normals()); +} + +/** + * Because NURBS control points are not necessarily on the path, the normal at the control points + * is not well defined, so create a temporary poly spline to find the normals. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + poly_spline.tilts().copy_from(spline.tilts()); + normals.copy_from(poly_spline.evaluated_normals()); +} + +static Array<float3> curve_normal_point_domain(const CurveEval &curve) +{ + Span<SplinePtr> splines = curve.splines(); + Array<int> offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array<float3> normals(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case CURVE_TYPE_BEZIER: + calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals); + break; + case CURVE_TYPE_POLY: + calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals); + break; + case CURVE_TYPE_NURBS: + calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals); + break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + break; + } + } + }); + return normals; +} + +VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain) +{ + if (component.is_empty()) { + return nullptr; + } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); + + if (domain == ATTR_DOMAIN_POINT) { + Array<float3> normals = curve_normal_point_domain(*curve); + return VArray<float3>::ForContainer(std::move(normals)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array<float3> point_normals = curve_normal_point_domain(*curve); + VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals)); + return component.attribute_try_adapt_domain<float3>( + std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + } + + return nullptr; +} + +} // namespace blender::bke + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attribute Access Helper Functions + * \{ */ + +int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (curves_ == nullptr) { + return 0; + } + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves_->geometry); + if (domain == ATTR_DOMAIN_POINT) { + return geometry.points_size(); + } + if (domain == ATTR_DOMAIN_CURVE) { + return geometry.curves_size(); + } + return 0; +} + +GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const +{ + return blender::bke::CurvesGeometry::wrap(curves_->geometry) + .adapt_domain(varray, from_domain, to_domain); +} + +static Curves *get_curves_from_component_for_write(GeometryComponent &component) +{ + BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); + CurveComponent &curve_component = static_cast<CurveComponent &>(component); + return curve_component.get_for_write(); +} + +static const Curves *get_curves_from_component_for_read(const GeometryComponent &component) +{ + BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return curve_component.get_for_read(); +} + +static void tag_component_topology_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); + } +} + +static void tag_component_positions_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed(); + } +} + +static void tag_component_normals_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed(); + } +} + +/** \} */ + +namespace blender::bke { + +/* -------------------------------------------------------------------- */ +/** \name Attribute Provider Declaration + * \{ */ + +/** + * In this function all the attribute providers for a curves component are created. + * Most data in this function is statically allocated, because it does not change over time. + */ +static ComponentAttributeProviders create_attribute_providers_for_curve() +{ + static CustomDataAccessInfo curve_access = { + [](GeometryComponent &component) -> CustomData * { + Curves *curves = get_curves_from_component_for_write(component); + return curves ? &curves->geometry.curve_data : nullptr; + }, + [](const GeometryComponent &component) -> const CustomData * { + const Curves *curves = get_curves_from_component_for_read(component); + return curves ? &curves->geometry.curve_data : nullptr; + }, + [](GeometryComponent &component) { + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); + } + }}; + static CustomDataAccessInfo point_access = { + [](GeometryComponent &component) -> CustomData * { + Curves *curves = get_curves_from_component_for_write(component); + return curves ? &curves->geometry.point_data : nullptr; + }, + [](const GeometryComponent &component) -> const CustomData * { + const Curves *curves = get_curves_from_component_for_read(component); + return curves ? &curves->geometry.point_data : nullptr; + }, + [](GeometryComponent &component) { + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); + } + }}; + + static BuiltinCustomDataLayerProvider position("position", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider radius("radius", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + nullptr); + + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + nullptr); + + static BuiltinCustomDataLayerProvider tilt("tilt", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + tag_component_normals_changed); + + static BuiltinCustomDataLayerProvider handle_right("handle_right", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider handle_left("handle_left", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right", + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left", + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order", + ATTR_DOMAIN_CURVE, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode", + ATTR_DOMAIN_CURVE, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider resolution("resolution", + ATTR_DOMAIN_CURVE, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider cyclic("cyclic", + ATTR_DOMAIN_CURVE, + CD_PROP_BOOL, + CD_PROP_BOOL, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<bool>, + make_array_write_attribute<bool>, + tag_component_topology_changed); + + static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access); + static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); + + return ComponentAttributeProviders({&position, + &radius, + &id, + &tilt, + &handle_right, + &handle_left, + &handle_type_right, + &handle_type_left, + &nurbs_order, + &nurbs_weight, + &resolution, + &cyclic}, + {&curve_custom_data, &point_custom_data}); +} + +/** \} */ + +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_curve(); + return &providers; +} diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index b83a8b1ee94..0cb2b0e812b 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -439,18 +439,6 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider } }; -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 2f8ff944420..104166df913 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -854,18 +854,6 @@ static GVMutableArray make_derived_write_attribute(void *data, const int domain_ MutableSpan<StructT>((StructT *)data, domain_size)); } -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - static float3 get_vertex_position(const MVert &vert) { return float3(vert.co); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index f6f3c4e1b4e..3db4db307a3 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -117,18 +117,6 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con namespace blender::bke { -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - /** * In this function all the attribute providers for a point cloud component are created. Most data * in this function is statically allocated, because it does not change over time. diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 9c7cfa04e0b..ca372ba8f38 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -7,6 +7,7 @@ #include "BKE_attribute.h" #include "BKE_attribute_access.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" @@ -186,8 +187,9 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma if (volume != nullptr) { have_minmax |= BKE_volume_min_max(volume, *r_min, *r_max); } - const CurveEval *curve = this->get_curve_for_read(); - if (curve != nullptr) { + const Curves *curves = this->get_curves_for_read(); + if (curves != nullptr) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */ have_minmax |= curve->bounds_min_max(*r_min, *r_max, true); } @@ -258,7 +260,7 @@ const Volume *GeometrySet::get_volume_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } -const CurveEval *GeometrySet::get_curve_for_read() const +const Curves *GeometrySet::get_curves_for_read() const { const CurveComponent *component = this->get_component_for_read<CurveComponent>(); return (component == nullptr) ? nullptr : component->get_for_read(); @@ -282,10 +284,10 @@ bool GeometrySet::has_volume() const return component != nullptr && component->has_volume(); } -bool GeometrySet::has_curve() const +bool GeometrySet::has_curves() const { const CurveComponent *component = this->get_component_for_read<CurveComponent>(); - return component != nullptr && component->has_curve(); + return component != nullptr && component->has_curves(); } bool GeometrySet::has_realized_data() const @@ -302,8 +304,8 @@ bool GeometrySet::has_realized_data() const bool GeometrySet::is_empty() const { - return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || this->has_volume() || - this->has_instances()); + return !(this->has_mesh() || this->has_curves() || this->has_pointcloud() || + this->has_volume() || this->has_instances()); } GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) @@ -327,12 +329,12 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, return geometry_set; } -GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) +GeometrySet GeometrySet::create_with_curves(Curves *curves, GeometryOwnershipType ownership) { GeometrySet geometry_set; - if (curve != nullptr) { + if (curves != nullptr) { CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - component.replace(curve, ownership); + component.replace(curves, ownership); } return geometry_set; } @@ -351,18 +353,18 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) component.replace(mesh, ownership); } -void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) +void GeometrySet::replace_curve(Curves *curves, GeometryOwnershipType ownership) { - if (curve == nullptr) { + if (curves == nullptr) { this->remove<CurveComponent>(); return; } - if (curve == this->get_curve_for_read()) { + if (curves == this->get_curves_for_read()) { return; } this->remove<CurveComponent>(); CurveComponent &component = this->get_component_for_write<CurveComponent>(); - component.replace(curve, ownership); + component.replace(curves, ownership); } void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) @@ -411,7 +413,7 @@ Volume *GeometrySet::get_volume_for_write() return component == nullptr ? nullptr : component->get_for_write(); } -CurveEval *GeometrySet::get_curve_for_write() +Curves *GeometrySet::get_curves_for_write() { CurveComponent *component = this->get_component_ptr<CurveComponent>(); return component == nullptr ? nullptr : component->get_for_write(); @@ -632,7 +634,7 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) is_instance = ob->type != OB_VOLUME; break; case GEO_COMPONENT_TYPE_CURVE: - is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT); + is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT); break; } if (is_instance) { diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 5ce45f6df5a..ee6b77e6463 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -443,7 +443,7 @@ static void gpencil_convert_spline(Main *bmain, } if (sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } /* Recalc fill geometry. */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 365171b300f..865bcebee25 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -202,8 +202,8 @@ static int stroke_march_next_point(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -211,15 +211,33 @@ static int stroke_march_next_point(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (pt->flag & GP_SPOINT_TEMP_TAG) { + pt = &gps->points[next_point_index]; + copy_v3_v3(result, &pt->x); + *pressure = gps->points[next_point_index].pressure; + *strength = gps->points[next_point_index].strength; + memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); + + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + *ratio_result = 1.0f; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -232,35 +250,37 @@ static int stroke_march_next_point(const bGPDstroke *gps, *strength = gps->points[next_point_index].strength; memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); - *index_from = next_point_index - 1; + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); *index_to = next_point_index; *ratio_result = 1.0f; return 0; } + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); + *ratio_result = ratio; + *pressure = interpf( - gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio); + gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio); *strength = interpf( - gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio); + gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio); interp_v4_v4v4(vert_color, - gps->points[next_point_index - 1].vert_color, + gps->points[*index_from].vert_color, gps->points[next_point_index].vert_color, ratio); - *index_from = next_point_index - 1; - *index_to = next_point_index; - *ratio_result = ratio; - - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } static int stroke_march_next_point_no_interp(const bGPDstroke *gps, const int index_next_pt, const float *current, const float dist, + const float sharp_threshold, float *result) { float remaining_till_next = 0.0f; @@ -270,8 +290,8 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -279,15 +299,29 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (next_point_index < gps->totpoints - 1 && + angle_v3v3v3(&gps->points[next_point_index - 1].x, + &gps->points[next_point_index].x, + &gps->points[next_point_index + 1].x) < sharp_threshold) { + copy_v3_v3(result, &pt->x); + pt->flag |= GP_SPOINT_TEMP_TAG; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -296,15 +330,16 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, if (remaining_till_next < remaining_march) { pt = &gps->points[next_point_index]; copy_v3_v3(result, &pt->x); + /* Stroke marching only terminates here. */ return 0; } float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } -static int stroke_march_count(const bGPDstroke *gps, const float dist) +static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold) { int point_count = 0; float point[3]; @@ -315,8 +350,13 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist) copy_v3_v3(point, &pt->x); point_count++; + /* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */ + for (int i = 0; i < gps->totpoints; i++) { + gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG); + } + while ((next_point_index = stroke_march_next_point_no_interp( - gps, next_point_index, point, dist, point)) > -1) { + gps, next_point_index, point, dist, sharp_threshold, point)) > -1) { point_count++; if (next_point_index == 0) { break; /* last point finished */ @@ -394,7 +434,11 @@ static void stroke_interpolate_deform_weights( } } -bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select) +bool BKE_gpencil_stroke_sample(bGPdata *gpd, + bGPDstroke *gps, + const float dist, + const bool select, + const float sharp_threshold) { bGPDspoint *pt = gps->points; bGPDspoint *pt1 = nullptr; @@ -406,7 +450,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return false; } /* TODO: Implement feature point preservation. */ - int count = stroke_march_count(gps, dist); + int count = stroke_march_count(gps, dist, sharp_threshold); bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); @@ -491,6 +535,8 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, gps->totpoints = i; + gps->flag &= (~GP_STROKE_CYCLIC); + /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -2052,27 +2098,30 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int MDeformVert *dvert_final = nullptr; MDeformVert *dvert_next = nullptr; int totnewpoints, oldtotpoints; - int i2; + + bool cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; for (int s = 0; s < level; s++) { - totnewpoints = gps->totpoints - 1; + totnewpoints = gps->totpoints; + if (!cyclic) { + totnewpoints--; + } /* duplicate points in a temp area */ - temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); + temp_points = gps->points; oldtotpoints = gps->totpoints; /* resize the points arrays */ gps->totpoints += totnewpoints; - gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->points = (bGPDspoint *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->points), __func__); if (gps->dvert != nullptr) { - temp_dverts = (MDeformVert *)MEM_dupallocN(gps->dvert); - gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + temp_dverts = gps->dvert; + gps->dvert = (MDeformVert *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->dvert), __func__); } /* move points from last to first to new place */ - i2 = gps->totpoints - 1; - for (int i = oldtotpoints - 1; i > 0; i--) { + for (int i = 0; i < oldtotpoints; i++) { bGPDspoint *pt = &temp_points[i]; - bGPDspoint *pt_final = &gps->points[i2]; + bGPDspoint *pt_final = &gps->points[i * 2]; copy_v3_v3(&pt_final->x, &pt->x); pt_final->pressure = pt->pressure; @@ -2085,18 +2134,16 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int if (gps->dvert != nullptr) { dvert = &temp_dverts[i]; - dvert_final = &gps->dvert[i2]; + dvert_final = &gps->dvert[i * 2]; dvert_final->totweight = dvert->totweight; dvert_final->dw = dvert->dw; } - i2 -= 2; } /* interpolate mid points */ - i2 = 1; - for (int i = 0; i < oldtotpoints - 1; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i2]; + for (int i = cyclic ? 0 : 1, j = cyclic ? oldtotpoints - 1 : 0; i < oldtotpoints; j = i, i++) { + bGPDspoint *pt = &temp_points[j]; + bGPDspoint *next = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[j * 2 + 1]; /* add a half way point */ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); @@ -2109,9 +2156,9 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); if (gps->dvert != nullptr) { - dvert = &temp_dverts[i]; - dvert_next = &temp_dverts[i + 1]; - dvert_final = &gps->dvert[i2]; + dvert = &temp_dverts[j]; + dvert_next = &temp_dverts[i]; + dvert_final = &gps->dvert[j * 2 + 1]; dvert_final->totweight = dvert->totweight; dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw); @@ -2126,8 +2173,6 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int } } } - - i2 += 2; } MEM_SAFE_FREE(temp_points); @@ -2135,20 +2180,18 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int /* Move points to smooth stroke (not simple type). */ if (type != GP_SUBDIV_SIMPLE) { - /* duplicate points in a temp area with the new subdivide data */ - temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); - + float mid[3]; /* extreme points are not changed */ - for (int i = 0; i < gps->totpoints - 2; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i + 1]; + for (int i = cyclic ? 0 : 2, j = cyclic ? gps->totpoints - 2 : 0; i < gps->totpoints - 2; + j = i, i += 2) { + bGPDspoint *prev = &gps->points[j + 1]; + bGPDspoint *pt = &gps->points[i]; + bGPDspoint *next = &gps->points[i + 1]; /* move point */ - interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + interp_v3_v3v3(mid, &prev->x, &next->x, 0.5f); + interp_v3_v3v3(&pt->x, mid, &pt->x, 0.5f); } - /* free temp memory */ - MEM_SAFE_FREE(temp_points); } } diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index 2551bb12511..5b9dfa55c45 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -59,7 +59,7 @@ static void id_type_init(void) INIT_TYPE(ID_LI); INIT_TYPE(ID_OB); INIT_TYPE(ID_ME); - INIT_TYPE(ID_CU); + INIT_TYPE(ID_CU_LEGACY); INIT_TYPE(ID_MB); INIT_TYPE(ID_MA); INIT_TYPE(ID_TE); @@ -215,7 +215,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(BR); CASE_IDFILTER(CA); CASE_IDFILTER(CF); - CASE_IDFILTER(CU); + CASE_IDFILTER(CU_LEGACY); CASE_IDFILTER(GD); CASE_IDFILTER(GR); CASE_IDFILTER(CV); @@ -264,7 +264,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(BR); CASE_IDFILTER(CA); CASE_IDFILTER(CF); - CASE_IDFILTER(CU); + CASE_IDFILTER(CU_LEGACY); CASE_IDFILTER(GD); CASE_IDFILTER(GR); CASE_IDFILTER(CV); @@ -312,7 +312,7 @@ int BKE_idtype_idcode_to_index(const short idcode) CASE_IDINDEX(BR); CASE_IDINDEX(CA); CASE_IDINDEX(CF); - CASE_IDINDEX(CU); + CASE_IDINDEX(CU_LEGACY); CASE_IDINDEX(GD); CASE_IDINDEX(GR); CASE_IDINDEX(CV); @@ -371,7 +371,7 @@ short BKE_idtype_idcode_from_index(const int index) CASE_IDCODE(BR); CASE_IDCODE(CA); CASE_IDCODE(CF); - CASE_IDCODE(CU); + CASE_IDCODE(CU_LEGACY); CASE_IDCODE(GD); CASE_IDCODE(GR); CASE_IDCODE(CV); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index fa63f99d3f1..8a212ed0d7d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -3569,6 +3569,7 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c /* Must copy image user changes to CoW data-block. */ DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); } + BKE_image_partial_update_mark_full_update(ima); } } @@ -3609,6 +3610,7 @@ static void image_free_tile(Image *ima, ImageTile *tile) } } } + BKE_image_partial_update_mark_full_update(ima); if (BKE_image_is_multiview(ima)) { const int totviews = BLI_listbase_count(&ima->views); @@ -3949,6 +3951,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } } } + BKE_image_partial_update_mark_full_update(ima); return tile; } @@ -4014,6 +4017,7 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } } } + BKE_image_partial_update_mark_full_update(ima); } static int tile_sort_cb(const void *a, const void *b) diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 5675641deb4..c4a43d8b023 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -322,19 +322,25 @@ static void image_gpu_texture_partial_update_changes_available( Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) { while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { - const int tile_offset_x = changes.changed_region.region.xmin; - const int tile_offset_y = changes.changed_region.region.ymin; - const int tile_width = min_ii(changes.tile_data.tile_buffer->x, - BLI_rcti_size_x(&changes.changed_region.region)); - const int tile_height = min_ii(changes.tile_data.tile_buffer->y, - BLI_rcti_size_y(&changes.changed_region.region)); + /* Calculate the clipping region with the tile buffer. + * TODO(jbakker): should become part of ImageTileData to deduplicate with image engine. */ + rcti buffer_rect; + BLI_rcti_init( + &buffer_rect, 0, changes.tile_data.tile_buffer->x, 0, changes.tile_data.tile_buffer->y); + rcti clipped_update_region; + const bool has_overlap = BLI_rcti_isect( + &buffer_rect, &changes.changed_region.region, &clipped_update_region); + if (!has_overlap) { + continue; + } + image_update_gputexture_ex(image, changes.tile_data.tile, changes.tile_data.tile_buffer, - tile_offset_x, - tile_offset_y, - tile_width, - tile_height); + clipped_update_region.xmin, + clipped_update_region.ymin, + BLI_rcti_size_x(&clipped_update_region), + BLI_rcti_size_y(&clipped_update_region)); } } @@ -431,7 +437,8 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (ibuf_intern == nullptr) { ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr); if (ibuf_intern == nullptr) { - return image_gpu_texture_error_create(textarget); + *tex = image_gpu_texture_error_create(textarget); + return *tex; } } diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc index 9d5635f49ab..4606a14ab69 100644 --- a/source/blender/blenkernel/intern/image_partial_update.cc +++ b/source/blender/blenkernel/intern/image_partial_update.cc @@ -198,8 +198,8 @@ struct TileChangeset { tile_width = image_buffer->x; tile_height = image_buffer->y; - int chunk_x_len = tile_width / CHUNK_SIZE; - int chunk_y_len = tile_height / CHUNK_SIZE; + int chunk_x_len = (tile_width + CHUNK_SIZE - 1) / CHUNK_SIZE; + int chunk_y_len = (tile_height + CHUNK_SIZE - 1) / CHUNK_SIZE; init_chunks(chunk_x_len, chunk_y_len); return true; } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index e5c1cf96f8c..abd6505456e 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -1090,7 +1090,7 @@ static char *get_rna_access(ID *id, propname = particle_adrcodes_to_paths(adrcode, &dummy_index); break; - case ID_CU: /* curve */ + case ID_CU_LEGACY: /* curve */ /* this used to be a 'dummy' curve which got evaluated on the fly... * now we've got real var for this! */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index b28d9db92cf..e28094c0abc 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -281,7 +281,7 @@ Key *BKE_key_add(Main *bmain, ID *id) /* common function */ key->elemsize = sizeof(float[KEYELEM_FLOAT_LEN_COORD]); break; - case ID_CU: + case ID_CU_LEGACY: el = key->elemstr; el[0] = KEYELEM_ELEM_SIZE_CURVE; @@ -659,7 +659,7 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int *ofs = sizeof(float[KEYELEM_FLOAT_LEN_COORD]); *poinsize = *ofs; break; - case ID_CU: + case ID_CU_LEGACY: if (mode == KEY_MODE_BPOINT) { *ofs = sizeof(float[KEYELEM_FLOAT_LEN_BPOINT]); *step = KEYELEM_ELEM_LEN_BPOINT; @@ -1524,7 +1524,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t tot = lt->pntsu * lt->pntsv * lt->pntsw; size = tot * sizeof(float[KEYELEM_FLOAT_LEN_COORD]); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; tot = BKE_keyblock_curve_element_count(&cu->nurb); @@ -1570,7 +1570,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t MEM_freeN(weights); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { cp_cu_key(ob->data, key, actkb, kb, 0, tot, out, tot); } } @@ -1582,7 +1582,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t else if (ob->type == OB_LATTICE) { do_latt_key(ob, key, out, tot); } - else if (ob->type == OB_CURVE) { + else if (ob->type == OB_CURVES_LEGACY) { do_curve_key(ob, key, out, tot); } else if (ob->type == OB_SURF) { @@ -1714,7 +1714,7 @@ bool BKE_key_idtype_support(const short id_type) { switch (id_type) { case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: return true; default: @@ -1729,7 +1729,7 @@ Key **BKE_key_from_id_p(ID *id) Mesh *me = (Mesh *)id; return &me->key; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; if (cu->vfont == NULL) { return &cu->key; @@ -2269,7 +2269,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve Lattice *lt = ob->data; BLI_assert((lt->pntsu * lt->pntsv * lt->pntsw) == kb->totelem); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; BLI_assert(BKE_keyblock_curve_element_count(&cu->nurb) == kb->totelem); } @@ -2293,7 +2293,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve copy_v3_v3(fp, *co); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; @@ -2335,7 +2335,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v tot = lt->pntsu * lt->pntsv * lt->pntsw; elemsize = lt->key->elemsize; } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; elemsize = cu->key->elemsize; tot = BKE_keyblock_curve_element_count(&cu->nurb); @@ -2366,7 +2366,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3] Lattice *lt = (Lattice *)ob->data; tot = lt->pntsu * lt->pntsv * lt->pntsw; } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; tot = BKE_nurbList_verts_count(&cu->nurb); } @@ -2383,7 +2383,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3] copy_v3_v3(*co, fp); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; @@ -2422,7 +2422,7 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, const float (*ofs add_v3_v3(fp, *ofs); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c index 057309d0896..0903c2a2cac 100644 --- a/source/blender/blenkernel/intern/layer_utils.c +++ b/source/blender/blenkernel/intern/layer_utils.c @@ -67,9 +67,11 @@ Object **BKE_view_layer_array_selected_objects_params( } FOREACH_SELECTED_OBJECT_END; - object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array)); - /* We always need a valid allocation (prevent crash on free). */ - if (object_array == NULL) { + if (object_array != NULL) { + BLI_array_trim(object_array); + } + else { + /* We always need a valid allocation (prevent crash on free). */ object_array = MEM_mallocN(0, __func__); } *r_len = BLI_array_len(object_array); @@ -121,9 +123,11 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer, } FOREACH_BASE_IN_MODE_END; - base_array = MEM_reallocN(base_array, sizeof(*base_array) * BLI_array_len(base_array)); /* We always need a valid allocation (prevent crash on free). */ - if (base_array == NULL) { + if (base_array != NULL) { + BLI_array_trim(base_array); + } + else { base_array = MEM_mallocN(0, __func__); } *r_len = BLI_array_len(base_array); diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index cf25af1c637..ba5556c8b2d 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -234,7 +234,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) { BLI_remlink(lb, id); BLI_addtail(&tagged_deleted_ids, id); /* Do not tag as no_main now, we want to unlink it first (lower-level ID management @@ -290,7 +290,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) { id->tag |= tag; BKE_id_remapper_add(remapper, id, NULL); } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 02cdd6fcd20..922c1beda38 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -72,12 +72,19 @@ static void lib_override_library_property_operation_clear( IDOverrideLibraryPropertyOperation *opop); /** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */ -BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id) +BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id) { + if (r_owner_id != NULL) { + *r_owner_id = id; + } if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) { const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->owner_get != NULL) { - return id_type->owner_get(bmain, id)->override_library; + ID *owner_id = id_type->owner_get(bmain, id); + if (r_owner_id != NULL) { + *r_owner_id = owner_id; + } + return owner_id->override_library; } BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter"); } @@ -146,6 +153,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f id_us_plus(dst_id->override_library->reference); dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root; + dst_id->override_library->flag = src_id->override_library->flag; if (do_full_copy) { BLI_duplicatelist(&dst_id->override_library->properties, @@ -313,17 +321,36 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, return local_id; } +/* TODO: Make this static local function instead? API is becoming complex, and it's not used + * outside of this file anyway. */ bool BKE_lib_override_library_create_from_tag(Main *bmain, Library *owner_library, const ID *id_root_reference, ID *id_hierarchy_root, + const ID *id_hierarchy_root_reference, const bool do_no_main) { - BLI_assert(id_root_reference != NULL); - BLI_assert(id_hierarchy_root != NULL || (id_root_reference->tag & LIB_TAG_DOIT) != 0); - BLI_assert(id_hierarchy_root == NULL || - (ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) && - id_hierarchy_root->override_library->reference == id_root_reference)); + BLI_assert(id_root_reference != NULL && ID_IS_LINKED(id_root_reference)); + /* If we do not have any hierarchy root given, then the root reference must be tagged for + * override. */ + BLI_assert(id_hierarchy_root != NULL || id_hierarchy_root_reference != NULL || + (id_root_reference->tag & LIB_TAG_DOIT) != 0); + /* At least one of the hierarchy root pointers must be NULL, passing both is useless and can + * create confusion. */ + BLI_assert(ELEM(NULL, id_hierarchy_root, id_hierarchy_root_reference)); + + if (id_hierarchy_root != NULL) { + /* If the hierarchy root is given, it must be a valid existing override (used during partial + * resync process mainly). */ + BLI_assert((ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) && + id_hierarchy_root->override_library->reference->lib == id_root_reference->lib)); + } + if (!ELEM(id_hierarchy_root_reference, NULL, id_root_reference)) { + /* If the reference hierarchy root is given, it must be from the same library as the reference + * root, and also tagged for override. */ + BLI_assert((id_hierarchy_root_reference->lib == id_root_reference->lib && + (id_hierarchy_root_reference->tag & LIB_TAG_DOIT) != 0)); + } const Library *reference_library = id_root_reference->lib; @@ -379,7 +406,12 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole * existing linked IDs usages. */ if (success) { - if (id_root_reference->newid != NULL) { + if (id_hierarchy_root_reference != NULL) { + id_hierarchy_root = id_hierarchy_root_reference->newid; + } + else if (id_root_reference->newid != NULL && + (id_hierarchy_root == NULL || + id_hierarchy_root->override_library->reference == id_root_reference)) { id_hierarchy_root = id_root_reference->newid; } BLI_assert(id_hierarchy_root != NULL); @@ -832,8 +864,8 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * continue; } - Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; - ID *to_id_reference = lib_override_get(bmain, to_id)->reference; + Library *reference_lib = lib_override_get(bmain, id_owner, NULL)->reference->lib; + ID *to_id_reference = lib_override_get(bmain, to_id, NULL)->reference; if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; @@ -879,12 +911,13 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) static bool lib_override_library_create_do(Main *bmain, Scene *scene, Library *owner_library, - ID *id_root) + ID *id_root_reference, + ID *id_hierarchy_root_reference) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, .scene = scene, - .id_root = id_root, + .id_root = id_root_reference, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = false, @@ -898,8 +931,18 @@ static bool lib_override_library_create_do(Main *bmain, BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); - const bool success = BKE_lib_override_library_create_from_tag( - bmain, owner_library, id_root, NULL, false); + bool success = false; + if (id_hierarchy_root_reference->lib != id_root_reference->lib) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib); + success = BKE_lib_override_library_create_from_tag( + bmain, owner_library, id_root_reference, id_hierarchy_root_reference, NULL, false); + } + else { + success = BKE_lib_override_library_create_from_tag( + bmain, owner_library, id_root_reference, NULL, id_hierarchy_root_reference, false); + } return success; } @@ -909,7 +952,7 @@ static void lib_override_library_create_post_process(Main *bmain, ViewLayer *view_layer, const Library *owner_library, ID *id_root, - ID *id_reference, + ID *id_instance_hint, Collection *residual_storage, const bool is_resync) { @@ -933,8 +976,8 @@ static void lib_override_library_create_post_process(Main *bmain, (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) { switch (GS(id_root->name)) { case ID_GR: { - Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? - (Object *)id_reference : + Object *ob_reference = id_instance_hint != NULL && GS(id_instance_hint->name) == ID_OB ? + (Object *)id_instance_hint : NULL; Collection *collection_new = ((Collection *)id_root->newid); if (is_resync && BKE_collection_is_in_scene(collection_new)) { @@ -943,10 +986,10 @@ static void lib_override_library_create_post_process(Main *bmain, if (ob_reference != NULL) { BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); } - else if (id_reference != NULL) { - BLI_assert(GS(id_reference->name) == ID_GR); + else if (id_instance_hint != NULL) { + BLI_assert(GS(id_instance_hint->name) == ID_GR); BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_reference), collection_new); + bmain, scene, ((Collection *)id_instance_hint), collection_new); } else { BKE_collection_add_from_collection( @@ -1039,26 +1082,32 @@ bool BKE_lib_override_library_create(Main *bmain, Scene *scene, ViewLayer *view_layer, Library *owner_library, - ID *id_root, - ID *id_reference, + ID *id_root_reference, + ID *id_hierarchy_root_reference, + ID *id_instance_hint, ID **r_id_root_override) { if (r_id_root_override != NULL) { *r_id_root_override = NULL; } - const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root); + if (id_hierarchy_root_reference == NULL) { + id_hierarchy_root_reference = id_root_reference; + } + + const bool success = lib_override_library_create_do( + bmain, scene, owner_library, id_root_reference, id_hierarchy_root_reference); if (!success) { return success; } if (r_id_root_override != NULL) { - *r_id_root_override = id_root->newid; + *r_id_root_override = id_root_reference->newid; } lib_override_library_create_post_process( - bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false); + bmain, scene, view_layer, owner_library, id_root_reference, id_instance_hint, NULL, false); /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -1102,11 +1151,18 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); - if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - /* This ID has already been processed. */ - BLI_assert(id->override_library != NULL); - *r_best_level = curr_level; - return id->override_library->hierarchy_root; + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + /* This ID has already been processed. */ + *r_best_level = curr_level; + return id->override_library->hierarchy_root; + } + + BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); + ID *id_owner; + int best_level_placeholder = 0; + lib_override_get(bmain, id, &id_owner); + return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder); } /* This way we won't process again that ID, should we encounter it again through another * relationship hierarchy. */ @@ -1140,7 +1196,17 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int } } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(best_root_id_candidate)) { + BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); + ID *id_owner; + int best_level_placeholder = 0; + lib_override_get(bmain, best_root_id_candidate, &id_owner); + best_root_id_candidate = lib_override_root_find( + bmain, id_owner, curr_level + 1, &best_level_placeholder); + } + BLI_assert(best_root_id_candidate != NULL); + BLI_assert((best_root_id_candidate->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) == 0); *r_best_level = best_level_candidate; return best_root_id_candidate; @@ -1430,7 +1496,7 @@ static bool lib_override_library_resync(Main *bmain, /* While this should not happen in typical cases (and won't be properly supported here), * user is free to do all kind of very bad things, including having different local * overrides of a same linked ID in a same hierarchy. */ - IDOverrideLibrary *id_override_library = lib_override_get(bmain, id); + IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, NULL); ID *reference_id = id_override_library->reference; if (GS(reference_id->name) != GS(id->name)) { switch (GS(id->name)) { @@ -1502,7 +1568,7 @@ static bool lib_override_library_resync(Main *bmain, * override IDs (including within the old overrides themselves, since those are tagged too * above). */ const bool success = BKE_lib_override_library_create_from_tag( - bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, true); + bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, NULL, true); if (!success) { BLI_ghash_free(linkedref_to_old_override, NULL, NULL); @@ -2127,7 +2193,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( "ID override %s from library level %d still found as needing resync, when all " "IDs from that level should have been processed after tackling library level %d", id->name, - id->lib != NULL ? id->lib->temp_index : 0, + ID_IS_LINKED(id) ? id->lib->temp_index : 0, library_indirect_level); id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; } @@ -2136,6 +2202,10 @@ static void lib_override_library_main_resync_on_library_indirect_level( BLI_ghash_free(id_roots, NULL, MEM_freeN); + /* In some fairly rare (and degenerate) cases, some root ID from other liboverrides may have been + * freed, and therefore set to NULL. Attempt to fix this as best as possible. */ + BKE_lib_override_library_main_hierarchy_root_ensure(bmain); + if (do_reports_recursive_resync_timing) { reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time; } diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c index 809235ad24c..dc164313788 100644 --- a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -42,7 +42,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, const bool is_override_instancing_object = ob_proxy_group != NULL; ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : &ob_proxy->proxy->id; - ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + ID *id_instance_hint = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not * sure this is a valid state, but for now just abort the overriding process. */ @@ -81,7 +81,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, FOREACH_MAIN_ID_END; return BKE_lib_override_library_create( - bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL); + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL); } static void lib_override_library_proxy_convert_do(Main *bmain, diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index ba009072db8..5de8704e13b 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -402,7 +402,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) return FILTER_ID_ALL; case ID_ME: return FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM; - case ID_CU: + case ID_CU_LEGACY: return FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_VF; case ID_MB: return FILTER_ID_MA; @@ -418,7 +418,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) return FILTER_ID_OB | FILTER_ID_IM; case ID_KE: /* Warning! key->from, could be more types in future? */ - return FILTER_ID_ME | FILTER_ID_CU | FILTER_ID_LT; + return FILTER_ID_ME | FILTER_ID_CU_LEGACY | FILTER_ID_LT; case ID_SCR: return FILTER_ID_SCE; case ID_WO: @@ -448,7 +448,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) case ID_WS: return FILTER_ID_SCE; case ID_CV: - return FILTER_ID_MA; + return FILTER_ID_MA | FILTER_ID_OB; case ID_PT: return FILTER_ID_MA; case ID_VO: @@ -490,7 +490,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) /* Exception: ID_KE aren't available as filter_id. */ if (id_type_used == ID_KE) { - return ELEM(id_type_owner, ID_ME, ID_CU, ID_LT); + return ELEM(id_type_owner, ID_ME, ID_CU_LEGACY, ID_LT); } /* Exception: ID_SCR aren't available as filter_id. */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 9329a09f1b6..24e7178dd63 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -380,7 +380,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o case ID_ME: multires_force_sculpt_rebuild(ob); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_type_test(ob); break; default: @@ -573,7 +573,7 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ bmain, NULL, (Collection *)old_id, (Collection *)new_id); break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_CV: case ID_PT: diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index b25432780ed..03e03dacfbc 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -563,7 +563,7 @@ ListBase *which_libbase(Main *bmain, short type) return &(bmain->objects); case ID_ME: return &(bmain->meshes); - case ID_CU: + case ID_CU_LEGACY: return &(bmain->curves); case ID_MB: return &(bmain->metaballs); @@ -670,7 +670,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) lb[INDEX_ID_CF] = &(bmain->cachefiles); lb[INDEX_ID_ME] = &(bmain->meshes); - lb[INDEX_ID_CU] = &(bmain->curves); + lb[INDEX_ID_CU_LEGACY] = &(bmain->curves); lb[INDEX_ID_MB] = &(bmain->metaballs); lb[INDEX_ID_CV] = &(bmain->hair_curves); lb[INDEX_ID_PT] = &(bmain->pointclouds); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index c559c6fe807..7d01a92e829 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -313,7 +313,7 @@ Material ***BKE_object_material_array_p(Object *ob) Mesh *me = ob->data; return &(me->mat); } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { Curve *cu = ob->data; return &(cu->mat); } @@ -346,7 +346,7 @@ short *BKE_object_material_len_p(Object *ob) Mesh *me = ob->data; return &(me->totcol); } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { Curve *cu = ob->data; return &(cu->totcol); } @@ -381,7 +381,7 @@ Material ***BKE_id_material_array_p(ID *id) switch (GS(id->name)) { case ID_ME: return &(((Mesh *)id)->mat); - case ID_CU: + case ID_CU_LEGACY: return &(((Curve *)id)->mat); case ID_MB: return &(((MetaBall *)id)->mat); @@ -407,7 +407,7 @@ short *BKE_id_material_len_p(ID *id) switch (GS(id->name)) { case ID_ME: return &(((Mesh *)id)->totcol); - case ID_CU: + case ID_CU_LEGACY: return &(((Curve *)id)->totcol); case ID_MB: return &(((MetaBall *)id)->totcol); @@ -434,7 +434,7 @@ static void material_data_index_remove_id(ID *id, short index) case ID_ME: BKE_mesh_material_index_remove((Mesh *)id, index); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_material_index_remove((Curve *)id, index); break; case ID_MB: @@ -468,7 +468,7 @@ bool BKE_object_material_slot_used(Object *object, short actcol) switch (GS(ob_data->name)) { case ID_ME: return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1); - case ID_CU: + case ID_CU_LEGACY: return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1); case ID_MB: /* Meta-elements don't support materials at the moment. */ @@ -489,7 +489,7 @@ static void material_data_index_clear_id(ID *id) case ID_ME: BKE_mesh_material_index_clear((Mesh *)id); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_material_index_clear((Curve *)id); break; case ID_MB: @@ -1062,7 +1062,7 @@ void BKE_object_material_remap(Object *ob, const unsigned int *remap) if (ob->type == OB_MESH) { BKE_mesh_material_remap(ob->data, remap, ob->totcol); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { BKE_curve_material_remap(ob->data, remap, ob->totcol); } else if (ob->type == OB_GPENCIL) { @@ -1314,7 +1314,7 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) } /* check indices from mesh */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { material_data_index_remove_id((ID *)ob->data, actcol - 1); if (ob->runtime.curve_cache) { BKE_displist_free(&ob->runtime.curve_cache->disp); diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 6ca5babbf13..37564f9334f 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -59,6 +59,8 @@ #include "BLO_read_write.h" +using blender::float3; + static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -1106,18 +1108,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, mesh_tessface_clear_intern(me_dst, false); } - me_dst->runtime.cd_dirty_poly = me_src->runtime.cd_dirty_poly; - me_dst->runtime.cd_dirty_vert = me_src->runtime.cd_dirty_vert; - - /* Ensure that when no normal layers exist, they are marked dirty, because - * normals might not have been included in the mask of copied layers. */ - if (!CustomData_has_layer(&me_dst->vdata, CD_NORMAL)) { - me_dst->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - } - if (!CustomData_has_layer(&me_dst->pdata, CD_NORMAL)) { - me_dst->runtime.cd_dirty_poly |= CD_MASK_NORMAL; - } - /* The destination mesh should at least have valid primary CD layers, * even in cases where the source mesh does not. */ mesh_ensure_cdlayers_primary(me_dst, do_tessface); @@ -1204,6 +1194,23 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, return mesh; } +static void ensure_orig_index_layer(CustomData &data, const int size) +{ + if (CustomData_has_layer(&data, CD_ORIGINDEX)) { + return; + } + int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_DEFAULT, nullptr, size); + range_vn_i(indices, size, 0); +} + +void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) +{ + BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + ensure_orig_index_layer(mesh->vdata, mesh->totvert); + ensure_orig_index_layer(mesh->edata, mesh->totedge); + ensure_orig_index_layer(mesh->pdata, mesh->totpoly); +} + BoundBox *BKE_mesh_boundbox_get(Object *ob) { /* This is Object-level data access, @@ -1968,8 +1975,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac nullptr); BKE_mesh_assert_normals_dirty_or_calculated(mesh); - - mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL; } void BKE_mesh_calc_normals_split(Mesh *mesh) @@ -2147,6 +2152,10 @@ static void split_faces_split_new_verts(Mesh *mesh, MVert *mvert = mesh->mvert; float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh); + /* Normals were already calculated at the beginning of this operation, we rely on that to update + * them partially here. */ + BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh)); + /* Remember new_verts is a single linklist, so its items are in reversed order... */ MVert *new_mv = &mvert[mesh->totvert - 1]; for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) { @@ -2157,7 +2166,6 @@ static void split_faces_split_new_verts(Mesh *mesh, copy_v3_v3(vert_normals[i], new_verts->vnor); } } - BKE_mesh_vertex_normals_clear_dirty(mesh); } /* Perform actual split of edges. */ @@ -2228,6 +2236,10 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) /* Update pointers to a newly allocated memory. */ BKE_mesh_update_customdata_pointers(mesh, false); + /* Update normals manually to avoid recalculation after this operation. */ + mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals, + sizeof(float[3]) * mesh->totvert); + /* Perform actual split of vertices and edges. */ split_faces_split_new_verts(mesh, new_verts, num_new_verts); if (do_edges) { diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index ec66cd0d84d..eee1d3b9eec 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -292,7 +292,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, r_info->mesh_edge_offset[mi] = e; r_info->mesh_poly_offset[mi] = f; /* Get matrix that transforms a coordinate in objects[mi]'s local space - * to the target space space. */ + * to the target space. */ const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() : clean_obmat(*obmats[mi]); r_info->to_target_transform[mi] = inv_target_mat * objn_mat; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 86e5f2b3cfe..1542f7119d1 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -739,14 +739,14 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1); if (nurblist.first) { - Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVE); + Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVES_LEGACY); cu->flag |= CU_3D; cu->nurb = nurblist; id_us_min(&((Mesh *)ob->data)->id); ob->data = cu; - ob->type = OB_CURVE; + ob->type = OB_CURVES_LEGACY; BKE_object_free_derived_caches(ob); } @@ -886,7 +886,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object) { /* Clear edit mode pointers that were explicitly copied to the temporary curve. */ ID *final_object_data = static_cast<ID *>(temp_object->data); - if (GS(final_object_data->name) == ID_CU) { + if (GS(final_object_data->name) == ID_CU_LEGACY) { Curve &curve = *reinterpret_cast<Curve *>(final_object_data); curve.editfont = nullptr; curve.editnurb = nullptr; @@ -901,7 +901,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object) */ static void curve_to_mesh_eval_ensure(Object &object) { - BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU); + BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU_LEGACY); Curve &curve = *static_cast<Curve *>(object.data); /* Clear all modifiers for the bevel object. * @@ -953,11 +953,11 @@ static const Mesh *get_evaluated_mesh_from_object(const Object *object) return nullptr; } -static const CurveEval *get_evaluated_curve_from_object(const Object *object) +static const Curves *get_evaluated_curves_from_object(const Object *object) { GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; if (geometry_set_eval) { - return geometry_set_eval->get_curve_for_read(); + return geometry_set_eval->get_curves_for_read(); } return nullptr; } @@ -968,8 +968,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o if (mesh) { return BKE_mesh_copy_for_eval(mesh, false); } - const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object); - if (curve) { + const Curves *curves = get_evaluated_curves_from_object(evaluated_object); + if (curves) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); return blender::bke::curve_to_wire_mesh(*curve); } return nullptr; @@ -1110,7 +1111,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Mesh *new_mesh = nullptr; switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: new_mesh = mesh_new_from_curve_type_object(object); break; @@ -1182,7 +1183,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, Object *object, bool preserve_all_data_layers) { - BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH)); + BLI_assert(ELEM(object->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL, OB_MESH)); Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false); if (mesh == nullptr) { @@ -1222,6 +1223,9 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); + /* Anonymous attributes shouldn't exist on original data. */ + BKE_mesh_anonymous_attributes_remove(mesh_in_bmain); + /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ BKE_library_foreach_ID_link( @@ -1478,17 +1482,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, tmp.cd_flag = mesh_src->cd_flag; tmp.runtime.deformed_only = mesh_src->runtime.deformed_only; - tmp.runtime.cd_dirty_poly = mesh_src->runtime.cd_dirty_poly; - tmp.runtime.cd_dirty_vert = mesh_src->runtime.cd_dirty_vert; - - /* Ensure that when no normal layers exist, they are marked dirty, because - * normals might not have been included in the mask of copied layers. */ - if (!CustomData_has_layer(&tmp.vdata, CD_NORMAL)) { - tmp.runtime.cd_dirty_vert |= CD_MASK_NORMAL; - } - if (!CustomData_has_layer(&tmp.pdata, CD_NORMAL)) { - tmp.runtime.cd_dirty_poly |= CD_MASK_NORMAL; - } + /* Clear the normals completely, since the new vertex / polygon count might be different. */ + BKE_mesh_clear_derived_normals(&tmp); if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { KeyBlock *kb; diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 3d801b301f9..1c2a903d8c3 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -94,53 +94,72 @@ static void add_v3_v3_atomic(float r[3], const float a[3]) void BKE_mesh_normals_tag_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + mesh->runtime.vert_normals_dirty = true; + mesh->runtime.poly_normals_dirty = true; } float (*BKE_mesh_vertex_normals_for_write(Mesh *mesh))[3] { - CustomData_duplicate_referenced_layer(&mesh->vdata, CD_NORMAL, mesh->totvert); - return (float(*)[3])CustomData_add_layer( - &mesh->vdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totvert); + if (mesh->runtime.vert_normals == nullptr) { + mesh->runtime.vert_normals = (float(*)[3])MEM_malloc_arrayN( + mesh->totvert, sizeof(float[3]), __func__); + } + + BLI_assert(MEM_allocN_len(mesh->runtime.vert_normals) >= sizeof(float[3]) * mesh->totvert); + + return mesh->runtime.vert_normals; } float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3] { - CustomData_duplicate_referenced_layer(&mesh->pdata, CD_NORMAL, mesh->totpoly); - return (float(*)[3])CustomData_add_layer( - &mesh->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totpoly); + if (mesh->runtime.poly_normals == nullptr) { + mesh->runtime.poly_normals = (float(*)[3])MEM_malloc_arrayN( + mesh->totpoly, sizeof(float[3]), __func__); + } + + BLI_assert(MEM_allocN_len(mesh->runtime.poly_normals) >= sizeof(float[3]) * mesh->totpoly); + + return mesh->runtime.poly_normals; } void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; + mesh->runtime.vert_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL; + mesh->runtime.poly_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL; + return mesh->runtime.vert_normals_dirty; } bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL; + return mesh->runtime.poly_normals_dirty; +} + +void BKE_mesh_clear_derived_normals(Mesh *mesh) +{ + MEM_SAFE_FREE(mesh->runtime.vert_normals); + MEM_SAFE_FREE(mesh->runtime.poly_normals); + + mesh->runtime.vert_normals_dirty = true; + mesh->runtime.poly_normals_dirty = true; } void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh) { - if (!(mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL)) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0); + if (!mesh->runtime.vert_normals_dirty) { + BLI_assert(mesh->runtime.vert_normals || mesh->totvert == 0); } - if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0); + if (!mesh->runtime.poly_normals_dirty) { + BLI_assert(mesh->runtime.poly_normals || mesh->totpoly == 0); } } @@ -201,14 +220,13 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert, * \{ */ struct MeshCalcNormalsData_PolyAndVertex { - /** Write into vertex normals #MVert.no. */ - MVert *mvert; + const MVert *mvert; const MLoop *mloop; const MPoly *mpoly; /** Polygon normal output. */ float (*pnors)[3]; - /** Vertex normal output (may be freed, copied into #MVert.no). */ + /** Vertex normal output. */ float (*vnors)[3]; }; @@ -279,7 +297,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( { MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; - MVert *mv = &data->mvert[vidx]; + const MVert *mv = &data->mvert[vidx]; float *no = data->vnors[vidx]; if (UNLIKELY(normalize_v3(no) == 0.0f)) { @@ -288,7 +306,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( } } -static void mesh_calc_normals_poly_and_vertex(MVert *mvert, +static void mesh_calc_normals_poly_and_vertex(const MVert *mvert, const int mvert_len, const MLoop *mloop, const int UNUSED(mloop_len), @@ -301,36 +319,22 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert, BLI_parallel_range_settings_defaults(&settings); settings.min_iter_per_thread = 1024; - float(*vnors)[3] = r_vert_normals; - bool free_vnors = false; - - /* First go through and calculate normals for all the polys. */ - if (vnors == nullptr) { - vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__); - free_vnors = true; - } - else { - memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len); - } + memset(r_vert_normals, 0, sizeof(*r_vert_normals) * (size_t)mvert_len); MeshCalcNormalsData_PolyAndVertex data = {}; data.mpoly = mpoly; data.mloop = mloop; data.mvert = mvert; data.pnors = r_poly_normals; - data.vnors = vnors; + data.vnors = r_vert_normals; - /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + /* Compute poly normals, accumulating them into vertex normals. */ BLI_task_parallel_range( 0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings); - /* Normalize and validate computed vertex normals (`vnors`). */ + /* Normalize and validate computed vertex normals. */ BLI_task_parallel_range( 0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings); - - if (free_vnors) { - MEM_freeN(vnors); - } } /** \} */ @@ -342,8 +346,8 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert, const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] { if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0); - return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL); + BLI_assert(mesh->runtime.vert_normals != nullptr || mesh->totvert == 0); + return mesh->runtime.vert_normals; } if (mesh->totvert == 0) { @@ -353,9 +357,9 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; BLI_mutex_lock(normals_mutex); if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL)); + BLI_assert(mesh->runtime.vert_normals != nullptr); BLI_mutex_unlock(normals_mutex); - return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL); + return mesh->runtime.vert_normals; } float(*vert_normals)[3]; @@ -388,8 +392,8 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] { if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0); - return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + BLI_assert(mesh->runtime.poly_normals != nullptr || mesh->totpoly == 0); + return mesh->runtime.poly_normals; } if (mesh->totpoly == 0) { @@ -399,9 +403,9 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; BLI_mutex_lock(normals_mutex); if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL)); + BLI_assert(mesh->runtime.poly_normals != nullptr); BLI_mutex_unlock(normals_mutex); - return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + return mesh->runtime.poly_normals; } float(*poly_normals)[3]; diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 204441d5326..7bd52abeb0d 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -88,6 +88,11 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) runtime->bvh_cache = NULL; runtime->shrinkwrap_data = NULL; + runtime->vert_normals_dirty = true; + runtime->poly_normals_dirty = true; + runtime->vert_normals = NULL; + runtime->poly_normals = NULL; + mesh_runtime_init_mutexes(mesh); } @@ -101,6 +106,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) BKE_mesh_runtime_clear_geometry(mesh); BKE_mesh_batch_cache_free(mesh); BKE_mesh_runtime_clear_edit_data(mesh); + BKE_mesh_clear_derived_normals(mesh); } /** diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index 96b588779f8..ae52e31cb9b 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -144,8 +144,7 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, MVert *mvert, int totface, int totloop, - int totpoly, - const bool do_face_nor_copy) + int totpoly) { #define USE_TESSFACE_SPEEDUP #define USE_TESSFACE_QUADS @@ -347,18 +346,6 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); CustomData_from_bmeshpoly(fdata, ldata, totface); - if (do_face_nor_copy) { - /* If polys have a normals layer, copying that to faces can help - * avoid the need to recalculate normals later. */ - if (CustomData_has_layer(pdata, CD_NORMAL)) { - float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL); - float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); - for (mface_index = 0; mface_index < totface; mface_index++) { - copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]); - } - } - } - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: * Polygons take care of their loops ordering, hence not of their vertices ordering. * Currently, our tfaces' fourth vertex index might be 0 even for a quad. @@ -395,16 +382,13 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, void BKE_mesh_tessface_calc(Mesh *mesh) { - mesh->totface = BKE_mesh_tessface_calc_ex( - &mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->mvert, - mesh->totface, - mesh->totloop, - mesh->totpoly, - /* Calculate normals right after, don't copy from polys here. */ - false); + mesh->totface = BKE_mesh_tessface_calc_ex(&mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->mvert, + mesh->totface, + mesh->totloop, + mesh->totpoly); BKE_mesh_update_customdata_pointers(mesh, true); } diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.cc index 53e19e6d16d..fb526354305 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -5,10 +5,10 @@ * \ingroup bke */ -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "CLG_log.h" @@ -41,25 +41,25 @@ static CLG_LogRef LOG = {"bke.mesh"}; /** \name Internal functions * \{ */ -typedef union { +union EdgeUUID { uint32_t verts[2]; int64_t edval; -} EdgeUUID; +}; -typedef struct SortFace { +struct SortFace { EdgeUUID es[4]; uint index; -} SortFace; +}; /* Used to detect polys (faces) using exactly the same vertices. */ /* Used to detect loops used by no (disjoint) or more than one (intersect) polys. */ -typedef struct SortPoly { +struct SortPoly { int *verts; int numverts; int loopstart; uint index; bool invalid; /* Poly index. */ -} SortPoly; +}; static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2) { @@ -106,7 +106,8 @@ static int int64_cmp(const void *v1, const void *v2) static int search_face_cmp(const void *v1, const void *v2) { - const SortFace *sfa = v1, *sfb = v2; + const SortFace *sfa = static_cast<const SortFace *>(v1); + const SortFace *sfb = static_cast<const SortFace *>(v2); if (sfa->es[0].edval > sfb->es[0].edval) { return 1; @@ -147,8 +148,8 @@ static int int_cmp(const void *v1, const void *v2) static int search_poly_cmp(const void *v1, const void *v2) { - const SortPoly *sp1 = v1; - const SortPoly *sp2 = v2; + const SortPoly *sp1 = static_cast<const SortPoly *>(v1); + const SortPoly *sp2 = static_cast<const SortPoly *>(v2); /* Reject all invalid polys at end of list! */ if (sp1->invalid || sp2->invalid) { @@ -168,8 +169,8 @@ static int search_poly_cmp(const void *v1, const void *v2) static int search_polyloop_cmp(const void *v1, const void *v2) { - const SortPoly *sp1 = v1; - const SortPoly *sp2 = v2; + const SortPoly *sp1 = static_cast<const SortPoly *>(v1); + const SortPoly *sp2 = static_cast<const SortPoly *>(v2); /* Reject all invalid polys at end of list! */ if (sp1->invalid || sp2->invalid) { @@ -275,7 +276,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge); - BLI_assert(!(do_fixes && mesh == NULL)); + BLI_assert(!(do_fixes && mesh == nullptr)); fix_flag.as_flag = 0; free_flag.as_flag = 0; @@ -288,7 +289,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, recalc_flag.edges = do_fixes; } - const float(*vert_normals)[3] = NULL; + const float(*vert_normals)[3] = nullptr; BKE_mesh_assert_normals_dirty_or_calculated(mesh); if (!BKE_mesh_vertex_normals_are_dirty(mesh)) { vert_normals = BKE_mesh_vertex_normals_ensure(mesh); @@ -394,7 +395,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MFace *mf; MFace *mf_prev; - SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces"); + SortFace *sort_faces = (SortFace *)MEM_callocN(sizeof(SortFace) * totface, "search faces"); SortFace *sf; SortFace *sf_prev; uint totsortface = 0; @@ -547,7 +548,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, { BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); - SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys"); + SortPoly *sort_polys = (SortPoly *)MEM_callocN(sizeof(SortPoly) * totpoly, + "mesh validate's sort_polys"); SortPoly *prev_sp, *sp = sort_polys; int prev_end; @@ -586,7 +588,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, /* Poly itself is valid, for now. */ int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */ sp->invalid = false; - sp->verts = v = MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); + sp->verts = v = (int *)MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); sp->numverts = mp->totloop; sp->loopstart = mp->loopstart; @@ -725,7 +727,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, /* Third check pass, testing loops used by none or more than one poly. */ qsort(sort_polys, totpoly, sizeof(SortPoly), search_polyloop_cmp); sp = sort_polys; - prev_sp = NULL; + prev_sp = nullptr; prev_end = 0; for (i = 0; i < totpoly; i++, sp++) { /* Free this now, we don't need it anymore, and avoid us another loop! */ @@ -733,7 +735,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MEM_freeN(sp->verts); } - /* Note above prev_sp: in following code, we make sure it is always valid poly (or NULL). */ + /* Note above prev_sp: in following code, we make sure it is always valid poly (or nullptr). + */ if (sp->invalid) { if (do_fixes) { REMOVE_POLY_TAG((&mpolys[sp->index])); @@ -792,7 +795,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MEM_freeN(sort_polys); } - BLI_edgehash_free(edge_hash, NULL); + BLI_edgehash_free(edge_hash, nullptr); /* fix deform verts */ if (dverts) { @@ -907,7 +910,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, if (free_flag.mselect) { MEM_freeN(mesh->mselect); - mesh->mselect = NULL; + mesh->mselect = nullptr; mesh->totselect = 0; } } @@ -1192,7 +1195,7 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me) MLoop *l; int a, b; /* New loops idx! */ - int *new_idx = MEM_mallocN(sizeof(int) * me->totloop, __func__); + int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__); for (a = b = 0, p = me->mpoly; a < me->totpoly; a++, p++) { bool invalid = false; @@ -1262,7 +1265,7 @@ void BKE_mesh_strip_loose_edges(Mesh *me) MEdge *e; MLoop *l; int a, b; - uint *new_idx = MEM_mallocN(sizeof(int) * me->totedge, __func__); + uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * me->totedge, __func__); for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) { if (e->v1 != e->v2) { @@ -1322,7 +1325,8 @@ static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, sh static int vergedgesort(const void *v1, const void *v2) { - const struct EdgeSort *x1 = v1, *x2 = v2; + const struct EdgeSort *x1 = static_cast<const struct EdgeSort *>(v1); + const struct EdgeSort *x2 = static_cast<const struct EdgeSort *>(v2); if (x1->v1 > x2->v1) { return 1; @@ -1380,12 +1384,12 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), if (totedge == 0) { /* flag that mesh has edges */ - (*r_medge) = MEM_callocN(0, __func__); + (*r_medge) = (MEdge *)MEM_callocN(0, __func__); (*r_totedge) = 0; return; } - ed = edsort = MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort"); + ed = edsort = (EdgeSort *)MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort"); for (a = totface, mface = allface; a > 0; a--, mface++) { to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2); @@ -1411,7 +1415,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), } totedge_final++; - medge = MEM_callocN(sizeof(MEdge) * totedge_final, __func__); + medge = (MEdge *)MEM_callocN(sizeof(MEdge) * totedge_final, __func__); for (a = totedge, med = medge, ed = edsort; a > 1; a--, ed++) { /* edge is unique when it differs from next edge, or is last */ @@ -1433,7 +1437,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), med++; } else { - /* equal edge, we merge the drawflag */ + /* Equal edge, merge the draw-flag. */ (ed + 1)->is_draw |= ed->is_draw; } } @@ -1469,7 +1473,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), } } - BLI_edgehash_free(hash, NULL); + BLI_edgehash_free(hash, nullptr); *r_medge = medge; *r_totedge = totedge_final; @@ -1499,7 +1503,7 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old) return; } - medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge); + medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge); me->medge = medge; me->totedge = totedge; @@ -1548,11 +1552,11 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh) /* write new edges into a temporary CustomData */ CustomData edgeData; CustomData_reset(&edgeData); - CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges); - CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); + CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, nullptr, numEdges); + CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, nullptr, numEdges); - MEdge *med = CustomData_get_layer(&edgeData, CD_MEDGE); - int *index = CustomData_get_layer(&edgeData, CD_ORIGINDEX); + MEdge *med = (MEdge *)CustomData_get_layer(&edgeData, CD_MEDGE); + int *index = (int *)CustomData_get_layer(&edgeData, CD_ORIGINDEX); EdgeSetIterator *ehi = BLI_edgesetIterator_new(eh); for (int i = 0; BLI_edgesetIterator_isDone(ehi) == false; @@ -1569,7 +1573,7 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh) mesh->edata = edgeData; mesh->totedge = numEdges; - mesh->medge = CustomData_get_layer(&mesh->edata, CD_MEDGE); + mesh->medge = (MEdge *)CustomData_get_layer(&mesh->edata, CD_MEDGE); BLI_edgeset_free(eh); } diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index 267020fb675..f9fcaa0dceb 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -115,6 +115,16 @@ static void mesh_wrapper_ensure_mdata_isolated(void *userdata) BMEditMesh *em = me->edit_mesh; BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); + /* Adding original index layers assumes that all BMesh mesh wrappers are created from + * original edit mode meshes (the only case where adding original indices makes sense). + * If that assumption is broken, the layers might be incorrect in that they might not + * actually be "original". + * + * There is also a performance aspect, where this also assumes that original indices are + * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not + * harmful. */ + BKE_mesh_ensure_default_orig_index_customdata(me); + EditMeshData *edit_data = me->runtime.edit_data; if (edit_data->vertexCos) { BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 46fea5ae115..5af8dfc2b72 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -131,7 +131,7 @@ void BKE_modifier_panel_expand(ModifierData *md) /***/ -ModifierData *BKE_modifier_new(int type) +static ModifierData *modifier_allocate_and_init(int type) { const ModifierTypeInfo *mti = BKE_modifier_get_info(type); ModifierData *md = MEM_callocN(mti->structSize, mti->structName); @@ -152,6 +152,13 @@ ModifierData *BKE_modifier_new(int type) mti->initData(md); } + return md; +} + +ModifierData *BKE_modifier_new(int type) +{ + ModifierData *md = modifier_allocate_and_init(type); + BKE_modifier_session_uuid_generate(md); return md; @@ -315,6 +322,16 @@ void BKE_modifiers_foreach_tex_link(Object *ob, TexWalkFunc walk, void *userData } } +ModifierData *BKE_modifier_copy_ex(const ModifierData *md, int flag) +{ + ModifierData *md_dst = modifier_allocate_and_init(md->type); + + BLI_strncpy(md_dst->name, md->name, sizeof(md_dst->name)); + BKE_modifier_copydata_ex(md, md_dst, flag); + + return md_dst; +} + void BKE_modifier_copydata_generic(const ModifierData *md_src, ModifierData *md_dst, const int UNUSED(flag)) @@ -348,7 +365,7 @@ static void modifier_copy_data_id_us_cb(void *UNUSED(userData), } } -void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int flag) +void BKE_modifier_copydata_ex(const ModifierData *md, ModifierData *target, const int flag) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); @@ -378,7 +395,7 @@ void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int } } -void BKE_modifier_copydata(ModifierData *md, ModifierData *target) +void BKE_modifier_copydata(const ModifierData *md, ModifierData *target) { BKE_modifier_copydata_ex(md, target, 0); } @@ -651,7 +668,7 @@ ModifierData *BKE_modifiers_get_virtual_modifierlist(const Object *ob, virtualModifierData->amd.deformflag = ((bArmature *)(ob->parent->data))->deformflag; md = &virtualModifierData->amd.modifier; } - else if (ob->parent->type == OB_CURVE && ob->partype == PARSKEL) { + else if (ob->parent->type == OB_CURVES_LEGACY && ob->partype == PARSKEL) { virtualModifierData->cmd.object = ob->parent; virtualModifierData->cmd.defaxis = ob->trackflag + 1; virtualModifierData->cmd.modifier.next = md; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c8e82302787..96bfcb0311b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1562,13 +1562,15 @@ static void socket_id_user_increment(bNodeSocket *sock) } } -static void socket_id_user_decrement(bNodeSocket *sock) +/** \return True if the socket had an ID default value. */ +static bool socket_id_user_decrement(bNodeSocket *sock) { switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1576,6 +1578,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1584,6 +1587,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1591,6 +1595,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1598,6 +1603,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1613,6 +1619,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) case SOCK_GEOMETRY: break; } + return false; } void nodeModifySocketType(bNodeTree *ntree, @@ -1945,6 +1952,8 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) } } + BLI_freelistN(&node->internal_links); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) { node_socket_free(sock, true); MEM_freeN(sock); @@ -2431,6 +2440,11 @@ bool nodeLinkIsHidden(const bNodeLink *link) return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock); } +bool nodeLinkIsSelected(const bNodeLink *link) +{ + return (link->fromnode->flag & NODE_SELECT) || (link->tonode->flag & NODE_SELECT); +} + /* Adjust the indices of links connected to the given multi input socket after deleting the link at * `deleted_index`. This function also works if the link has not yet been deleted. */ static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree, @@ -2972,6 +2986,8 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) * do to ID user refcounting and removal of animdation data then. */ BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0); + bool node_has_id = false; + if (do_id_user) { /* Free callback for NodeCustomGroup. */ if (node->typeinfo->freefunc_api) { @@ -2984,13 +3000,14 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) /* Do user counting. */ if (node->id) { id_us_min(node->id); + node_has_id = true; } LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - socket_id_user_decrement(sock); + node_has_id |= socket_id_user_decrement(sock); } LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - socket_id_user_decrement(sock); + node_has_id |= socket_id_user_decrement(sock); } } @@ -3007,6 +3024,12 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) } } + if (node_has_id) { + if (bmain != nullptr) { + DEG_relations_tag_update(bmain); + } + } + nodeUnlinkNode(ntree, node); node_unlink_attached(ntree, node); @@ -4735,6 +4758,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); + register_node_type_geo_duplicate_elements(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_dual_mesh(); register_node_type_geo_edge_split(); @@ -4753,6 +4777,7 @@ static void registerGeometryNodes() register_node_type_geo_input_mesh_edge_neighbors(); register_node_type_geo_input_mesh_edge_vertices(); register_node_type_geo_input_mesh_face_area(); + register_node_type_geo_input_mesh_face_is_planar(); register_node_type_geo_input_mesh_face_neighbors(); register_node_type_geo_input_mesh_island(); register_node_type_geo_input_mesh_vertex_neighbors(); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 579e61750f0..985c9edac1a 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -73,7 +73,7 @@ #include "BKE_constraint.h" #include "BKE_crazyspace.h" #include "BKE_curve.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_duplilist.h" @@ -1385,8 +1385,14 @@ ModifierData *BKE_object_active_modifier(const Object *ob) bool BKE_object_supports_modifiers(const Object *ob) { - return ( - ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME)); + return (ELEM(ob->type, + OB_MESH, + OB_CURVES_LEGACY, + OB_SURF, + OB_FONT, + OB_LATTICE, + OB_POINTCLOUD, + OB_VOLUME)); } bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) @@ -1402,7 +1408,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) { return (mti->modifyGeometrySet != nullptr); } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) { if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { return false; } @@ -1599,9 +1605,7 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst, continue; } - ModifierData *md_dst = BKE_modifier_new(md_src->type); - BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); - BKE_modifier_copydata_ex(md_src, md_dst, flag_subdata); + ModifierData *md_dst = BKE_modifier_copy_ex(md_src, flag_subdata); BLI_addtail(&ob_dst->modifiers, md_dst); } @@ -1873,7 +1877,7 @@ bool BKE_object_is_in_editmode(const Object *ob) case OB_LATTICE: return ((Lattice *)ob->data)->editlatt != nullptr; case OB_SURF: - case OB_CURVE: + case OB_CURVES_LEGACY: return ((Curve *)ob->data)->editnurb != nullptr; case OB_GPENCIL: /* Grease Pencil object has no edit mode data. */ @@ -1895,7 +1899,7 @@ bool BKE_object_data_is_in_editmode(const ID *id) switch (type) { case ID_ME: return ((const Mesh *)id)->edit_mesh != nullptr; - case ID_CU: + case ID_CU_LEGACY: return ((((const Curve *)id)->editnurb != nullptr) || (((const Curve *)id)->editfont != nullptr)); case ID_MB: @@ -1921,7 +1925,7 @@ char *BKE_object_data_editmode_flush_ptr_get(struct ID *id) } break; } - case ID_CU: { + case ID_CU_LEGACY: { if (((Curve *)id)->vfont != nullptr) { EditFont *ef = ((Curve *)id)->editfont; if (ef != nullptr) { @@ -2064,7 +2068,7 @@ static const char *get_obdata_defname(int type) switch (type) { case OB_MESH: return DATA_("Mesh"); - case OB_CURVE: + case OB_CURVES_LEGACY: return DATA_("Curve"); case OB_SURF: return DATA_("Surf"); @@ -2135,8 +2139,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) switch (type) { case OB_MESH: return BKE_mesh_add(bmain, name); - case OB_CURVE: - return BKE_curve_add(bmain, name, OB_CURVE); + case OB_CURVES_LEGACY: + return BKE_curve_add(bmain, name, OB_CURVES_LEGACY); case OB_SURF: return BKE_curve_add(bmain, name, OB_SURF); case OB_FONT: @@ -2177,7 +2181,7 @@ int BKE_object_obdata_to_type(const ID *id) switch (GS(id->name)) { case ID_ME: return OB_MESH; - case ID_CU: + case ID_CU_LEGACY: return BKE_curve_type_get((const Curve *)id); case ID_MB: return OB_MBALL; @@ -2658,7 +2662,7 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: if (dupflag & USER_DUP_CURVE) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } @@ -3222,7 +3226,7 @@ static void give_parvert(Object *par, int nr, float vec[3]) "object position can be wrong now"); } } - else if (ELEM(par->type, OB_CURVE, OB_SURF)) { + else if (ELEM(par->type, OB_CURVES_LEGACY, OB_SURF)) { ListBase *nurb; /* Unless there's some weird depsgraph failure the cache should exist. */ @@ -3295,7 +3299,7 @@ void BKE_object_get_parent_matrix(Object *ob, Object *par, float r_parentmat[4][ switch (ob->partype & PARTYPE) { case PAROBJECT: { bool ok = false; - if (par->type == OB_CURVE) { + if (par->type == OB_CURVES_LEGACY) { if ((((Curve *)par->data)->flag & CU_PATH) && (ob_parcurve(ob, par, tmat))) { ok = true; } @@ -3591,7 +3595,7 @@ BoundBox *BKE_object_boundbox_get(Object *ob) case OB_MESH: bb = BKE_mesh_boundbox_get(ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: bb = BKE_curve_boundbox_get(ob); @@ -3760,7 +3764,7 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us bool changed = false; switch (ob->type) { - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: { BoundBox bb = *BKE_curve_boundbox_get(ob); @@ -3938,7 +3942,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, ListBase *lb = object_duplilist(depsgraph, scene, ob); LISTBASE_FOREACH (DupliObject *, dob, lb) { - if ((use_hidden == false) && (dob->no_draw != 0)) { + if (((use_hidden == false) && (dob->no_draw != 0)) || dob->ob_data == nullptr) { /* pass */ } else { @@ -4189,7 +4193,7 @@ bool BKE_object_obdata_texspace_get(Object *ob, char **r_texflag, float **r_loc, BKE_mesh_texspace_get_reference((Mesh *)ob->data, r_texflag, r_loc, r_size); break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob->data; BKE_curve_texspace_ensure(cu); if (r_texflag) { @@ -4557,7 +4561,7 @@ KeyBlock *BKE_object_shapekey_insert(Main *bmain, case OB_MESH: key = insert_meshkey(bmain, ob, name, from_mix); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: key = insert_curvekey(bmain, ob, name, from_mix); break; @@ -4629,7 +4633,7 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) case OB_MESH: BKE_keyblock_convert_to_mesh(key->refkey, (Mesh *)ob->data); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: BKE_keyblock_convert_to_curve( key->refkey, (Curve *)ob->data, BKE_curve_nurbs_get((Curve *)ob->data)); @@ -4844,7 +4848,7 @@ int BKE_object_is_deform_modified(Scene *scene, Object *ob) flag |= eModifierMode_Realtime | eModifierMode_Render; } - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; if (cu->taperobj != nullptr && object_deforms_in_time(cu->taperobj)) { flag |= eModifierMode_Realtime | eModifierMode_Render; @@ -4921,7 +4925,7 @@ bool BKE_object_supports_material_slots(struct Object *ob) { return ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_MBALL, @@ -5155,7 +5159,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) BLI_kdtree_3d_balance(tree); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: { /* TODO: take deformation into account */ Curve *cu = (Curve *)ob->data; @@ -5462,7 +5466,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, } /* for curve following objects, parented curve has to be updated too */ - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; BKE_animsys_evaluate_animdata( &cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 18dd61004f5..009a7bd70be 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -851,7 +851,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, dupli->ob_data = (ID *)volume; } } - if (!ELEM(ctx->object->type, OB_CURVE, OB_FONT) || geometry_set_is_instance) { + if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT) || geometry_set_is_instance) { const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); if (curve_component != nullptr) { const Curve *curve = curve_component->get_curve_for_render(); diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 2b0e04d0bd0..3bc2139ca0c 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -181,7 +181,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o BKE_displist_make_mball(depsgraph, scene, ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: { bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); @@ -300,7 +300,7 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data) BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data, BKE_LATTICE_BATCH_DIRTY_ALL); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL); break; case ID_MB: @@ -364,7 +364,7 @@ void BKE_object_data_select_update(Depsgraph *depsgraph, ID *object_data) case ID_ME: BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_SELECT); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_batch_cache_dirty_tag((Curve *)object_data, BKE_CURVE_BATCH_DIRTY_SELECT); break; case ID_LT: diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index ffd0a03fc51..d42c8ea37d5 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -327,6 +327,9 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) case PAINT_MODE_WEIGHT_GPENCIL: paint_ptr = (Paint **)&ts->gp_weightpaint; break; + case PAINT_MODE_SCULPT_CURVES: + paint_ptr = (Paint **)&ts->curves_sculpt; + break; case PAINT_MODE_INVALID: break; } @@ -362,6 +365,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->gp_sculptpaint->paint; case PAINT_MODE_WEIGHT_GPENCIL: return &ts->gp_weightpaint->paint; + case PAINT_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; case PAINT_MODE_INVALID: return NULL; default: @@ -394,6 +399,8 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode) return rna_enum_brush_gpencil_sculpt_types_items; case PAINT_MODE_WEIGHT_GPENCIL: return rna_enum_brush_gpencil_weight_types_items; + case PAINT_MODE_SCULPT_CURVES: + return rna_enum_brush_curves_sculpt_tool_items; case PAINT_MODE_INVALID: break; } @@ -422,6 +429,8 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode) return "gpencil_sculpt_tool"; case PAINT_MODE_WEIGHT_GPENCIL: return "gpencil_weight_tool"; + case PAINT_MODE_SCULPT_CURVES: + return "curves_sculpt_tool"; case PAINT_MODE_INVALID: break; } @@ -453,6 +462,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->gp_sculptpaint->paint; case OB_MODE_WEIGHT_GPENCIL: return &ts->gp_weightpaint->paint; + case OB_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; case OB_MODE_EDIT: return ts->uvsculpt ? &ts->uvsculpt->paint : NULL; default: @@ -573,6 +584,8 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) return PAINT_MODE_SCULPT_GPENCIL; case CTX_MODE_WEIGHT_GPENCIL: return PAINT_MODE_WEIGHT_GPENCIL; + case CTX_MODE_SCULPT_CURVES: + return PAINT_MODE_SCULPT_CURVES; } } else if (tref->space_type == SPACE_IMAGE) { @@ -641,6 +654,10 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL; } + else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { + paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES; + } else { BLI_assert_unreachable(); } @@ -668,6 +685,8 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode) return offsetof(Brush, gpencil_sculpt_tool); case PAINT_MODE_WEIGHT_GPENCIL: return offsetof(Brush, gpencil_weight_tool); + case PAINT_MODE_SCULPT_CURVES: + return offsetof(Brush, curves_sculpt_tool); case PAINT_MODE_INVALID: break; /* We don't use these yet. */ } @@ -1028,6 +1047,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) (Paint *)ts->vpaint, (Paint *)ts->wpaint, (Paint *)ts->uvsculpt, + (Paint *)ts->curves_sculpt, (Paint *)&ts->imapaint)); #ifdef DEBUG struct Paint paint_test = **r_paint; @@ -1075,6 +1095,10 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) UvSculpt *data = MEM_callocN(sizeof(*data), __func__); paint = &data->paint; } + else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { + CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__); + paint = &data->paint; + } else if (*r_paint == &ts->imapaint.paint) { paint = &ts->imapaint.paint; } diff --git a/source/blender/blenkernel/intern/paint_toolslots.c b/source/blender/blenkernel/intern/paint_toolslots.c index 04b70aae199..f35755021d2 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.c +++ b/source/blender/blenkernel/intern/paint_toolslots.c @@ -98,6 +98,9 @@ void BKE_paint_toolslots_init_from_main(struct Main *bmain) if (ts->gp_weightpaint) { paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint); } + if (ts->curves_sculpt) { + paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint); + } } } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 2a4e0715293..3ee46fc4f15 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -11,6 +11,7 @@ #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_bounds.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" @@ -254,68 +255,28 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) return pointcloud; } -struct MinMaxResult { - float3 min; - float3 max; -}; - -static MinMaxResult min_max_no_radii(Span<float3> positions) +static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds( + const PointCloud &pointcloud) { - using namespace blender::math; - - return blender::threading::parallel_reduce( - positions.index_range(), - 1024, - MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)}, - [&](IndexRange range, const MinMaxResult &init) { - MinMaxResult result = init; - for (const int i : range) { - min_max(positions[i], result.min, result.max); - } - return result; - }, - [](const MinMaxResult &a, const MinMaxResult &b) { - return MinMaxResult{min(a.min, b.min), max(a.max, b.max)}; - }); -} - -static MinMaxResult min_max_with_radii(Span<float3> positions, Span<float> radii) -{ - using namespace blender::math; - - return blender::threading::parallel_reduce( - positions.index_range(), - 1024, - MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)}, - [&](IndexRange range, const MinMaxResult &init) { - MinMaxResult result = init; - for (const int i : range) { - result.min = min(positions[i] - radii[i], result.min); - result.max = max(positions[i] + radii[i], result.max); - } - return result; - }, - [](const MinMaxResult &a, const MinMaxResult &b) { - return MinMaxResult{min(a.min, b.min), max(a.max, b.max)}; - }); + Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint}; + if (pointcloud.radius) { + Span<float> radii{pointcloud.radius, pointcloud.totpoint}; + return blender::bounds::min_max_with_radii(positions, radii); + } + return blender::bounds::min_max(positions); } bool BKE_pointcloud_minmax(const PointCloud *pointcloud, float r_min[3], float r_max[3]) { - using namespace blender::math; + using namespace blender; - if (!pointcloud->totpoint) { + const std::optional<bounds::MinMaxResult<float3>> min_max = point_cloud_bounds(*pointcloud); + if (!min_max) { return false; } - Span<float3> positions{reinterpret_cast<float3 *>(pointcloud->co), pointcloud->totpoint}; - const MinMaxResult min_max = (pointcloud->radius) ? - min_max_with_radii(positions, - {pointcloud->radius, pointcloud->totpoint}) : - min_max_no_radii(positions); - - copy_v3_v3(r_min, min(min_max.min, float3(r_min))); - copy_v3_v3(r_max, max(min_max.max, float3(r_max))); + copy_v3_v3(r_min, math::min(min_max->min, float3(r_min))); + copy_v3_v3(r_max, math::max(min_max->max, float3(r_max))); return true; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 7827c40e2c2..baf2f0bac8a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -317,12 +317,6 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int scene_dst->r.avicodecdata->lpParms = MEM_dupallocN(scene_dst->r.avicodecdata->lpParms); } - if (scene_src->r.ffcodecdata.properties) { - /* intentionally check sce_dst not sce_src. */ /* XXX ??? comment outdated... */ - scene_dst->r.ffcodecdata.properties = IDP_CopyProperty_ex(scene_src->r.ffcodecdata.properties, - flag_subdata); - } - if (scene_src->display.shading.prop) { scene_dst->display.shading.prop = IDP_CopyProperty(scene_src->display.shading.prop); } @@ -393,10 +387,6 @@ static void scene_free_data(ID *id) MEM_freeN(scene->r.avicodecdata); scene->r.avicodecdata = NULL; } - if (scene->r.ffcodecdata.properties) { - IDP_FreeProperty(scene->r.ffcodecdata.properties); - scene->r.ffcodecdata.properties = NULL; - } scene_free_markers(scene, do_id_user); BLI_freelistN(&scene->transform_spaces); @@ -718,6 +708,16 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, reader, &toolsett_old->gp_weightpaint->paint)); } + if (toolsett->curves_sculpt) { + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->curves_sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->curves_sculpt->paint)); + } BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, toolsett->gp_sculpt.guide.reference_object, @@ -972,6 +972,10 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLO_write_struct(writer, GpWeightPaint, tos->gp_weightpaint); BKE_paint_blend_write(writer, &tos->gp_weightpaint->paint); } + if (tos->curves_sculpt) { + BLO_write_struct(writer, CurvesSculpt, tos->curves_sculpt); + BKE_paint_blend_write(writer, &tos->curves_sculpt->paint); + } /* write grease-pencil custom ipo curve to file */ if (tos->gp_interpolate.custom_ipo) { BKE_curvemapping_blend_write(writer, tos->gp_interpolate.custom_ipo); @@ -1014,9 +1018,6 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLO_write_raw(writer, (size_t)sce->r.avicodecdata->cbParms, sce->r.avicodecdata->lpParms); } } - if (sce->r.ffcodecdata.properties) { - IDP_BlendWrite(writer, sce->r.ffcodecdata.properties); - } /* writing dynamic list of TimeMarkers to the blend file */ LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { @@ -1148,6 +1149,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_vertexpaint); direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_sculptpaint); direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_weightpaint); + direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->curves_sculpt); BKE_paint_blend_read_data(reader, sce, &sce->toolsettings->imapaint.paint); @@ -1256,11 +1258,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &sce->r.avicodecdata->lpFormat); BLO_read_data_address(reader, &sce->r.avicodecdata->lpParms); } - if (sce->r.ffcodecdata.properties) { - BLO_read_data_address(reader, &sce->r.ffcodecdata.properties); - IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties); - } - BLO_read_list(reader, &(sce->markers)); LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { BLO_read_data_address(reader, &marker->prop); @@ -1406,6 +1403,9 @@ static void scene_blend_read_lib(BlendLibReader *reader, ID *id) if (sce->toolsettings->gp_weightpaint) { BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->gp_weightpaint->paint); } + if (sce->toolsettings->curves_sculpt) { + BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->curves_sculpt->paint); + } if (sce->toolsettings->sculpt) { BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->sculpt->gravity_object); @@ -1726,6 +1726,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->gp_weightpaint = MEM_dupallocN(ts->gp_weightpaint); BKE_paint_copy(&ts->gp_weightpaint->paint, &ts->gp_weightpaint->paint, flag); } + if (ts->curves_sculpt) { + ts->curves_sculpt = MEM_dupallocN(ts->curves_sculpt); + BKE_paint_copy(&ts->curves_sculpt->paint, &ts->curves_sculpt->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->particle.paintcursor = NULL; @@ -1781,6 +1785,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->gp_weightpaint->paint); MEM_freeN(toolsettings->gp_weightpaint); } + if (toolsettings->curves_sculpt) { + BKE_paint_free(&toolsettings->curves_sculpt->paint); + MEM_freeN(toolsettings->curves_sculpt); + } BKE_paint_free(&toolsettings->imapaint.paint); /* free Grease Pencil interpolation curve */ @@ -1873,10 +1881,6 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) sce_copy->r.avicodecdata->lpParms = MEM_dupallocN(sce_copy->r.avicodecdata->lpParms); } - if (sce->r.ffcodecdata.properties) { /* intentionally check scen not sce. */ - sce_copy->r.ffcodecdata.properties = IDP_CopyProperty(sce->r.ffcodecdata.properties); - } - BKE_sound_reset_scene_runtime(sce_copy); /* grease pencil */ diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 7b8e5a1409a..38066f95084 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2967,7 +2967,7 @@ static void curve_surf_to_softbody(Object *ob) totvert = BKE_nurbList_verts_count(&cu->nurb); if (ob->softflag & OB_SB_EDGES) { - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { totspring = totvert - BLI_listbase_count(&cu->nurb); } } @@ -3320,7 +3320,7 @@ static void softbody_reset(Object *ob, SoftBody *sb, float (*vertexCos)[3], int break; case OB_LATTICE: break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; default: @@ -3537,7 +3537,7 @@ void sbObjectStep(struct Depsgraph *depsgraph, case OB_LATTICE: lattice_to_softbody(ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: curve_surf_to_softbody(ob); break; diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index d1c4756a3b9..dc5b1d28539 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -23,7 +23,7 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray; -Spline::Type Spline::type() const +CurveType Spline::type() const { return type_; } @@ -34,15 +34,18 @@ void Spline::copy_base_settings(const Spline &src, Spline &dst) dst.is_cyclic_ = src.is_cyclic_; } -static SplinePtr create_spline(const Spline::Type type) +static SplinePtr create_spline(const CurveType type) { switch (type) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return std::make_unique<PolySpline>(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return std::make_unique<BezierSpline>(); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return std::make_unique<NURBSpline>(); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 57f1d73d55e..3c2ac1dae9c 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -93,11 +93,11 @@ Span<float> BezierSpline::tilts() const { return tilts_; } -Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const +Span<int8_t> BezierSpline::handle_types_left() const { return handle_types_left_; } -MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left() +MutableSpan<int8_t> BezierSpline::handle_types_left() { return handle_types_left_; } @@ -114,11 +114,11 @@ MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only) return handle_positions_left_; } -Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const +Span<int8_t> BezierSpline::handle_types_right() const { return handle_types_right_; } -MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right() +MutableSpan<int8_t> BezierSpline::handle_types_right() { return handle_types_right_; } @@ -187,7 +187,7 @@ void BezierSpline::ensure_auto_handles() const for (const int i : IndexRange(this->size())) { using namespace blender; - if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) { + if (ELEM(BEZIER_HANDLE_AUTO, handle_types_left_[i], handle_types_right_[i])) { const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i); const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i]; float prev_len = math::length(prev_diff); @@ -203,23 +203,23 @@ void BezierSpline::ensure_auto_handles() const /* This magic number is unfortunate, but comes from elsewhere in Blender. */ const float len = math::length(dir) * 2.5614f; if (len != 0.0f) { - if (handle_types_left_[i] == HandleType::Auto) { + if (handle_types_left_[i] == BEZIER_HANDLE_AUTO) { const float prev_len_clamped = std::min(prev_len, next_len * 5.0f); handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len); } - if (handle_types_right_[i] == HandleType::Auto) { + if (handle_types_right_[i] == BEZIER_HANDLE_AUTO) { const float next_len_clamped = std::min(next_len, prev_len * 5.0f); handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len); } } } - if (handle_types_left_[i] == HandleType::Vector) { + if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) { const float3 prev = previous_position(positions_, is_cyclic_, i); handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f); } - if (handle_types_right_[i] == HandleType::Vector) { + if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) { const float3 next = next_position(positions_, is_cyclic_, i); handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f); } @@ -257,8 +257,8 @@ void BezierSpline::transform(const blender::float4x4 &matrix) } static void set_handle_position(const float3 &position, - const BezierSpline::HandleType type, - const BezierSpline::HandleType type_other, + const HandleType type, + const HandleType type_other, const float3 &new_value, float3 &handle, float3 &handle_other) @@ -266,12 +266,12 @@ static void set_handle_position(const float3 &position, using namespace blender::math; /* Don't bother when the handle positions are calculated automatically anyway. */ - if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) { + if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) { return; } handle = new_value; - if (type_other == BezierSpline::HandleType::Align) { + if (type_other == BEZIER_HANDLE_ALIGN) { /* Keep track of the old length of the opposite handle. */ const float length = distance(handle_other, position); /* Set the other handle to directly opposite from the current handle. */ @@ -283,8 +283,8 @@ static void set_handle_position(const float3 &position, void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value) { set_handle_position(positions_[index], - handle_types_right_[index], - handle_types_left_[index], + static_cast<HandleType>(handle_types_right_[index]), + static_cast<HandleType>(handle_types_left_[index]), value, handle_positions_right_[index], handle_positions_left_[index]); @@ -293,8 +293,8 @@ void BezierSpline::set_handle_position_right(const int index, const blender::flo void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value) { set_handle_position(positions_[index], - handle_types_left_[index], - handle_types_right_[index], + static_cast<HandleType>(handle_types_right_[index]), + static_cast<HandleType>(handle_types_left_[index]), value, handle_positions_left_[index], handle_positions_right_[index]); @@ -302,8 +302,8 @@ void BezierSpline::set_handle_position_left(const int index, const blender::floa bool BezierSpline::point_is_sharp(const int index) const { - return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || - ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); + return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) || + ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE); } bool BezierSpline::segment_is_vector(const int index) const @@ -313,15 +313,15 @@ bool BezierSpline::segment_is_vector(const int index) const if (index == this->size() - 1) { if (is_cyclic_) { - return handle_types_right_.last() == HandleType::Vector && - handle_types_left_.first() == HandleType::Vector; + return handle_types_right_.last() == BEZIER_HANDLE_VECTOR && + handle_types_left_.first() == BEZIER_HANDLE_VECTOR; } /* There is actually no segment in this case, but it's nice to avoid * having a special case for the last segment in calling code. */ return true; } - return handle_types_right_[index] == HandleType::Vector && - handle_types_left_[index + 1] == HandleType::Vector; + return handle_types_right_[index] == BEZIER_HANDLE_VECTOR && + handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR; } void BezierSpline::mark_cache_invalid() diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index fd56582f5e3..50135110a64 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -72,6 +72,9 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx) static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices) { + if (!ctx->have_displacement) { + return; + } ctx->accumulated_counters = MEM_calloc_arrayN( num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } @@ -424,18 +427,17 @@ static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx, const float v, MVert *subdiv_vert) { + /* Accumulate displacement. */ Subdiv *subdiv = ctx->subdiv; const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert; float dummy_P[3], dPdu[3], dPdv[3], D[3]; BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv); - /* Accumulate displacement if needed. */ - if (ctx->have_displacement) { - /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero - * locations as a default calloc(). */ - BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); - add_v3_v3(subdiv_vert->co, D); - } + /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero + * locations as a default calloc(). */ + BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); + add_v3_v3(subdiv_vert->co, D); + if (ctx->accumulated_counters) { ++ctx->accumulated_counters[subdiv_vertex_index]; } @@ -554,12 +556,13 @@ static void evaluate_vertex_and_apply_displacement_interpolate( add_v3_v3(subdiv_vert->co, D); } -static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int ptex_face_index, - const float u, - const float v, - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_corner_or_edge( + const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int ptex_face_index, + const float u, + const float v, + const int subdiv_vertex_index) { SubdivMeshContext *ctx = foreach_context->user_data; Mesh *subdiv_mesh = ctx->subdiv_mesh; @@ -568,31 +571,32 @@ static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext * subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert); } -static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_vertex_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_corner( + const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_vertex_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) { - subdiv_mesh_vertex_every_corner_or_edge( + subdiv_mesh_vertex_displacement_every_corner_or_edge( foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); } -static void subdiv_mesh_vertex_every_edge(const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_edge_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_edge_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) { - subdiv_mesh_vertex_every_corner_or_edge( + subdiv_mesh_vertex_displacement_every_corner_or_edge( foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); } @@ -1078,12 +1082,8 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, foreach_context->topology_info = subdiv_mesh_topology_info; /* Every boundary geometry. Used for displacement averaging. */ if (subdiv_context->have_displacement) { - foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner; - foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge; - } - else { - foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner; - foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge; + foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner; + foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge; } foreach_context->vertex_corner = subdiv_mesh_vertex_corner; foreach_context->vertex_edge = subdiv_mesh_vertex_edge; diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index dd35388f230..34dfdaf7595 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -54,23 +54,20 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene, return md; } -bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, - const Object *ob, - const SubsurfModifierData *smd, - int required_mode, - bool skip_check_is_last) +bool BKE_subsurf_modifier_use_custom_loop_normals(const SubsurfModifierData *smd, const Mesh *mesh) { - if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { - return false; - } + return (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh->flag & ME_AUTOSMOOTH) && + CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); +} - if (!skip_check_is_last) { - ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); - if (md != (const ModifierData *)smd) { - return false; - } - } +static bool subsurf_modifier_use_autosmooth_or_split_normals(const SubsurfModifierData *smd, + const Mesh *mesh) +{ + return (mesh->flag & ME_AUTOSMOOTH) || BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); +} +static bool is_subdivision_evaluation_possible_on_gpu(void) +{ /* Only OpenGL is supported for OpenSubdiv evaluation for now. */ if (GPU_backend_get_type() != GPU_BACKEND_OPENGL) { return false; @@ -88,8 +85,52 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, return true; } +bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfModifierData *smd, + const Mesh *mesh) +{ + if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { + /* GPU subdivision is explicitly disabled, so we don't force it. */ + return false; + } + + if (!is_subdivision_evaluation_possible_on_gpu()) { + /* The GPU type is not compatible with the subdivision. */ + return false; + } + + return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh); +} + +bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, + const Object *ob, + const Mesh *mesh, + const SubsurfModifierData *smd, + int required_mode, + bool skip_check_is_last) +{ + if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { + return false; + } + + /* Deactivate GPU subdivision if autosmooth or custom split normals are used as those are + * complicated to support on GPU, and should really be separate workflows. */ + if (subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh)) { + return false; + } + + if (!skip_check_is_last) { + ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); + if (md != (const ModifierData *)smd) { + return false; + } + } + + return is_subdivision_evaluation_possible_on_gpu(); +} + bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, const Object *ob, + const Mesh *mesh, int required_mode) { ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); @@ -103,7 +144,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, } return BKE_subsurf_modifier_can_do_gpu_subdiv_ex( - scene, ob, (SubsurfModifierData *)md, required_mode, true); + scene, ob, mesh, (SubsurfModifierData *)md, required_mode, true); } void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL; diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index cb5e351e78a..5f751da1ee1 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -811,8 +811,8 @@ static bool vfont_to_curve(Object *ob, #define MARGIN_X_MIN (xof_scale + tb_scale.x) #define MARGIN_Y_MIN (yof_scale + tb_scale.y) - /* remark: do calculations including the trailing '\0' of a string - * because the cursor can be at that location */ + /* NOTE: do calculations including the trailing '\0' of a string + * because the cursor can be at that location. */ BLI_assert(ob == NULL || ob->type == OB_FONT); @@ -905,8 +905,8 @@ static bool vfont_to_curve(Object *ob, custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK | CU_CHINFO_OVERFLOW); } - for (i = 0; i <= slen; i++) { - makebreak: + i = 0; + while (i <= slen) { /* Characters in the list */ info = &custrinfo[i]; ascii = mem[i]; @@ -941,19 +941,16 @@ static bool vfont_to_curve(Object *ob, che = find_vfont_char(vfd, ascii); BLI_rw_mutex_unlock(&vfont_rwlock); - /* - * The character wasn't in the current curve base so load it + /* The character wasn't in the current curve base so load it. * But if the font is built-in then do not try loading since - * whole font is in the memory already - */ + * whole font is in the memory already. */ if (che == NULL && BKE_vfont_is_builtin(vfont) == false) { BLI_rw_mutex_lock(&vfont_rwlock, THREAD_LOCK_WRITE); /* Check it once again, char might have been already load - * between previous BLI_rw_mutex_unlock() and this BLI_rw_mutex_lock(). + * between previous #BLI_rw_mutex_unlock() and this #BLI_rw_mutex_lock(). * * Such a check should not be a bottleneck since it wouldn't - * happen often once all the chars are load. - */ + * happen often once all the chars are load. */ if ((che = find_vfont_char(vfd, ascii)) == NULL) { che = BKE_vfontdata_char_from_freetypefont(vfont, ascii); } @@ -985,8 +982,23 @@ static bool vfont_to_curve(Object *ob, } else if (x_used > x_available) { // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]); - for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { - bool dobreak = false; + bool dobreak = false; + for (j = i; (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { + + /* Special case when there are no breaks possible. */ + if (UNLIKELY(j == 0)) { + if (i == slen) { + /* Use the behavior of zero a height text-box when a break cannot be inserted. + * + * Typically when a text-box has any height and overflow is set to scale + * the text will wrap to fit the width as necessary. When wrapping isn't + * possible it's important to use the same code-path as zero-height lines. + * Without this exception a single word will not scale-to-fit (see: T95116). */ + tb_scale.h = 0.0f; + } + break; + } + if (ELEM(mem[j], ' ', '-')) { ct -= (i - (j - 1)); cnr -= (i - (j - 1)); @@ -1001,24 +1013,18 @@ static bool vfont_to_curve(Object *ob, ct[1].dobreak = 1; custrinfo[i + 1].flag |= CU_CHINFO_WRAP; dobreak = true; + break; } - else if (chartransdata[j].dobreak) { - // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]); - ct->dobreak = 1; - custrinfo[i + 1].flag |= CU_CHINFO_WRAP; - ct -= 1; - cnr -= 1; - i--; - xof = ct->xof; - dobreak = true; - } - if (dobreak) { - if (tb_scale.h == 0.0f) { - /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ - custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; - } - goto makebreak; + BLI_assert(chartransdata[j].dobreak == 0); + } + + if (dobreak) { + if (tb_scale.h == 0.0f) { + /* NOTE: If underlined text is truncated away, the extra space is also truncated. */ + custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW; } + /* Since a break was added, re-run this loop with `i` at it's new value. */ + continue; } } } @@ -1063,22 +1069,12 @@ static bool vfont_to_curve(Object *ob, current_line_length = 0.0f; } - /* XXX(campbell): has been unused for years, need to check if this is useful, r4613 r5282. */ -#if 0 - if (ascii == '\n') { - xof = xof_scale; - } - else { - xof = MARGIN_X_MIN; - } -#else xof = MARGIN_X_MIN; -#endif lnr++; cnr = 0; wsnr = 0; } - else if (ascii == 9) { /* TAB */ + else if (ascii == '\t') { /* Tab character. */ float tabfac; ct->xof = xof; @@ -1106,7 +1102,7 @@ static bool vfont_to_curve(Object *ob, sb->w = xof * font_size; } - if (ascii == 32) { + if (ascii == ' ') { /* Space character. */ wsfac = cu->wordspace; wsnr++; } @@ -1114,7 +1110,7 @@ static bool vfont_to_curve(Object *ob, wsfac = 1.0f; } - /* Set the width of the character */ + /* Set the width of the character. */ twidth = char_width(cu, che, info); xof += (twidth * wsfac * (1.0f + (info->kern / 40.0f))) + xtrax; @@ -1124,7 +1120,9 @@ static bool vfont_to_curve(Object *ob, } } ct++; + i++; } + current_line_length += xof + twidth - MARGIN_X_MIN; longest_line_length = MAX2(current_line_length, longest_line_length); @@ -1213,7 +1211,7 @@ static bool vfont_to_curve(Object *ob, } } - /* top-baseline is default, in this case, do nothing */ + /* Top-baseline is default, in this case, do nothing. */ if (cu->align_y != CU_ALIGN_Y_TOP_BASELINE) { if (tb_scale.h != 0.0f) { /* We need to loop all the text-boxes even the "full" ones. @@ -1238,7 +1236,7 @@ static bool vfont_to_curve(Object *ob, } textbox_scale(&tb_scale, &cu->tb[tb_index], 1.0f / font_size); - /* The initial Y origin of the textbox is hardcoded to 1.0f * text scale. */ + /* The initial Y origin of the text-box is hard-coded to 1.0f * text scale. */ const float textbox_y_origin = 1.0f; float yoff = 0.0f; @@ -1302,8 +1300,8 @@ static bool vfont_to_curve(Object *ob, MEM_freeN(i_textbox_array); /* TEXT ON CURVE */ - /* NOTE: Only OB_CURVE objects could have a path. */ - if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) { + /* NOTE: Only #OB_CURVES_LEGACY objects could have a path. */ + if (cu->textoncurve && cu->textoncurve->type == OB_CURVES_LEGACY) { BLI_assert(cu->textoncurve->runtime.curve_cache != NULL); if (cu->textoncurve->runtime.curve_cache != NULL && cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) { @@ -1393,8 +1391,8 @@ static bool vfont_to_curve(Object *ob, ctime = timeofs + distfac * (ct->xof - minx); CLAMP(ctime, 0.0f, 1.0f); - /* calc the right loc AND the right rot separately */ - /* vec, tvec need 4 items */ + /* Calculate the right loc AND the right rot separately. */ + /* `vec`, `tvec` need 4 items. */ BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL); BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL); @@ -1455,7 +1453,7 @@ static bool vfont_to_curve(Object *ob, break; } cnr = ct->charnr; - /* seek for char with lnr en cnr */ + /* Seek for char with `lnr` & `cnr`. */ ef->pos = 0; ct = chartransdata; for (i = 0; i < slen; i++) { @@ -1473,7 +1471,7 @@ static bool vfont_to_curve(Object *ob, } } - /* cursor first */ + /* Cursor first. */ if (ef) { float si, co; @@ -1519,13 +1517,13 @@ static bool vfont_to_curve(Object *ob, } /* Only do that check in case we do have an object, otherwise all materials get erased every - * time that code is called without an object... */ + * time that code is called without an object. */ if (ob != NULL && (info->mat_nr > (ob->totcol))) { // CLOG_ERROR( // &LOG, "Illegal material index (%d) in text object, setting to 0", info->mat_nr); info->mat_nr = 0; } - /* We do not want to see any character for \n or \r */ + /* We don't want to see any character for '\n'. */ if (cha != '\n') { BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); } @@ -1601,8 +1599,7 @@ static bool vfont_to_curve(Object *ob, * * Keep in mind that there is no single number that will make all fit to the end. * In a way, our ultimate goal is to get the highest scale that still leads to the - * number of extra lines to zero. - */ + * number of extra lines to zero. */ if (iter_data->status == VFONT_TO_CURVE_INIT) { bool valid = true; @@ -1635,7 +1632,7 @@ static bool vfont_to_curve(Object *ob, iter_data->bisect.max = iter_data->scale_to_fit; } else { - /* It fits inside the textbox, scale it up. */ + /* It fits inside the text-box, scale it up. */ iter_data->bisect.min = iter_data->scale_to_fit; valid = true; } @@ -1697,7 +1694,7 @@ finally: } } - /* Store the effective scale, to use for the textbox lines. */ + /* Store the effective scale, to use for the text-box lines. */ cu->fsize_realtime = font_size; return ok; diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index ff3f20479f8..07db0328f56 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -1331,9 +1331,6 @@ VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid) if (grid.isType<openvdb::Vec3dGrid>()) { return VOLUME_GRID_VECTOR_DOUBLE; } - if (grid.isType<openvdb::StringGrid>()) { - return VOLUME_GRID_STRING; - } if (grid.isType<openvdb::MaskGrid>()) { return VOLUME_GRID_MASK; } @@ -1369,7 +1366,6 @@ int BKE_volume_grid_channels(const VolumeGrid *grid) case VOLUME_GRID_VECTOR_DOUBLE: case VOLUME_GRID_VECTOR_INT: return 3; - case VOLUME_GRID_STRING: case VOLUME_GRID_POINTS: case VOLUME_GRID_UNKNOWN: return 0; @@ -1610,13 +1606,8 @@ struct CreateGridWithChangedResolutionOp { template<typename GridType> typename openvdb::GridBase::Ptr operator()() { - if constexpr (std::is_same_v<GridType, openvdb::StringGrid>) { - return {}; - } - else { - return create_grid_with_changed_resolution(static_cast<const GridType &>(grid), - resolution_factor); - } + return create_grid_with_changed_resolution(static_cast<const GridType &>(grid), + resolution_factor); } }; diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc index ca18d6c3446..e7620be6401 100644 --- a/source/blender/blenkernel/intern/volume_render.cc +++ b/source/blender/blenkernel/intern/volume_render.cc @@ -63,7 +63,6 @@ static void extract_dense_float_voxels(const VolumeGridType grid_type, case VOLUME_GRID_VECTOR_INT: return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>( grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels)); - case VOLUME_GRID_STRING: case VOLUME_GRID_POINTS: case VOLUME_GRID_UNKNOWN: /* Zero channels to copy. */ diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 3ee75ca21ce..b9d013d4756 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -42,6 +42,7 @@ * like M_SQRT1_2 leading to warnings with MSVC */ # include <libavcodec/avcodec.h> # include <libavformat/avformat.h> +# include <libavutil/channel_layout.h> # include <libavutil/imgutils.h> # include <libavutil/opt.h> # include <libavutil/rational.h> @@ -101,8 +102,6 @@ typedef struct FFMpegContext { printf static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value); -static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value); -static void ffmpeg_set_expert_options(RenderData *rd); static void ffmpeg_filepath_get(FFMpegContext *context, char *string, const struct RenderData *rd, @@ -414,99 +413,6 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixe return context->current_frame; } -static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary) -{ - char name[128]; - char *param; - - PRINT("FFMPEG expert option: %s: ", prop->name); - - BLI_strncpy(name, prop->name, sizeof(name)); - - param = strchr(name, ':'); - - if (param) { - *param++ = '\0'; - } - - switch (prop->type) { - case IDP_STRING: - PRINT("%s.\n", IDP_String(prop)); - av_dict_set(dictionary, name, IDP_String(prop), 0); - break; - case IDP_FLOAT: - PRINT("%g.\n", IDP_Float(prop)); - ffmpeg_dict_set_float(dictionary, prop->name, IDP_Float(prop)); - break; - case IDP_INT: - PRINT("%d.\n", IDP_Int(prop)); - - if (param) { - if (IDP_Int(prop)) { - av_dict_set(dictionary, name, param, 0); - } - else { - return; - } - } - else { - ffmpeg_dict_set_int(dictionary, prop->name, IDP_Int(prop)); - } - break; - } -} - -static int ffmpeg_proprty_valid(AVCodecContext *c, const char *prop_name, IDProperty *curr) -{ - int valid = 1; - - if (STREQ(prop_name, "video")) { - if (STREQ(curr->name, "bf")) { - /* flash codec doesn't support b frames */ - valid &= c->codec_id != AV_CODEC_ID_FLV1; - } - } - - return valid; -} - -static void set_ffmpeg_properties(RenderData *rd, - AVCodecContext *c, - const char *prop_name, - AVDictionary **dictionary) -{ - IDProperty *prop; - IDProperty *curr; - - /* TODO(sergey): This is actually rather stupid, because changing - * codec settings in render panel would also set expert options. - * - * But we need ti here in order to get rid of deprecated settings - * when opening old files in new blender. - * - * For as long we don't allow editing properties in the interface - * it's all good. bug if we allow editing them, we'll need to - * replace it with some smarter code which would port settings - * from deprecated to new one. - */ - ffmpeg_set_expert_options(rd); - - if (!rd->ffcodecdata.properties) { - return; - } - - prop = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, prop_name); - if (!prop) { - return; - } - - for (curr = prop->data.group.first; curr; curr = curr->next) { - if (ffmpeg_proprty_valid(c, prop_name, curr)) { - set_ffmpeg_property_option(curr, dictionary); - } - } -} - static AVRational calc_time_base(uint den, double num, int codec_id) { /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer @@ -561,7 +467,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodec *codec; + const AVCodec *codec; AVDictionary *opts = NULL; error[0] = '\0'; @@ -574,21 +480,15 @@ static AVStream *alloc_video_stream(FFMpegContext *context, /* Set up the codec context */ - context->video_codec = avcodec_alloc_context3(NULL); - AVCodecContext *c = context->video_codec; - c->codec_id = codec_id; - c->codec_type = AVMEDIA_TYPE_VIDEO; - - codec = avcodec_find_encoder(c->codec_id); + codec = avcodec_find_encoder(codec_id); if (!codec) { fprintf(stderr, "Couldn't find valid video codec\n"); - avcodec_free_context(&c); context->video_codec = NULL; return NULL; } - /* Load codec defaults into 'c'. */ - avcodec_get_context_defaults3(c, codec); + context->video_codec = avcodec_alloc_context3(codec); + AVCodecContext *c = context->video_codec; /* Get some values from the current render settings */ @@ -702,6 +602,13 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } + if (codec_id == AV_CODEC_ID_DNXHD) { + if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { + /* Set the block decision algorithm to be of the highest quality ("rd" == 2). */ + c->mb_decision = 2; + } + } + if (codec_id == AV_CODEC_ID_FFV1) { c->pix_fmt = AV_PIX_FMT_RGB32; } @@ -738,8 +645,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, 255); st->avg_frame_rate = av_inv_q(c->time_base); - set_ffmpeg_properties(rd, c, "video", &opts); - if (codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { c->thread_count = 0; } @@ -804,8 +709,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodec *codec; - AVDictionary *opts = NULL; + const AVCodec *codec; error[0] = '\0'; @@ -815,24 +719,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, } st->id = 1; - context->audio_codec = avcodec_alloc_context3(NULL); - AVCodecContext *c = context->audio_codec; - c->thread_count = BLI_system_thread_count(); - c->thread_type = FF_THREAD_SLICE; - - c->codec_id = codec_id; - c->codec_type = AVMEDIA_TYPE_AUDIO; - - codec = avcodec_find_encoder(c->codec_id); + codec = avcodec_find_encoder(codec_id); if (!codec) { fprintf(stderr, "Couldn't find valid audio codec\n"); - avcodec_free_context(&c); context->audio_codec = NULL; return NULL; } - /* Load codec defaults into 'c'. */ - avcodec_get_context_defaults3(c, codec); + context->audio_codec = avcodec_alloc_context3(codec); + AVCodecContext *c = context->audio_codec; + c->thread_count = BLI_system_thread_count(); + c->thread_type = FF_THREAD_SLICE; c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; @@ -900,19 +797,15 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } - set_ffmpeg_properties(rd, c, "audio", &opts); - - int ret = avcodec_open2(c, codec, &opts); + int ret = avcodec_open2(c, codec, NULL); if (ret < 0) { fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); - av_dict_free(&opts); avcodec_free_context(&c); context->audio_codec = NULL; return NULL; } - av_dict_free(&opts); /* need to prevent floating point exception when using vorbis audio codec, * initialize this value in the same way as it's done in FFmpeg itself (sergey) */ @@ -958,15 +851,6 @@ static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value) av_dict_set(dict, key, buffer, 0); } -static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value) -{ - char buffer[32]; - - BLI_snprintf(buffer, sizeof(buffer), "%.8f", value); - - av_dict_set(dict, key, buffer, 0); -} - static void ffmpeg_add_metadata_callback(void *data, const char *propname, char *propvalue, @@ -985,8 +869,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, { /* Handle to the output file */ AVFormatContext *of; - AVOutputFormat *fmt; - AVDictionary *opts = NULL; + const AVOutputFormat *fmt; char name[FILE_MAX], error[1024]; const char **exts; @@ -1023,11 +906,13 @@ static int start_ffmpeg_impl(FFMpegContext *context, rectx, recty); + /* Sanity checks for the output file extensions. */ exts = get_file_extensions(context->ffmpeg_type); if (!exts) { BKE_report(reports, RPT_ERROR, "No valid formats found"); return 0; } + fmt = av_guess_format(NULL, exts[0], NULL); if (!fmt) { BKE_report(reports, RPT_ERROR, "No valid formats found"); @@ -1036,66 +921,55 @@ static int start_ffmpeg_impl(FFMpegContext *context, of = avformat_alloc_context(); if (!of) { - BKE_report(reports, RPT_ERROR, "Error opening output file"); + BKE_report(reports, RPT_ERROR, "Can't allocate ffmpeg format context"); return 0; } - /* Returns after this must 'goto fail;' */ - - of->oformat = fmt; - - /* Only bother with setting packet size & mux rate when CRF is not used. */ - if (context->ffmpeg_crf == 0) { - of->packet_size = rd->ffcodecdata.mux_packet_size; - if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate); - } - else { - av_dict_set(&opts, "muxrate", "0", 0); - } - } - - ffmpeg_dict_set_int(&opts, "preload", (int)(0.5 * AV_TIME_BASE)); - - of->max_delay = (int)(0.7 * AV_TIME_BASE); - - fmt->audio_codec = context->ffmpeg_audio_codec; + enum AVCodecID audio_codec = context->ffmpeg_audio_codec; + enum AVCodecID video_codec = context->ffmpeg_codec; of->url = av_strdup(name); - /* set the codec to the user's selection */ + /* Check if we need to force change the codec because of file type codec restrictions */ switch (context->ffmpeg_type) { - case FFMPEG_AVI: - case FFMPEG_MOV: - case FFMPEG_MKV: - fmt->video_codec = context->ffmpeg_codec; - break; case FFMPEG_OGG: - fmt->video_codec = AV_CODEC_ID_THEORA; + video_codec = AV_CODEC_ID_THEORA; break; case FFMPEG_DV: - fmt->video_codec = AV_CODEC_ID_DVVIDEO; + video_codec = AV_CODEC_ID_DVVIDEO; break; case FFMPEG_MPEG1: - fmt->video_codec = AV_CODEC_ID_MPEG1VIDEO; + video_codec = AV_CODEC_ID_MPEG1VIDEO; break; case FFMPEG_MPEG2: - fmt->video_codec = AV_CODEC_ID_MPEG2VIDEO; + video_codec = AV_CODEC_ID_MPEG2VIDEO; break; case FFMPEG_H264: - fmt->video_codec = AV_CODEC_ID_H264; + video_codec = AV_CODEC_ID_H264; break; case FFMPEG_XVID: - fmt->video_codec = AV_CODEC_ID_MPEG4; + video_codec = AV_CODEC_ID_MPEG4; break; case FFMPEG_FLV: - fmt->video_codec = AV_CODEC_ID_FLV1; + video_codec = AV_CODEC_ID_FLV1; break; - case FFMPEG_MPEG4: default: - fmt->video_codec = context->ffmpeg_codec; + /* These containers are not restricted to any specific codec types. + * Currently we expect these to be .avi, .mov, .mkv, and .mp4. + */ + video_codec = context->ffmpeg_codec; break; } - if (fmt->video_codec == AV_CODEC_ID_DVVIDEO) { + + /* Returns after this must 'goto fail;' */ + +# if LIBAVFORMAT_VERSION_MAJOR >= 59 + of->oformat = fmt; +# else + /* *DEPRECATED* 2022/08/01 For FFMPEG (<5.0) remove this else branch and the `ifdef` above. */ + of->oformat = (AVOutputFormat *)fmt; +# endif + + if (video_codec == AV_CODEC_ID_DVVIDEO) { if (rectx != 720) { BKE_report(reports, RPT_ERROR, "Render width has to be 720 pixels for DV!"); goto fail; @@ -1111,7 +985,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, } if (context->ffmpeg_type == FFMPEG_DV) { - fmt->audio_codec = AV_CODEC_ID_PCM_S16LE; + audio_codec = AV_CODEC_ID_PCM_S16LE; if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!"); @@ -1119,9 +993,9 @@ static int start_ffmpeg_impl(FFMpegContext *context, } } - if (fmt->video_codec != AV_CODEC_ID_NONE) { + if (video_codec != AV_CODEC_ID_NONE) { context->video_stream = alloc_video_stream( - context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); + context, rd, video_codec, of, rectx, recty, error, sizeof(error)); PRINT("alloc video stream %p\n", context->video_stream); if (!context->video_stream) { if (error[0]) { @@ -1137,8 +1011,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, } if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - context->audio_stream = alloc_audio_stream( - context, rd, fmt->audio_codec, of, error, sizeof(error)); + context->audio_stream = alloc_audio_stream(context, rd, audio_codec, of, error, sizeof(error)); if (!context->audio_stream) { if (error[0]) { BKE_report(reports, RPT_ERROR, error); @@ -1175,7 +1048,6 @@ static int start_ffmpeg_impl(FFMpegContext *context, context->outfile = of; av_dump_format(of, 0, name, 1); - av_dict_free(&opts); return 1; @@ -1192,7 +1064,6 @@ fail: context->audio_stream = NULL; } - av_dict_free(&opts); avformat_free_context(of); return 0; } @@ -1526,198 +1397,17 @@ void BKE_ffmpeg_end(void *context_v) end_ffmpeg_impl(context, false); } -/* properties */ - -void BKE_ffmpeg_property_del(RenderData *rd, void *type, void *prop_) -{ - struct IDProperty *prop = (struct IDProperty *)prop_; - IDProperty *group; - - if (!rd->ffcodecdata.properties) { - return; - } - - group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type); - if (group && prop) { - IDP_FreeFromGroup(group, prop); - } -} - -static IDProperty *BKE_ffmpeg_property_add(RenderData *rd, - const char *type, - const AVOption *o, - const AVOption *parent) -{ - AVCodecContext c; - IDProperty *group; - IDProperty *prop; - IDPropertyTemplate val; - int idp_type; - char name[256]; - - val.i = 0; - - avcodec_get_context_defaults3(&c, NULL); - - if (!rd->ffcodecdata.properties) { - rd->ffcodecdata.properties = IDP_New(IDP_GROUP, &val, "ffmpeg"); - } - - group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type); - - if (!group) { - group = IDP_New(IDP_GROUP, &val, type); - IDP_AddToGroup(rd->ffcodecdata.properties, group); - } - - if (parent) { - BLI_snprintf(name, sizeof(name), "%s:%s", parent->name, o->name); - } - else { - BLI_strncpy(name, o->name, sizeof(name)); - } - - PRINT("ffmpeg_property_add: %s %s\n", type, name); - - prop = IDP_GetPropertyFromGroup(group, name); - if (prop) { - return prop; - } - - switch (o->type) { - case AV_OPT_TYPE_INT: - case AV_OPT_TYPE_INT64: - val.i = o->default_val.i64; - idp_type = IDP_INT; - break; - case AV_OPT_TYPE_DOUBLE: - case AV_OPT_TYPE_FLOAT: - val.f = o->default_val.dbl; - idp_type = IDP_FLOAT; - break; - case AV_OPT_TYPE_STRING: - val.string.str = - (char - *)" "; - val.string.len = 80; - idp_type = IDP_STRING; - break; - case AV_OPT_TYPE_CONST: - val.i = 1; - idp_type = IDP_INT; - break; - default: - return NULL; - } - prop = IDP_New(idp_type, &val, name); - IDP_AddToGroup(group, prop); - return prop; -} - -/* not all versions of ffmpeg include that, so here we go ... */ - -int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char *str) -{ - AVCodecContext c; - const AVOption *o = NULL; - const AVOption *p = NULL; - char name_[128]; - char *name; - char *param; - IDProperty *prop = NULL; - - avcodec_get_context_defaults3(&c, NULL); - - BLI_strncpy(name_, str, sizeof(name_)); - - name = name_; - while (*name == ' ') { - name++; - } - - param = strchr(name, ':'); - - if (!param) { - param = strchr(name, ' '); - } - if (param) { - *param++ = '\0'; - while (*param == ' ') { - param++; - } - } - - o = av_opt_find(&c, name, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ); - if (!o) { - PRINT("Ignoring unknown expert option %s\n", str); - return 0; - } - if (param && o->type == AV_OPT_TYPE_CONST) { - return 0; - } - if (param && o->type != AV_OPT_TYPE_CONST && o->unit) { - p = av_opt_find(&c, param, o->unit, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ); - if (p) { - prop = BKE_ffmpeg_property_add(rd, (char *)type, p, o); - } - else { - PRINT("Ignoring unknown expert option %s\n", str); - } - } - else { - prop = BKE_ffmpeg_property_add(rd, (char *)type, o, NULL); - } - - if (!prop) { - return 0; - } - - if (param && !p) { - switch (prop->type) { - case IDP_INT: - IDP_Int(prop) = atoi(param); - break; - case IDP_FLOAT: - IDP_Float(prop) = atof(param); - break; - case IDP_STRING: - strncpy(IDP_String(prop), param, prop->len); - break; - } - } - return 1; -} - -static void ffmpeg_set_expert_options(RenderData *rd) -{ - int codec_id = rd->ffcodecdata.codec; - - if (rd->ffcodecdata.properties) { - IDP_FreePropertyContent(rd->ffcodecdata.properties); - } - - if (codec_id == AV_CODEC_ID_DNXHD) { - if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { - BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); - } - } -} - void BKE_ffmpeg_preset_set(RenderData *rd, int preset) { - int isntsc = (rd->frs_sec != 25); - - if (rd->ffcodecdata.properties) { - IDP_FreePropertyContent(rd->ffcodecdata.properties); - } + bool is_ntsc = (rd->frs_sec != 25); switch (preset) { case FFMPEG_PRESET_VCD: rd->ffcodecdata.type = FFMPEG_MPEG1; rd->ffcodecdata.video_bitrate = 1150; rd->xsch = 352; - rd->ysch = isntsc ? 240 : 288; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ysch = is_ntsc ? 240 : 288; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 1150; rd->ffcodecdata.rc_min_rate = 1150; rd->ffcodecdata.rc_buffer_size = 40 * 8; @@ -1729,8 +1419,8 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.type = FFMPEG_MPEG2; rd->ffcodecdata.video_bitrate = 2040; rd->xsch = 480; - rd->ysch = isntsc ? 480 : 576; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ysch = is_ntsc ? 480 : 576; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 2516; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1747,7 +1437,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ysch = isntsc ? 480 : 576; # endif - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1758,14 +1448,14 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) case FFMPEG_PRESET_DV: rd->ffcodecdata.type = FFMPEG_DV; rd->xsch = 720; - rd->ysch = isntsc ? 480 : 576; + rd->ysch = is_ntsc ? 480 : 576; break; case FFMPEG_PRESET_H264: rd->ffcodecdata.type = FFMPEG_AVI; rd->ffcodecdata.codec = AV_CODEC_ID_H264; rd->ffcodecdata.video_bitrate = 6000; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1786,7 +1476,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) } rd->ffcodecdata.video_bitrate = 6000; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1794,8 +1484,6 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.mux_rate = 10080000; break; } - - ffmpeg_set_expert_options(rd); } void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) @@ -1841,11 +1529,6 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) } } -void BKE_ffmpeg_codec_settings_verify(RenderData *rd) -{ - ffmpeg_set_expert_options(rd); -} - bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd) { int codec = rd->ffcodecdata.codec; diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index d3bb3401d7e..80e865ded62 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -146,6 +146,17 @@ void _bli_array_grow_func(void **arr_p, */ #define BLI_array_fake_user(arr) ((void)_##arr##_len, (void)_##arr##_static) +/** + * Trim excess items from the array (when they exist). + */ +#define BLI_array_trim(arr) \ + { \ + if (_bli_array_totalsize_dynamic(arr) != _##arr##_len) { \ + arr = MEM_reallocN(arr, sizeof(*arr) * _##arr##_len); \ + } \ + } \ + ((void)0) + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index a580f12851e..91dfc81ae27 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -278,18 +278,20 @@ class Array { } /** - * Return a reference to the last element in the array. - * This invokes undefined behavior when the array is empty. + * Return a reference to the nth last element. + * This invokes undefined behavior when the array is too short. */ - const T &last() const + const T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return *(data_ + size_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < size_); + return *(data_ + size_ - 1 - n); } - T &last() + T &last(const int64_t n = 0) { - BLI_assert(size_ > 0); - return *(data_ + size_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < size_); + return *(data_ + size_ - 1 - n); } /** diff --git a/source/blender/blenlib/BLI_bounds.hh b/source/blender/blenlib/BLI_bounds.hh new file mode 100644 index 00000000000..d20382ed500 --- /dev/null +++ b/source/blender/blenlib/BLI_bounds.hh @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * Generic algorithms for finding the largest and smallest elements in a span. + */ + +#include <optional> + +#include "BLI_math_vector.hh" +#include "BLI_task.hh" + +namespace blender::bounds { + +template<typename T> struct MinMaxResult { + T min; + T max; +}; + +/** + * Find the smallest and largest values element-wise in the span. + */ +template<typename T> static std::optional<MinMaxResult<T>> min_max(Span<T> values) +{ + if (values.is_empty()) { + return std::nullopt; + } + return threading::parallel_reduce( + values.index_range(), + 1024, + MinMaxResult<T>(), + [&](IndexRange range, const MinMaxResult<T> &init) { + MinMaxResult<T> result = init; + for (const int i : range) { + math::min_max(values[i], result.min, result.max); + } + return result; + }, + [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) { + return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)}; + }); +} + +/** + * Find the smallest and largest values element-wise in the span, adding the radius to each element + * first. The template type T is expected to have an addition operator implemented with RadiusT. + */ +template<typename T, typename RadiusT> +static std::optional<MinMaxResult<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii) +{ + BLI_assert(values.size() == radii.size()); + if (values.is_empty()) { + return std::nullopt; + } + return threading::parallel_reduce( + values.index_range(), + 1024, + MinMaxResult<T>(), + [&](IndexRange range, const MinMaxResult<T> &init) { + MinMaxResult<T> result = init; + for (const int i : range) { + result.min = math::min(values[i] - radii[i], result.min); + result.max = math::max(values[i] + radii[i], result.max); + } + return result; + }, + [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) { + return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)}; + }); +} + +} // namespace blender::bounds diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index 339f02dce0f..51bf8d06cf1 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -3,7 +3,25 @@ #pragma once #ifdef WITH_TBB -# include <tbb/enumerable_thread_specific.h> + +# ifdef WITH_TBB +/* Quiet top level deprecation message, unrelated to API usage here. */ +# if defined(WIN32) && !defined(NOMINMAX) +/* TBB includes Windows.h which will define min/max macros causing issues + * when we try to use std::min and std::max later on. */ +# define NOMINMAX +# define TBB_MIN_MAX_CLEANUP +# endif +# include <tbb/enumerable_thread_specific.h> +# ifdef WIN32 +/* We cannot keep this defined, since other parts of the code deal with this on their own, leading + * to multiple define warnings unless we un-define this, however we can only undefine this if we + * were the ones that made the definition earlier. */ +# ifdef TBB_MIN_MAX_CLEANUP +# undef NOMINMAX +# endif +# endif +# endif #endif #include <atomic> diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh index 40b20dbd84f..334634613a2 100644 --- a/source/blender/blenlib/BLI_hash_tables.hh +++ b/source/blender/blenlib/BLI_hash_tables.hh @@ -8,6 +8,7 @@ * This file contains code that can be shared between different hash table implementations. */ +#include <algorithm> #include <cmath> #include "BLI_allocator.hh" diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 7f4e7be543b..3decd8b9441 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -209,6 +209,18 @@ class IndexMask { return indices_.is_empty(); } + bool contained_in(const IndexRange range) const + { + if (indices_.is_empty()) { + return true; + } + if (range.size() < indices_.size()) { + return false; + } + return indices_.first() >= range.first() && indices_.last() <= range.last(); + } + + IndexMask slice(int64_t start, int64_t size) const; IndexMask slice(IndexRange slice) const; /** * Create a sub-mask that is also shifted to the beginning. @@ -229,6 +241,30 @@ class IndexMask { * so that the first index in the output is zero. */ IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const; + + /** + * Get a new mask that contains all the indices that are not in the current mask. + * If necessary, the indices referenced by the new mask are inserted in #r_new_indices. + */ + IndexMask invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const; + + /** + * Get all contiguous index ranges within the mask. + */ + Vector<IndexRange> extract_ranges() const; + + /** + * Similar to #extract ranges, but works on the inverted mask. So the returned ranges are + * in-between the indices in the mask. + * + * Using this method is generally more efficient than first inverting the index mask and then + * extracting the ranges. + * + * If #r_skip_amounts is passed in, it will contain the number of indices that have been skipped + * before each range in the return value starts. + */ + Vector<IndexRange> extract_ranges_invert(const IndexRange full_range, + Vector<int64_t> *r_skip_amounts) const; }; } // namespace blender diff --git a/source/blender/blenlib/BLI_index_mask_ops.hh b/source/blender/blenlib/BLI_index_mask_ops.hh new file mode 100644 index 00000000000..48a1f27a2fa --- /dev/null +++ b/source/blender/blenlib/BLI_index_mask_ops.hh @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * This is separate from `BLI_index_mask.hh` because it includes headers just `IndexMask` shouldn't + * depend on. + */ + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_index_mask.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" + +namespace blender::index_mask_ops { + +namespace detail { +IndexMask find_indices_based_on_predicate__merge( + IndexMask indices_to_check, + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks, + Vector<int64_t> &r_indices); +} // namespace detail + +/** + * Evaluate the #predicate for all indices in #indices_to_check and return a mask that contains all + * indices where the predicate was true. + * + * #r_indices indices is only used if necessary. + */ +template<typename Predicate> +inline IndexMask find_indices_based_on_predicate(const IndexMask indices_to_check, + const int64_t parallel_grain_size, + Vector<int64_t> &r_indices, + const Predicate &predicate) +{ + /* Evaluate predicate in parallel. Since the size of the final mask is not known yet, many + * smaller vectors have to be filled with all indices where the predicate is true. Those smaller + * vectors are joined afterwards. */ + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> sub_masks; + threading::parallel_for( + indices_to_check.index_range(), parallel_grain_size, [&](const IndexRange range) { + const IndexMask sub_mask = indices_to_check.slice(range); + Vector<int64_t> masked_indices; + for (const int64_t i : sub_mask) { + if (predicate(i)) { + masked_indices.append(i); + } + } + if (!masked_indices.is_empty()) { + sub_masks.local().append(std::move(masked_indices)); + } + }); + + /* This part doesn't have to be in the header. */ + return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices); +} + +} // namespace blender::index_mask_ops diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh new file mode 100644 index 00000000000..6a988eda8a9 --- /dev/null +++ b/source/blender/blenlib/BLI_math_base.hh @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include <algorithm> +#include <cmath> +#include <type_traits> + +#include "BLI_math_base_safe.h" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +#endif + +namespace blender::math { + +template<typename T> inline bool is_zero(const T &a) +{ + return a == T(0); +} + +template<typename T> inline bool is_any_zero(const T &a) +{ + return is_zero(a); +} + +template<typename T> inline T abs(const T &a) +{ + return std::abs(a); +} + +template<typename T> inline T min(const T &a, const T &b) +{ + return std::min(a, b); +} + +template<typename T> inline T max(const T &a, const T &b) +{ + return std::max(a, b); +} + +template<typename T> inline T clamp(const T &a, const T &min, const T &max) +{ + return std::clamp(a, min, max); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T mod(const T &a, const T &b) +{ + return std::fmod(a, b); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const T &a, const T &b) +{ + return (b != 0) ? std::fmod(a, b) : 0; +} + +template<typename T> inline void min_max(const T &value, T &min, T &max) +{ + min = math::min(value, min); + max = math::max(value, max); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_divide(const T &a, const T &b) +{ + return (b != 0) ? a / b : T(0.0f); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T floor(const T &a) +{ + return std::floor(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T ceil(const T &a) +{ + return std::ceil(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(const T &a) +{ + return a - std::floor(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T interpolate(const T &a, const T &b, const T &t) +{ + return a * (1 - t) + b * t; +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T midpoint(const T &a, const T &b) +{ + return (a + b) * T(0.5); +} + +} // namespace blender::math diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 3d2ac5688ff..4bba84f2e29 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -303,6 +303,9 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4], const float bbmin[3], const float bbmax[3]); +/** Returns the distance between two 2D line segments. */ +float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3]); + float closest_to_ray_v3(float r_close[3], const float p[3], const float ray_orig[3], diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index 8e897870098..389307e331d 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -14,6 +14,10 @@ #include "BLI_utildefines.h" +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +#endif + namespace blender { /* clang-format off */ @@ -60,16 +64,6 @@ template<typename T> uint64_t vector_hash(const T &vec) return result; } -template<typename T> inline bool is_any_zero(const T &a) -{ - for (int i = 0; i < T::type_length; i++) { - if (a[i] == T::base_type(0)) { - return true; - } - } - return false; -} - } // namespace math template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> { @@ -349,7 +343,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> friend vec_base operator/(const vec_base &a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] / b[i]); } @@ -361,7 +357,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> friend vec_base operator/(T a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a / b[i]); } @@ -505,7 +503,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> BLI_INT_OP(T) friend vec_base operator%(const vec_base &a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] % b[i]); } @@ -579,4 +579,13 @@ using double2 = vec_base<double, 2>; using double3 = vec_base<double, 3>; using double4 = vec_base<double, 4>; +template<typename T> +inline constexpr bool is_math_float_type = (std::is_floating_point_v<T> +#ifdef WITH_GMP + || std::is_same_v<T, mpq_class> +#endif +); + +template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>; + } // namespace blender diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh index d2ef2a1c5c8..7c848eeb145 100644 --- a/source/blender/blenlib/BLI_math_vector.hh +++ b/source/blender/blenlib/BLI_math_vector.hh @@ -15,10 +15,6 @@ #include "BLI_span.hh" #include "BLI_utildefines.h" -#ifdef WITH_GMP -# include "BLI_math_mpq.hh" -#endif - namespace blender::math { #ifndef NDEBUG @@ -33,277 +29,303 @@ namespace blender::math { # define BLI_ASSERT_UNIT(v) (void)(v) #endif -#define bT typename T::base_type - -#ifdef WITH_GMP -# define BLI_ENABLE_IF_FLT_VEC(T) \ - BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type> || \ - std::is_same_v<typename T::base_type, mpq_class>)) -#else -# define BLI_ENABLE_IF_FLT_VEC(T) BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type>)) -#endif - -#define BLI_ENABLE_IF_INT_VEC(T) BLI_ENABLE_IF((std::is_integral_v<typename T::base_type>)) - -template<typename T> inline bool is_zero(const T &a) +template<typename T, int Size> inline bool is_zero(const vec_base<T, Size> &a) { - for (int i = 0; i < T::type_length; i++) { - if (a[i] != bT(0)) { + for (int i = 0; i < Size; i++) { + if (a[i] != T(0)) { return false; } } return true; } -template<typename T> inline T abs(const T &a) +template<typename T, int Size> inline bool is_any_zero(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + for (int i = 0; i < Size; i++) { + if (a[i] == T(0)) { + return true; + } + } + return false; +} + +template<typename T, int Size> inline vec_base<T, Size> abs(const vec_base<T, Size> &a) +{ + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] >= 0 ? a[i] : -a[i]; } return result; } -template<typename T> inline T min(const T &a, const T &b) +template<typename T, int Size> +inline vec_base<T, Size> min(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] < b[i] ? a[i] : b[i]; } return result; } -template<typename T> inline T max(const T &a, const T &b) +template<typename T, int Size> +inline vec_base<T, Size> max(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] > b[i] ? a[i] : b[i]; } return result; } -template<typename T> inline T clamp(const T &a, const T &min_v, const T &max_v) +template<typename T, int Size> +inline T clamp(const vec_base<T, Size> &a, + const vec_base<T, Size> &min, + const vec_base<T, Size> &max) { - T result = a; - for (int i = 0; i < T::type_length; i++) { - CLAMP(result[i], min_v[i], max_v[i]); + vec_base<T, Size> result = a; + for (int i = 0; i < Size; i++) { + std::clamp(result[i], min[i], max[i]); } return result; } -template<typename T> inline T clamp(const T &a, const bT &min_v, const bT &max_v) +template<typename T, int Size> +inline vec_base<T, Size> clamp(const vec_base<T, Size> &a, const T &min, const T &max) { - T result = a; - for (int i = 0; i < T::type_length; i++) { - CLAMP(result[i], min_v, max_v); + vec_base<T, Size> result = a; + for (int i = 0; i < Size; i++) { + std::clamp(result[i], min, max); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { BLI_assert(b[i] != 0); result[i] = std::fmod(a[i], b[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const T &b) { BLI_assert(b != 0); - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = (b[i] != 0) ? std::fmod(a[i], b[i]) : 0; } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const vec_base<T, Size> &a, const T &b) { if (b == 0) { - return T(0.0f); + return vec_base<T, Size>(0); } - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } -template<typename T> inline void min_max(const T &vector, T &min_vec, T &max_vec) +template<typename T, int Size> +inline void min_max(const vec_base<T, Size> &vector, + vec_base<T, Size> &min, + vec_base<T, Size> &max) { - min_vec = min(vector, min_vec); - max_vec = max(vector, max_vec); + min = math::min(vector, min); + max = math::max(vector, max); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = (b[i] == 0) ? 0 : a[i] / b[i]; } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const T &b) { - return (b != 0) ? a / b : T(0.0f); + return (b != 0) ? a / b : vec_base<T, Size>(0.0f); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T floor(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> floor(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::floor(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T ceil(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> ceil(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::ceil(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T fract(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> fract(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] - std::floor(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT dot(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T dot(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - bT result = a[0] * b[0]; - for (int i = 1; i < T::type_length; i++) { + T result = a[0] * b[0]; + for (int i = 1; i < Size; i++) { result += a[i] * b[i]; } return result; } -template<typename T> inline bT length_manhattan(const T &a) +template<typename T, int Size> inline T length_manhattan(const vec_base<T, Size> &a) { - bT result = std::abs(a[0]); - for (int i = 1; i < T::type_length; i++) { + T result = std::abs(a[0]); + for (int i = 1; i < Size; i++) { result += std::abs(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length_squared(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T length_squared(const vec_base<T, Size> &a) { return dot(a, a); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T length(const vec_base<T, Size> &a) { return std::sqrt(length_squared(a)); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_manhattan(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance_manhattan(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length_manhattan(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_squared(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance_squared(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length_squared(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T reflect(const T &incident, const T &normal) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> reflect(const vec_base<T, Size> &incident, + const vec_base<T, Size> &normal) { BLI_ASSERT_UNIT(normal); return incident - 2.0 * dot(normal, incident) * normal; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T refract(const T &incident, const T &normal, const bT eta) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> refract(const vec_base<T, Size> &incident, + const vec_base<T, Size> &normal, + const T &eta) { float dot_ni = dot(normal, incident); float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni); if (k < 0.0f) { - return T(0.0f); + return vec_base<T, Size>(0.0f); } return eta * incident - (eta * dot_ni + sqrt(k)) * normal; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T project(const T &p, const T &v_proj) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> project(const vec_base<T, Size> &p, const vec_base<T, Size> &v_proj) { if (UNLIKELY(is_zero(v_proj))) { - return T(0.0f); + return vec_base<T, Size>(0.0f); } return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj)); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T normalize_and_get_length(const T &v, bT &out_length) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> normalize_and_get_length(const vec_base<T, Size> &v, T &out_length) { out_length = length_squared(v); /* A larger value causes normalize errors in a scaled down models with camera extreme close. */ - constexpr bT threshold = std::is_same_v<bT, double> ? 1.0e-70 : 1.0e-35f; + constexpr T threshold = std::is_same_v<T, double> ? 1.0e-70 : 1.0e-35f; if (out_length > threshold) { out_length = sqrt(out_length); return v / out_length; } /* Either the vector is small or one of it's values contained `nan`. */ out_length = 0.0; - return T(0.0); + return vec_base<T, Size>(0.0); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T normalize(const T &v) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> normalize(const vec_base<T, Size> &v) { - bT len; + T len; return normalize_and_get_length(v, len); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))> -inline T cross(const T &a, const T &b) +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, 3> cross(const vec_base<T, 3> &a, const vec_base<T, 3> &b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; } -template<typename T, - BLI_ENABLE_IF((std::is_same_v<bT, float>)), - BLI_ENABLE_IF((T::type_length == 3))> -inline T cross_high_precision(const T &a, const T &b) +inline vec_base<float, 3> cross_high_precision(const vec_base<float, 3> &a, + const vec_base<float, 3> &b) { return {(float)((double)a.y * b.z - (double)a.z * b.y), (float)((double)a.z * b.x - (double)a.x * b.z), (float)((double)a.x * b.y - (double)a.y * b.x)}; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))> -inline T cross_poly(Span<T> poly) +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, 3> cross_poly(Span<vec_base<T, 3>> poly) { /* Newell's Method. */ int nv = static_cast<int>(poly.size()); if (nv < 3) { - return T(0, 0, 0); + return vec_base<T, 3>(0, 0, 0); } - const T *v_prev = &poly[nv - 1]; - const T *v_curr = &poly[0]; - T n(0, 0, 0); + const vec_base<T, 3> *v_prev = &poly[nv - 1]; + const vec_base<T, 3> *v_curr = &poly[0]; + vec_base<T, 3> n(0, 0, 0); for (int i = 0; i < nv;) { n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); @@ -317,25 +339,31 @@ inline T cross_poly(Span<T> poly) return n; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T interpolate(const T &a, const T &b, bT t) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> interpolate(const vec_base<T, Size> &a, + const vec_base<T, Size> &b, + const T &t) { return a * (1 - t) + b * t; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T midpoint(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> midpoint(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return (a + b) * 0.5; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T faceforward(const T &vector, const T &incident, const T &reference) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> faceforward(const vec_base<T, Size> &vector, + const vec_base<T, Size> &incident, + const vec_base<T, Size> &reference) { return (dot(reference, incident) < 0) ? vector : -vector; } -template<typename T> inline int dominant_axis(const T &a) +template<typename T> inline int dominant_axis(const vec_base<T, 3> &a) { - T b = abs(a); + vec_base<T, 3> b = abs(a); return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2)); } @@ -348,14 +376,13 @@ template<typename T> struct isect_result { LINE_LINE_EXACT = 1, LINE_LINE_CROSS = 2, } kind; - bT lambda; + typename T::base_type lambda; }; -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -isect_result<T> isect_seg_seg(const T &v1, const T &v2, const T &v3, const T &v4); - -#undef BLI_ENABLE_IF_FLT_VEC -#undef BLI_ENABLE_IF_INT_VEC -#undef bT +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +isect_result<vec_base<T, Size>> isect_seg_seg(const vec_base<T, Size> &v1, + const vec_base<T, Size> &v2, + const vec_base<T, Size> &v3, + const vec_base<T, Size> &v4); } // namespace blender::math diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index dd6b8463d84..a7cad5461b4 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -544,3 +544,31 @@ Container &move_assign_container(Container &dst, Container &&src) noexcept( } } // namespace blender + +namespace blender::detail { + +template<typename Func> struct ScopedDeferHelper { + Func func; + + ~ScopedDeferHelper() + { + func(); + } +}; + +} // namespace blender::detail + +#define BLI_SCOPED_DEFER_NAME1(a, b) a##b +#define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b) +#define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__) + +/** + * Execute the given function when the current scope ends. This can be used to cheaply implement + * some RAII-like behavior for C types that don't support it. Long term, the types we want to use + * this with should either be converted to C++ or get a proper C++ API. Until then, this function + * can help avoid common resource leakages. + */ +#define BLI_SCOPED_DEFER(function_to_defer) \ + auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \ + blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \ + BLI_SCOPED_DEFER_NAME(helper){std::move(BLI_SCOPED_DEFER_NAME(func))}; diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index b4427b1dc2a..7b3e3e983f0 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -298,7 +298,7 @@ bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(); bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL(); /** * Replaces "#" character sequence in last slash-separated component of `path` - * with frame as decimal integer, with leading zeroes as necessary, to make digits digits. + * with frame as decimal integer, with leading zeroes as necessary, to make digits. */ bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL(); /** diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index d82f21a57ff..9ab096094de 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -307,13 +307,14 @@ template<typename T> class Span { } /** - * Returns a reference to the last element in the array. This invokes undefined behavior when the - * array is empty. + * Returns a reference to the nth last element. This invokes undefined behavior when the span is + * too short. */ - constexpr const T &last() const + constexpr const T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return data_[size_ - 1]; + BLI_assert(n >= 0); + BLI_assert(n < size_); + return data_[size_ - 1 - n]; } /** @@ -673,13 +674,14 @@ template<typename T> class MutableSpan { } /** - * Returns a reference to the last element. This invokes undefined behavior when the array is - * empty. + * Returns a reference to the nth last element. This invokes undefined behavior when the span is + * too short. */ - constexpr T &last() const + constexpr T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return data_[size_ - 1]; + BLI_assert(n >= 0); + BLI_assert(n < size_); + return data_[size_ - 1 - n]; } /** diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index d5d33b8a000..da9ab9c313e 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -639,18 +639,20 @@ class Vector { } /** - * Return a reference to the last element in the vector. - * This invokes undefined behavior when the vector is empty. + * Return a reference to the nth last element. + * This invokes undefined behavior when the vector is too short. */ - const T &last() const + const T &last(const int64_t n = 0) const { - BLI_assert(this->size() > 0); - return *(end_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < this->size()); + return *(end_ - 1 - n); } - T &last() + T &last(const int64_t n = 0) { - BLI_assert(this->size() > 0); - return *(end_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < this->size()); + return *(end_ - 1 - n); } /** diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index d697590b946..16fd706c99d 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -667,7 +667,7 @@ template<typename T> class VArrayCommon { } /** - * Returns the internally used span of the virtual array. This invokes undefined behavior is the + * Returns the internally used span of the virtual array. This invokes undefined behavior if the * virtual array is not stored as a span internally. */ Span<T> get_internal_span() const diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 29015084679..6e3e84f6495 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -162,6 +162,7 @@ set(SRC BLI_bitmap_draw_2d.h BLI_blenlib.h BLI_boxpack_2d.h + BLI_bounds.hh BLI_buffer.h BLI_color.hh BLI_compiler_attrs.h @@ -202,6 +203,7 @@ set(SRC BLI_heap.h BLI_heap_simple.h BLI_index_mask.hh + BLI_index_mask_ops.hh BLI_index_range.hh BLI_inplace_priority_queue.hh BLI_iterator.h @@ -220,6 +222,7 @@ set(SRC BLI_map.hh BLI_map_slots.hh BLI_math.h + BLI_math_base.hh BLI_math_base.h BLI_math_base_safe.h BLI_math_bits.h @@ -238,6 +241,7 @@ set(SRC BLI_math_vec_mpq_types.hh BLI_math_vec_types.hh BLI_math_vector.h + BLI_math_vector.hh BLI_memarena.h BLI_memblock.h BLI_memiter.h @@ -306,6 +310,9 @@ set(SRC BLI_winstuff.h PIL_time.h PIL_time_utildefines.h + + # Without these files listed, they aren't known to CMake. + ../../../extern/json/include/json.hpp ) set(LIB @@ -394,6 +401,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bounds_test.cc tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index cb0ba763c94..b7dbd7d679c 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -1691,7 +1691,7 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco, BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); UNUSED_VARS_NDEBUG(vc); - auto isect = isect_seg_seg<vec2<T>>(va->co.exact, vb->co.exact, curco.exact, v2->co.exact); + auto isect = isect_seg_seg(va->co.exact, vb->co.exact, curco.exact, v2->co.exact); T &lambda = isect.lambda; switch (isect.kind) { case isect_result<vec2<T>>::LINE_LINE_CROSS: { @@ -2556,10 +2556,10 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state) if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) { continue; /* Don't count hits on edges between faces in same region. */ } - auto isect = isect_seg_seg<vec2<T>>(ray_end.exact, - mid.exact, - e->symedges[0].vert->co.exact, - e->symedges[1].vert->co.exact); + auto isect = isect_seg_seg(ray_end.exact, + mid.exact, + e->symedges[0].vert->co.exact, + e->symedges[1].vert->co.exact); switch (isect.kind) { case isect_result<vec2<T>>::LINE_LINE_CROSS: { hits++; diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc index b55de6a9264..1e301bc5fb9 100644 --- a/source/blender/blenlib/intern/index_mask.cc +++ b/source/blender/blenlib/intern/index_mask.cc @@ -1,9 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_index_mask.hh" +#include "BLI_index_mask_ops.hh" namespace blender { +IndexMask IndexMask::slice(int64_t start, int64_t size) const +{ + return this->slice(IndexRange(start, size)); +} + IndexMask IndexMask::slice(IndexRange slice) const { return IndexMask(indices_.slice(slice)); @@ -30,4 +36,161 @@ IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r return IndexMask(r_new_indices.as_span()); } +IndexMask IndexMask::invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const +{ + BLI_assert(this->contained_in(full_range)); + if (full_range.size() == indices_.size()) { + return {}; + } + if (indices_.is_empty()) { + return full_range; + } + r_new_indices.clear(); + + const Vector<IndexRange> ranges = this->extract_ranges_invert(full_range, nullptr); + for (const IndexRange &range : ranges) { + for (const int64_t index : range) { + r_new_indices.append(index); + } + } + return r_new_indices.as_span(); +} + +Vector<IndexRange> IndexMask::extract_ranges() const +{ + Vector<IndexRange> ranges; + int64_t range_start = 0; + while (range_start < indices_.size()) { + int64_t current_range_end = range_start + 1; + int64_t step_size = 1; + + while (true) { + const int64_t possible_range_end = current_range_end + step_size; + if (possible_range_end > indices_.size()) { + break; + } + if (!this->slice(range_start, possible_range_end - range_start).is_range()) { + break; + } + current_range_end = possible_range_end; + step_size *= 2; + } + + /* This step size was tried already, no need to try it again. */ + step_size /= 2; + + while (step_size > 0) { + const int64_t possible_range_end = current_range_end + step_size; + step_size /= 2; + if (possible_range_end > indices_.size()) { + continue; + } + if (!this->slice(range_start, possible_range_end - range_start).is_range()) { + continue; + } + current_range_end = possible_range_end; + } + + ranges.append(IndexRange{indices_[range_start], current_range_end - range_start}); + range_start = current_range_end; + } + return ranges; +} + +Vector<IndexRange> IndexMask::extract_ranges_invert(const IndexRange full_range, + Vector<int64_t> *r_skip_amounts) const +{ + BLI_assert(this->contained_in(full_range)); + const Vector<IndexRange> ranges = this->extract_ranges(); + Vector<IndexRange> inverted_ranges; + + int64_t skip_amount = 0; + int64_t next_start = full_range.start(); + for (const int64_t i : ranges.index_range()) { + const IndexRange range = ranges[i]; + if (range.start() > next_start) { + inverted_ranges.append({next_start, range.start() - next_start}); + if (r_skip_amounts != nullptr) { + r_skip_amounts->append(skip_amount); + } + } + next_start = range.one_after_last(); + skip_amount += range.size(); + } + if (next_start < full_range.one_after_last()) { + inverted_ranges.append({next_start, full_range.one_after_last() - next_start}); + if (r_skip_amounts != nullptr) { + r_skip_amounts->append(skip_amount); + } + } + return inverted_ranges; +} + } // namespace blender + +namespace blender::index_mask_ops::detail { + +IndexMask find_indices_based_on_predicate__merge( + IndexMask indices_to_check, + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks, + Vector<int64_t> &r_indices) +{ + /* Gather vectors that have been generated by possibly multiple threads. */ + Vector<Vector<int64_t> *> all_vectors; + int64_t result_mask_size = 0; + for (Vector<Vector<int64_t>> &local_sub_masks : sub_masks) { + for (Vector<int64_t> &sub_mask : local_sub_masks) { + all_vectors.append(&sub_mask); + result_mask_size += sub_mask.size(); + } + } + + if (all_vectors.is_empty()) { + /* Special case when the predicate was false for all elements. */ + return {}; + } + if (result_mask_size == indices_to_check.size()) { + /* Special case when the predicate was true for all elements. */ + return indices_to_check; + } + if (all_vectors.size() == 1) { + /* Special case when all indices for which the predicate is true happen to be in a single + * vector. */ + r_indices = std::move(*all_vectors[0]); + return r_indices.as_span(); + } + + /* Indices in separate vectors don't overlap. So it is ok to sort the vectors just by looking at + * the first element. */ + std::sort(all_vectors.begin(), + all_vectors.end(), + [](const Vector<int64_t> *a, const Vector<int64_t> *b) { return (*a)[0] < (*b)[0]; }); + + /* Precompute the offsets for the individual vectors, so that the indices can be copied into the + * final vector in parallel. */ + Vector<int64_t> offsets; + offsets.reserve(all_vectors.size() + 1); + offsets.append(0); + for (Vector<int64_t> *vector : all_vectors) { + offsets.append(offsets.last() + vector->size()); + } + + r_indices.resize(result_mask_size); + + /* Fill the final index mask in parallel again. */ + threading::parallel_for(all_vectors.index_range(), 100, [&](const IndexRange all_vectors_range) { + for (const int64_t vector_index : all_vectors_range) { + Vector<int64_t> &vector = *all_vectors[vector_index]; + const int64_t offset = offsets[vector_index]; + threading::parallel_for(vector.index_range(), 1024, [&](const IndexRange range) { + initialized_copy_n(vector.data() + range.start(), + range.size(), + r_indices.data() + offset + range.start()); + }); + } + }); + + return r_indices.as_span(); +} + +} // namespace blender::index_mask_ops::detail diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index f96c80185b1..bc3ed099fd5 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -903,6 +903,18 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4], /** \} */ +float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3]) +{ + if (isect_seg_seg_v2_simple(a1, a2, b1, b2)) { + return 0.0f; + } + const float d1 = dist_squared_to_line_segment_v2(a1, b1, b2); + const float d2 = dist_squared_to_line_segment_v2(a2, b1, b2); + const float d3 = dist_squared_to_line_segment_v2(b1, a1, a2); + const float d4 = dist_squared_to_line_segment_v2(b2, a1, a2); + return sqrtf(min_ffff(d1, d2, d3, d4)); +} + void closest_on_tri_to_point_v3( float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3]) { diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 6e2e9787ebe..70030fc2bdf 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -2185,7 +2185,7 @@ static void finish_patch_cell_graph(const IMesh &tm, * There will be a vector of \a nshapes winding numbers in each cell, one per * input shape. * As one crosses a patch into a new cell, the original shape (mesh part) - * that that patch was part of dictates which winding number changes. + * that patch was part of dictates which winding number changes. * The shape_fn(triangle_number) function should return the shape that the * triangle is part of. * Also, as soon as the winding numbers for a cell are set, use bool_optype diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 976c1b0226f..75fa628e701 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -307,8 +307,9 @@ size_t BLI_str_unescape_ex(char *__restrict dst, { size_t len = 0; bool is_complete = true; + const size_t max_strlen = dst_maxncpy - 1; /* Account for trailing zero byte. */ for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { - if (UNLIKELY(len == dst_maxncpy)) { + if (UNLIKELY(len == max_strlen)) { is_complete = false; break; } diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index 6d12b54099a..74eeb5e4e5e 100644 --- a/source/blender/blenlib/tests/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -231,9 +231,11 @@ TEST(array, Last) { Array<int> array = {5, 7, 8, 9}; EXPECT_EQ(array.last(), 9); + EXPECT_EQ(array.last(1), 8); array.last() = 1; EXPECT_EQ(array[3], 1); EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1); + EXPECT_EQ(const_cast<const Array<int> &>(array).last(2), 7); } TEST(array, Reinitialize) diff --git a/source/blender/blenlib/tests/BLI_bounds_test.cc b/source/blender/blenlib/tests/BLI_bounds_test.cc new file mode 100644 index 00000000000..9c123d4705c --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bounds_test.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "BLI_math_base.hh" + +#include "BLI_array.hh" +#include "BLI_bounds.hh" + +namespace blender::tests { + +TEST(bounds, Empty) +{ + Span<float2> empty_span{}; + EXPECT_TRUE(empty_span.is_empty()); + auto result = bounds::min_max(empty_span); + EXPECT_EQ(result, std::nullopt); +} + +TEST(bounds, MinMax) +{ + Array<float2> data = {float2(0, 1), float2(3, -1), float2(0, -2), float2(-1, 1)}; + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, float2(-1, -2)); + EXPECT_EQ(result->max, float2(3, 1)); +} + +TEST(bounds, MinMaxFloat) +{ + Array<float> data = {1.0f, 3.0f, 0.0f, -1.0f}; + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, -1.0f); + EXPECT_EQ(result->max, 3.0f); +} + +TEST(bounds, MinMaxRadii) +{ + Array<int2> data = {int2(0, 1), int2(3, -1), int2(0, -2), int2(-1, 1)}; + Array<int> radii = {5, 1, 1, 4}; + auto result = bounds::min_max_with_radii(data.as_span(), radii.as_span()); + EXPECT_EQ(result->min, int2(-5, -4)); + EXPECT_EQ(result->max, int2(5, 6)); +} + +TEST(bounds, Large) +{ + Array<int2> data(10000); + for (const int64_t i : data.index_range()) { + data[i] = int2(i, i); + } + + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, int2(0, 0)); + EXPECT_EQ(result->max, int2(9999, 9999)); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index 179c1c58cc4..86ae31cedcc 100644 --- a/source/blender/blenlib/tests/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -64,4 +64,153 @@ TEST(index_mask, SliceAndOffset) } } +TEST(index_mask, ExtractRanges) +{ + { + Vector<int64_t> indices = {1, 2, 3, 5, 7, 8}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 3); + EXPECT_EQ(ranges[0], IndexRange(1, 3)); + EXPECT_EQ(ranges[1], IndexRange(5, 1)); + EXPECT_EQ(ranges[2], IndexRange(7, 2)); + } + { + Vector<int64_t> indices; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 0); + } + { + Vector<int64_t> indices = {5, 6, 7, 8, 9, 10}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 1); + EXPECT_EQ(ranges[0], IndexRange(5, 6)); + } + { + Vector<int64_t> indices = {1, 3, 6, 8}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 4); + EXPECT_EQ(ranges[0], IndexRange(1, 1)); + EXPECT_EQ(ranges[1], IndexRange(3, 1)); + EXPECT_EQ(ranges[2], IndexRange(6, 1)); + EXPECT_EQ(ranges[3], IndexRange(8, 1)); + } + { + Vector<int64_t> indices; + IndexRange range1{4, 10}; + IndexRange range2{20, 30}; + IndexRange range3{100, 1}; + IndexRange range4{150, 100}; + for (const IndexRange &range : {range1, range2, range3, range4}) { + for (const int64_t i : range) { + indices.append(i); + } + } + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 4); + EXPECT_EQ(ranges[0], range1); + EXPECT_EQ(ranges[1], range2); + EXPECT_EQ(ranges[2], range3); + EXPECT_EQ(ranges[3], range4); + } + { + const int64_t max_test_range_size = 50; + Vector<int64_t> indices; + int64_t offset = 0; + for (const int64_t range_size : IndexRange(1, max_test_range_size)) { + for (const int i : IndexRange(range_size)) { + indices.append(offset + i); + } + offset += range_size + 1; + } + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), max_test_range_size); + for (const int64_t range_size : IndexRange(1, max_test_range_size)) { + const IndexRange range = ranges[range_size - 1]; + EXPECT_EQ(range.size(), range_size); + } + } +} + +TEST(index_mask, Invert) +{ + { + Vector<int64_t> indices; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 10); + EXPECT_TRUE(new_indices.is_empty()); + } + { + Vector<int64_t> indices = {3, 4, 5, 6}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(3, 4), new_indices); + EXPECT_TRUE(inverted_mask.is_empty()); + } + { + Vector<int64_t> indices = {5}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 9); + EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({0, 1, 2, 3, 4, 6, 7, 8, 9})); + } + { + Vector<int64_t> indices = {0, 1, 2, 6, 7, 9}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 4); + EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({3, 4, 5, 8})); + } +} + +TEST(index_mask, ExtractRangesInvert) +{ + { + Vector<int64_t> indices; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10), nullptr); + EXPECT_EQ(ranges.size(), 1); + EXPECT_EQ(ranges[0], IndexRange(10)); + } + { + Vector<int64_t> indices = {1, 2, 3, 6, 7}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10), + &skip_amounts); + EXPECT_EQ(ranges.size(), 3); + EXPECT_EQ(ranges[0], IndexRange(0, 1)); + EXPECT_EQ(ranges[1], IndexRange(4, 2)); + EXPECT_EQ(ranges[2], IndexRange(8, 2)); + EXPECT_EQ(skip_amounts[0], 0); + EXPECT_EQ(skip_amounts[1], 3); + EXPECT_EQ(skip_amounts[2], 5); + } + { + Vector<int64_t> indices = {0, 1, 2, 3, 4}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5), + &skip_amounts); + EXPECT_TRUE(ranges.is_empty()); + EXPECT_TRUE(skip_amounts.is_empty()); + } + { + Vector<int64_t> indices = {5, 6, 7, 10, 11}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5, 20), + &skip_amounts); + EXPECT_EQ(ranges.size(), 2); + EXPECT_EQ(ranges[0], IndexRange(8, 2)); + EXPECT_EQ(ranges[1], IndexRange(12, 13)); + EXPECT_EQ(skip_amounts[0], 3); + EXPECT_EQ(skip_amounts[1], 5); + } +} + +TEST(index_mask, ContainedIn) +{ + EXPECT_TRUE(IndexMask({3, 4, 5}).contained_in(IndexRange(10))); + EXPECT_TRUE(IndexMask().contained_in(IndexRange(5, 0))); + EXPECT_FALSE(IndexMask({3}).contained_in(IndexRange(3))); + EXPECT_FALSE(IndexMask({4, 5, 6}).contained_in(IndexRange(5, 10))); + EXPECT_FALSE(IndexMask({5, 6}).contained_in(IndexRange())); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_base_test.cc b/source/blender/blenlib/tests/BLI_math_base_test.cc index 33acefeeac2..62f2b2775d0 100644 --- a/source/blender/blenlib/tests/BLI_math_base_test.cc +++ b/source/blender/blenlib/tests/BLI_math_base_test.cc @@ -3,6 +3,10 @@ #include "testing/testing.h" #include "BLI_math.h" +#include "BLI_math_base.hh" +#include "BLI_math_vector.hh" + +namespace blender::tests { /* In tests below, when we are using -1.0f as max_diff value, we actually turn the function into a * pure-ULP one. */ @@ -131,3 +135,20 @@ TEST(math_base, FloorPowerOf10) EXPECT_NEAR(floor_power_of_10(100.1f), 100.0f, 1e-4f); EXPECT_NEAR(floor_power_of_10(99.9f), 10.0f, 1e-4f); } + +TEST(math_base, MinVectorAndFloat) +{ + EXPECT_EQ(math::min(1.0f, 2.0f), 1.0f); +} + +TEST(math_base, ClampInt) +{ + EXPECT_EQ(math::clamp(111, -50, 101), 101); +} + +TEST(math_base, Midpoint) +{ + EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc index 07eb6b29a20..7590d77525b 100644 --- a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc +++ b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc @@ -146,4 +146,29 @@ TEST(math_vec_types, VectorTypeConversion) EXPECT_EQ(d[1], -1.0); } +TEST(math_vec_types, Divide) +{ + float2 a(1.0f, 2.0f); + float2 b(0.5f, 2.0f); + float2 result = a / b; + EXPECT_FLOAT_EQ(result.x, 2.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + +TEST(math_vec_types, DivideFloatByVector) +{ + float a = 2.0f; + float2 b(0.5f, 2.0f); + float2 result = a / b; + EXPECT_FLOAT_EQ(result.x, 4.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + +TEST(math_vec_types, DivideFloatByVectorSmall) +{ + float2 result = 2.0f / float2(2.0f); + EXPECT_FLOAT_EQ(result.x, 1.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index 993434ddeba..ab716e5d011 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -176,4 +176,29 @@ static_assert(!is_same_any_v<int, float, bool>); static_assert(!is_same_any_v<int, float>); static_assert(!is_same_any_v<int>); +TEST(memory_utils, ScopedDefer1) +{ + int a = 0; + { + BLI_SCOPED_DEFER([&]() { a -= 5; }); + { + BLI_SCOPED_DEFER([&]() { a *= 10; }); + a = 5; + } + } + EXPECT_EQ(a, 45); +} + +TEST(memory_utils, ScopedDefer2) +{ + std::string s; + { + BLI_SCOPED_DEFER([&]() { s += "A"; }); + BLI_SCOPED_DEFER([&]() { s += "B"; }); + BLI_SCOPED_DEFER([&]() { s += "C"; }); + BLI_SCOPED_DEFER([&]() { s += "D"; }); + } + EXPECT_EQ(s, "DCBA"); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 35fb22b3257..0bd34250deb 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -247,6 +247,8 @@ TEST(span, FirstLast) Span<int> a_span(a); EXPECT_EQ(a_span.first(), 6); EXPECT_EQ(a_span.last(), 9); + EXPECT_EQ(a_span.last(1), 8); + EXPECT_EQ(a_span.last(2), 7); } TEST(span, FirstLast_OneElement) @@ -255,6 +257,7 @@ TEST(span, FirstLast_OneElement) Span<int> a_span(&a, 1); EXPECT_EQ(a_span.first(), 3); EXPECT_EQ(a_span.last(), 3); + EXPECT_EQ(a_span.last(0), 3); } TEST(span, Get) diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index 40cda20c395..29b6d2b41fe 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -447,6 +447,9 @@ TEST(vector, Last) { Vector<int> a{3, 5, 7}; EXPECT_EQ(a.last(), 7); + EXPECT_EQ(a.last(0), 7); + EXPECT_EQ(a.last(1), 5); + EXPECT_EQ(a.last(2), 3); } TEST(vector, AppendNTimes) diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c index 10150f56098..e1527201e22 100644 --- a/source/blender/blenloader/intern/blend_validate.c +++ b/source/blender/blenloader/intern/blend_validate.c @@ -181,5 +181,20 @@ bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports) BKE_main_unlock(bmain); + /* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here. + */ + LISTBASE_FOREACH_MUTABLE (Key *, shapekey, &bmain->shapekeys) { + if (shapekey->from != NULL) { + continue; + } + + BKE_reportf(reports, + RPT_ERROR, + "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted", + shapekey->id.name, + shapekey->from); + BKE_id_delete(bmain, shapekey); + } + return is_valid; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 84625fea6fc..9539436cf69 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -320,15 +320,22 @@ static void oldnewmap_increase_size(OldNewMap *onm) /* Public OldNewMap API */ -static OldNewMap *oldnewmap_new(void) +static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp) { - OldNewMap *onm = MEM_callocN(sizeof(*onm), "OldNewMap"); + memset(onm, 0x0, sizeof(*onm)); - onm->capacity_exp = DEFAULT_SIZE_EXP; + onm->capacity_exp = capacity_exp; onm->entries = MEM_malloc_arrayN( ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries"); onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map"); oldnewmap_clear_map(onm); +} + +static OldNewMap *oldnewmap_new(void) +{ + OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap"); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); return onm; } @@ -395,9 +402,10 @@ static void oldnewmap_clear(OldNewMap *onm) } } - onm->capacity_exp = DEFAULT_SIZE_EXP; - oldnewmap_clear_map(onm); - onm->nentries = 0; + MEM_freeN(onm->entries); + MEM_freeN(onm->map); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); } static void oldnewmap_free(OldNewMap *onm) @@ -2913,7 +2921,7 @@ static const char *dataname(short id_code) return "Data from MA"; case ID_TE: return "Data from TE"; - case ID_CU: + case ID_CU_LEGACY: return "Data from CU"; case ID_GR: return "Data from GR"; diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6bf3402eafe..ac59e3efc72 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -1644,7 +1644,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) BLI_addtail((ListBase *)&ob->modifiers, lmd); ob->partype = PAROBJECT; } - else if (parent->type == OB_CURVE && ob->partype == PARCURVE) { + else if (parent->type == OB_CURVES_LEGACY && ob->partype == PARCURVE) { CurveModifierData *cmd; cmd = (CurveModifierData *)BKE_modifier_new(eModifierType_Curve); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index f14029d6555..2a840ea585a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2544,6 +2544,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 301, 7) || + (bmain->versionfile == 302 && !MAIN_VERSION_ATLEAST(bmain, 302, 4))) { + /* Duplicate value for two flags that mistakenly had the same numeric value. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_WeightVGProximity) { + WeightVGProximityModifierData *wpmd = (WeightVGProximityModifierData *)md; + if (wpmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) { + wpmd->proximity_flags |= MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE; + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 6aac76642d5..281769410bd 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -56,7 +56,7 @@ ID *do_versions_rename_id(Main *bmain, ListBase *lb = which_libbase(bmain, id_type); ID *id = nullptr; LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == nullptr) { + if (!ID_IS_LINKED(idtest)) { if (STREQ(idtest->name + 2, name_src)) { id = idtest; } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index cd4efa95d5e..2908b2b151b 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1356,7 +1356,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) bFollowPathConstraint *data = con->data; Object *obc = blo_do_versions_newlibadr(fd, lib, data->tar); - if (obc && obc->type == OB_CURVE) { + if (obc && obc->type == OB_CURVES_LEGACY) { Curve *cu = blo_do_versions_newlibadr(fd, lib, obc->data); if (cu) { cu->flag |= CU_PATH; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 8685a0fa62d..e933964221b 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -33,6 +33,7 @@ #include "readfile.h" /* Own include. */ +#include "WM_types.h" #include "wm_event_types.h" /* Don't use translation strings in versioning! @@ -363,10 +364,12 @@ static void do_version_select_mouse(UserDef *userdef, wmKeyMapItem *kmi) kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE; break; case EVT_TWEAK_S: - kmi->type = (left) ? EVT_TWEAK_L : EVT_TWEAK_R; + kmi->type = (left) ? LEFTMOUSE : RIGHTMOUSE; + kmi->val = KM_CLICK_DRAG; break; case EVT_TWEAK_A: - kmi->type = (left) ? EVT_TWEAK_R : EVT_TWEAK_L; + kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE; + kmi->val = KM_CLICK_DRAG; break; default: break; @@ -385,6 +388,39 @@ static bool keymap_item_has_invalid_wm_context_data_path(wmKeyMapItem *kmi, return false; } +static bool keymap_item_update_tweak_event(wmKeyMapItem *kmi, void *UNUSED(user_data)) +{ + /* Tweak events for L M R mouse-buttons. */ + enum { + EVT_TWEAK_L = 0x5002, + EVT_TWEAK_M = 0x5003, + EVT_TWEAK_R = 0x5004, + }; + switch (kmi->type) { + case EVT_TWEAK_L: + kmi->type = LEFTMOUSE; + break; + case EVT_TWEAK_M: + kmi->type = MIDDLEMOUSE; + break; + case EVT_TWEAK_R: + kmi->type = RIGHTMOUSE; + break; + default: + kmi->direction = KM_ANY; + return false; + } + + if (kmi->val >= KM_DIRECTION_N && kmi->val <= KM_DIRECTION_NW) { + kmi->direction = kmi->val; + } + else { + kmi->direction = KM_ANY; + } + kmi->val = KM_CLICK_DRAG; + return false; +} + void blo_do_versions_userdef(UserDef *userdef) { /* #UserDef & #Main happen to have the same struct member. */ @@ -948,6 +984,20 @@ void blo_do_versions_userdef(UserDef *userdef) } } + if (!USER_VERSION_ATLEAST(300, 43)) { + userdef->ndof_flag |= NDOF_CAMERA_PAN_ZOOM; + } + + if (!USER_VERSION_ATLEAST(302, 5)) { + BKE_keyconfig_pref_filter_items(userdef, + &((struct wmKeyConfigFilterItemParams){ + .check_item = true, + .check_diff_item_add = true, + }), + keymap_item_update_tweak_event, + NULL); + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 495054923f9..490328106ca 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -580,7 +580,7 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren /** * END the mywrite wrapper - * \return 1 if write failed + * \return True if write failed * \return unknown global variable otherwise * \warning Talks to other functions with global parameters */ @@ -1256,12 +1256,12 @@ static bool do_history(const char *name, ReportList *reports) int hisnr = U.versions; if (U.versions == 0) { - return 0; + return false; } if (strlen(name) < 2) { BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short"); - return 1; + return true; } while (hisnr > 1) { @@ -1287,7 +1287,7 @@ static bool do_history(const char *name, ReportList *reports) } } - return 0; + return false; } /** \} */ @@ -1334,7 +1334,7 @@ bool BLO_write_file(Main *mainvar, if (ww.open(&ww, tempname) == false) { BKE_reportf( reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno)); - return 0; + return false; } if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) { @@ -1426,7 +1426,7 @@ bool BLO_write_file(Main *mainvar, BKE_report(reports, RPT_ERROR, strerror(errno)); remove(tempname); - return 0; + return false; } /* file save to temporary file was successful */ @@ -1435,13 +1435,13 @@ bool BLO_write_file(Main *mainvar, const bool err_hist = do_history(filepath, reports); if (err_hist) { BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)"); - return 0; + return false; } } if (BLI_rename(tempname, filepath) != 0) { BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)"); - return 0; + return false; } if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) { @@ -1449,7 +1449,7 @@ bool BLO_write_file(Main *mainvar, BLO_main_validate_libraries(mainvar, reports); } - return 1; + return true; } bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags) diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index a7873d5d2d0..ebb0f604df7 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -92,7 +92,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_CAMERA "Camera" #define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile" #define BLT_I18NCONTEXT_ID_COLLECTION "Collection" -#define BLT_I18NCONTEXT_ID_CURVE "Curve" +#define BLT_I18NCONTEXT_ID_CURVE_LEGACY "Curve" #define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" #define BLT_I18NCONTEXT_ID_GPENCIL "GPencil" #define BLT_I18NCONTEXT_ID_CURVES "Curves" @@ -129,6 +129,9 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip" #define BLT_I18NCONTEXT_ID_MASK "Mask" +/* Editors-types contexts. */ +#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D" + /* Helper for bpy.app.i18n object... */ typedef struct { const char *c_id; @@ -154,7 +157,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_COLLECTION, "id_collection"), \ - BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVES, "id_curves"), \ @@ -191,6 +194,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORLD, "id_world"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORKSPACE, "id_workspace"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \ { \ NULL, NULL, NULL \ } \ diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 77b0781bcb6..31492cd5c13 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../depsgraph ../makesdna ../../../intern/atomic + ../../../intern/clog ../../../intern/eigen ../../../intern/guardedalloc ../../../extern/rangetree diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index ae6b4da6003..ecdc86d31cf 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -54,6 +54,19 @@ * * This has the effect from the users POV of leaving the mesh un-touched, * and only editing the active shape key-block. + * + * \subsection other_notes Other Notes + * + * Other details noted here which might not be so obvious: + * + * - The #CD_SHAPEKEY layer is only used in edit-mode, + * and the #Mesh.key is only used in object-mode. + * Although the #CD_SHAPEKEY custom-data layer is converted into #Key data-blocks for each + * undo-step while in edit-mode. + * - The #CD_SHAPE_KEYINDEX layer is used to check if vertices existed when entering edit-mode. + * Values of the indices are only used for shape-keys when the #CD_SHAPEKEY layer can't be found, + * allowing coordinates from the #Key to be used to prevent data-loss. + * These indices are also used to maintain correct indices for hook modifiers and vertex parents. */ #include "DNA_key_types.h" @@ -84,6 +97,10 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bmesh.mesh.convert"}; + using blender::Array; using blender::IndexRange; using blender::Span; @@ -195,10 +212,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (!me || !me->totvert) { if (me && is_new) { /* No verts? still copy custom-data layout. */ - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_ASSIGN, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_ASSIGN, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_ASSIGN, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); + CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -553,8 +570,89 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) return vertMap; } +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh to Shape Key Conversion + * + * There are some details relating to using data from shape keys that need to be + * considered carefully for shape key synchronization logic. + * + * Key Block Usage + * *************** + * + * Key blocks (data in #Mesh.key must be used carefully). + * + * They can be used to query which key blocks are relative to the basis + * since it's not possible to add/remove/reorder key blocks while in edit-mode. + * + * Key Block Coordinates + * ===================== + * + * Key blocks locations must *not* be used. This was done from v2.67 to 3.0, + * causing bugs T35170 & T44415. + * + * Shape key synchronizing could work under the assumption that the key-block is + * fixed-in-place when entering edit-mode allowing them to be used as a reference when exiting. + * It often does work but isn't reliable since for e.g. rendering may flush changes + * from the edit-mesh to the key-block (there are a handful of other situations where + * changes may be flushed, see #ED_editors_flush_edits and related functions). + * When using undo, it's not known if the data in key-block is from the past or future, + * so just don't use this data as it causes pain and suffering for users and developers alike. + * + * Instead, use the shape-key values stored in #CD_SHAPEKEY since they are reliably + * based on the original locations, unless explicitly manipulated. + * It's important to write the final shape-key values back to the #CD_SHAPEKEY so applying + * the difference between the original-basis and the new coordinates isn't done multiple times. + * Therefore #ED_editors_flush_edits and other flushing calls will update both the #Mesh.key + * and the edit-mode #CD_SHAPEKEY custom-data layers. + * + * WARNING: There is an exception to the rule of ignoring coordinates in the destination: + * that is when shape-key data in `bm` can't be found (which is itself an error/exception). + * In this case our own rule is violated as the alternative is loosing the shape-data entirely. + * + * Flushing Coordinates Back to the #BMesh + * --------------------------------------- + * + * The edit-mesh may be flushed back to the #Mesh and #Key used to generate it. + * When this is done, the new values are written back to the #BMesh's #CD_SHAPEKEY as well. + * This is necessary when editing basis-shapes so the difference in shape keys + * is not applied multiple times. If it were important to avoid it could be skipped while + * exiting edit-mode (as the entire #BMesh is freed in that case), however it's just copying + * back a `float[3]` so the work to check if it's necessary isn't worth the overhead. + * + * In general updating the #BMesh's #CD_SHAPEKEY makes shake-key logic easier to reason about + * since it means flushing data back to the mesh has the same behavior as exiting and entering + * edit-mode (a more common operation). Meaning there is one less corner-case to have to consider. + * + * Exceptional Cases + * ***************** + * + * There are some situations that should not happen in typical usage but are + * still handled in this code, since failure to handle them could loose user-data. + * These could be investigated further since if they never happen in practice, + * we might consider removing them. However, the possibility of an mesh directly + * being modified by Python or some other low level logic that changes key-blocks + * means there is a potential this to happen so keeping code to these cases remain supported. + * + * - Custom Data & Mesh Key Block Synchronization. + * Key blocks in `me->key->block` should always have an associated + * #CD_SHAPEKEY layer in `bm->vdata`. + * If they don't there are two fall-backs for setting the location, + * - Use the value from the original shape key + * WARNING: this is technically incorrect! (see note on "Key Block Usage"). + * - Use the current vertex location, + * Also not correct but it's better then having it zeroed for e.g. + * + * - Missing key-index layer. + * In this case the basis key wont apply it's deltas to other keys and in the case + * a shape-key layer is missing, its coordinates will be initialized from the edit-mesh + * vertex locations instead of attempting to remap the shape-keys coordinates. + * + * \note These cases are considered abnormal and shouldn't occur in typical usage. + * A warning is logged in this case to help troubleshooting bugs with shape-keys. + * \{ */ + /** - * Returns custom-data shapekey index from a keyblock or -1 + * Returns custom-data shape-key index from a key-block or -1 * \note could split this out into a more generic function. */ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) @@ -573,6 +671,196 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) return -1; } +/** + * Update `key` with shape key data stored in `bm`. + * + * \param bm: The source BMesh. + * \param key: The destination key. + * \param mvert: The destination vertex array (in some situations it's coordinates are updated). + */ +static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert) +{ + KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1)); + + /* It's unlikely this ever remains false, check for correctness. */ + bool actkey_has_layer = false; + + /* Go through and find any shape-key custom-data layers + * that might not have corresponding KeyBlocks, and add them if necessary. */ + for (int i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) { + continue; + } + + KeyBlock *currkey; + for (currkey = (KeyBlock *)key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) { + break; + } + } + + if (currkey) { + if (currkey == actkey) { + actkey_has_layer = true; + } + } + else { + currkey = BKE_keyblock_add(key, bm->vdata.layers[i].name); + currkey->uid = bm->vdata.layers[i].uid; + } + } + + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMIter iter; + BMVert *eve; + float(*ofs)[3] = nullptr; + + /* Editing the basis key updates others. */ + if ((key->type == KEY_RELATIVE) && + /* The shape-key coordinates used from entering edit-mode are used. */ + (actkey_has_layer == true) && + /* Original key-indices are only used to check the vertex existed when entering edit-mode. */ + (cd_shape_keyindex_offset != -1) && + /* Offsets are only needed if the current shape is a basis for others. */ + BKE_keyblock_is_basis(key, bm->shapenr - 1)) { + + BLI_assert(actkey != nullptr); /* Assured by `actkey_has_layer` check. */ + const int actkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, actkey); + + /* Since `actkey_has_layer == true`, this must never fail. */ + BLI_assert(actkey_uuid != -1); + + const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, actkey_uuid); + + ofs = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float[3]) * bm->totvert, __func__)); + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + /* Check the vertex existed when entering edit-mode (otherwise don't apply an offset). */ + if (keyi != ORIGINDEX_NONE) { + float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset); + /* Could use 'eve->co' or the destination #MVert.co, they're the same at this point. */ + sub_v3_v3v3(ofs[i], eve->co, co_orig); + } + else { + /* If there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate. */ + MEM_freeN(ofs); + ofs = nullptr; + break; + } + } + } + + LISTBASE_FOREACH (KeyBlock *, currkey, &key->block) { + int keyi; + float(*currkey_data)[3]; + + const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); + const int cd_shape_offset = (currkey_uuid == -1) ? + -1 : + CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid); + + /* Common case, the layer data is available, use it where possible. */ + if (cd_shape_offset != -1) { + const bool apply_offset = (ofs != nullptr) && (currkey != actkey) && + (bm->shapenr - 1 == currkey->relative); + + if (currkey->data && (currkey->totelem == bm->totvert)) { + /* Use memory in-place. */ + } + else { + currkey->data = MEM_reallocN(currkey->data, key->elemsize * bm->totvert); + currkey->totelem = bm->totvert; + } + currkey_data = (float(*)[3])currkey->data; + + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset); + + if (currkey == actkey) { + copy_v3_v3(currkey_data[i], eve->co); + + if (actkey != key->refkey) { + /* Without this, the real mesh coordinates (uneditable) as soon as you create + * the Basis shape, see: T30771 for details. */ + if (cd_shape_keyindex_offset != -1) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE) { + copy_v3_v3(mvert[i].co, co_orig); + } + } + } + } + else { + copy_v3_v3(currkey_data[i], co_orig); + } + + /* Propagate edited basis offsets to other shapes. */ + if (apply_offset) { + add_v3_v3(currkey_data[i], ofs[i]); + } + + /* Apply back new coordinates shape-keys that have offset into #BMesh. + * Otherwise, in case we call again #BM_mesh_bm_to_me on same #BMesh, + * we'll apply diff from previous call to #BM_mesh_bm_to_me, + * to shape-key values from original creation of the #BMesh. See T50524. */ + copy_v3_v3(co_orig, currkey_data[i]); + } + } + else { + /* No original layer data, use fallback information. */ + if (currkey->data && (cd_shape_keyindex_offset != -1)) { + CLOG_WARN(&LOG, + "Found shape-key but no CD_SHAPEKEY layers to read from, " + "using existing shake-key data where possible"); + } + else { + CLOG_WARN(&LOG, + "Found shape-key but no CD_SHAPEKEY layers to read from, " + "using basis shape-key data"); + } + + currkey_data = static_cast<float(*)[3]>( + MEM_mallocN(key->elemsize * bm->totvert, "currkey->data")); + + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + + if ((currkey->data != nullptr) && (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < currkey->totelem)) { + /* Reconstruct keys via vertices original key indices. + * WARNING(@campbellbarton): `currkey->data` is known to be unreliable as the edit-mesh + * coordinates may be flushed back to the shape-key when exporting or rendering. + * This is a last resort! If this branch is running as part of regular usage + * it can be considered a bug. */ + const float(*oldkey)[3] = static_cast<const float(*)[3]>(currkey->data); + copy_v3_v3(currkey_data[i], oldkey[keyi]); + } + else { + /* Fail! fill in with dummy value. */ + copy_v3_v3(currkey_data[i], eve->co); + } + } + + currkey->totelem = bm->totvert; + if (currkey->data) { + MEM_freeN(currkey->data); + } + currkey->data = currkey_data; + } + } + + if (ofs) { + MEM_freeN(ofs); + } +} + +/** \} */ + BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) { /* This is a cheap way to set the edge draw, its not precise and will @@ -604,23 +892,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); - MVert *oldverts = nullptr; const int ototvert = me->totvert; - if (me->key && (cd_shape_keyindex_offset != -1)) { - /* Keep the old verts in case we are working on* a key, which is done at the end. */ - - /* Use the array in-place instead of duplicating the array. */ -#if 0 - oldverts = MEM_dupallocN(me->mvert); -#else - oldverts = me->mvert; - me->mvert = nullptr; - CustomData_update_typemap(&me->vdata); - CustomData_set_layer(&me->vdata, CD_MVERT, nullptr); -#endif - } - /* Free custom data. */ CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -661,9 +934,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); - /* There is no way to tell if BMesh normals are dirty or not. Instead of calculating the normals - * on the BMesh possibly unnecessarily, just tag them dirty on the resulting mesh. */ - BKE_mesh_normals_tag_dirty(me); + /* Clear normals on the mesh completely, since the original vertex and polygon count might be + * different than the BMesh's. */ + BKE_mesh_clear_derived_normals(me); me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); @@ -846,152 +1119,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - /* See comment below, this logic is in twice. */ - if (me->key) { - KeyBlock *currkey; - KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1)); - - float(*ofs)[3] = nullptr; - - /* Go through and find any shape-key custom-data layers - * that might not have corresponding KeyBlocks, and add them if necessary. */ - for (i = 0; i < bm->vdata.totlayer; i++) { - if (bm->vdata.layers[i].type != CD_SHAPEKEY) { - continue; - } - - for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) { - if (currkey->uid == bm->vdata.layers[i].uid) { - break; - } - } - - if (!currkey) { - currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); - currkey->uid = bm->vdata.layers[i].uid; - } - } - - /* Editing the base key should update others. */ - if (/* Only need offsets for relative shape keys. */ - (me->key->type == KEY_RELATIVE) && - - /* Unlikely, but the active key may not be valid if the - * BMesh and the mesh are out of sync. */ - (actkey != nullptr) && - - /* Not used here, but 'oldverts' is used later for applying 'ofs'. */ - (oldverts != nullptr) && - - /* Needed for referencing oldverts. */ - (cd_shape_keyindex_offset != -1)) { - - const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); - - /* Active key is a base. */ - if (act_is_basis) { - const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data); - - ofs = static_cast<float(*)[3]>( - MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data")); - mvert = me->mvert; - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - - /* Could use 'eve->co' or 'mvert->co', they're the same at this point. */ - if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) { - sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); - } - else { - /* If there are new vertices in the mesh, we can't propagate the offset - * because it will only work for the existing vertices and not the new - * ones, creating a mess when doing e.g. subdivide + translate. */ - MEM_freeN(ofs); - ofs = nullptr; - break; - } - - mvert++; - } - } - } - - LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) { - int keyi; - const float(*ofs_pt)[3] = ofs; - float *newkey, (*oldkey)[3], *fp; - - const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); - const int cd_shape_offset = (currkey_uuid == -1) ? -1 : - CustomData_get_n_offset(&bm->vdata, - CD_SHAPEKEY, - currkey_uuid); - const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) && - (currkey != actkey) && (bm->shapenr - 1 == currkey->relative); - - fp = newkey = static_cast<float *>( - MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data")); - oldkey = static_cast<float(*)[3]>(currkey->data); - - mvert = me->mvert; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - - if (currkey == actkey) { - copy_v3_v3(fp, eve->co); - - if (actkey != me->key->refkey) { /* Important see bug T30771. */ - if (cd_shape_keyindex_offset != -1) { - if (oldverts) { - keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */ - copy_v3_v3(mvert->co, oldverts[keyi].co); - } - } - } - } - } - else if (cd_shape_offset != -1) { - /* In most cases this runs. */ - copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); - } - else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) && - ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && - (keyi < currkey->totelem)) { - /* Old method of reconstructing keys via vertices original key indices, - * currently used if the new method above fails - * (which is theoretically possible in certain cases of undo). */ - copy_v3_v3(fp, oldkey[keyi]); - } - else { - /* Fail! fill in with dummy value. */ - copy_v3_v3(fp, mvert->co); - } - - /* Propagate edited basis offsets to other shapes. */ - if (apply_offset) { - add_v3_v3(fp, *ofs_pt++); - /* Apply back new coordinates shape-keys that have offset into BMesh. - * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh, - * we'll apply diff from previous call to #BM_mesh_bm_to_me, - * to shape-key values from *original creation of the BMesh*. See T50524. */ - copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); - } - - fp += 3; - mvert++; - } - - currkey->totelem = bm->totvert; - if (currkey->data) { - MEM_freeN(currkey->data); - } - currkey->data = newkey; - } - - if (ofs) { - MEM_freeN(ofs); - } + bm_to_mesh_shape(bm, me->key, me->mvert); } /* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */ @@ -1005,10 +1134,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - if (oldverts != nullptr) { - MEM_freeN(oldverts); - } - /* Topology could be changed, ensure #CD_MDISPS are ok. */ multires_topology_changed(me); @@ -1028,10 +1153,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * me->totloop = bm->totloop; me->totpoly = bm->totface; - CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totvert); - CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totedge); - CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totface); - CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop); @@ -1059,22 +1180,18 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * MEdge *medge = me->medge; MLoop *mloop = me->mloop; MPoly *mpoly = me->mpoly; - int *index, add_orig; unsigned int i, j; const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); - BKE_mesh_normals_tag_dirty(me); + /* Clear normals on the mesh completely, since the original vertex and polygon count might be + * different than the BMesh's. */ + BKE_mesh_clear_derived_normals(me); me->runtime.deformed_only = true; - /* Don't add origindex layer if one already exists. */ - add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX); - - index = (int *)CustomData_get_layer(&me->vdata, CD_ORIGINDEX); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { MVert *mv = &mvert[i]; @@ -1088,15 +1205,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); } - if (add_orig) { - *index++ = i; - } - CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i); } bm->elem_index_dirty &= ~BM_VERT; - index = (int *)CustomData_get_layer(&me->edata, CD_ORIGINDEX); BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { MEdge *med = &medge[i]; @@ -1123,13 +1235,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i); - if (add_orig) { - *index++ = i; - } } bm->elem_index_dirty &= ~BM_EDGE; - index = (int *)CustomData_get_layer(&me->pdata, CD_ORIGINDEX); j = 0; BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { BMLoop *l_iter; @@ -1156,10 +1264,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } while ((l_iter = l_iter->next) != l_first); CustomData_from_bmesh_block(&bm->pdata, &me->pdata, efa->head.data, i); - - if (add_orig) { - *index++ = i; - } } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index 45a42c29d84..1bc5b70f874 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -2234,7 +2234,9 @@ int BM_mesh_calc_face_groups(BMesh *bm, MEM_freeN(stack); /* reduce alloc to required size */ - group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + if (group_index_len != group_curr) { + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + } *r_group_index = group_index; return group_curr; @@ -2354,7 +2356,9 @@ int BM_mesh_calc_edge_groups(BMesh *bm, MEM_freeN(stack); /* reduce alloc to required size */ - group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + if (group_index_len != group_curr) { + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + } *r_group_index = group_index; return group_curr; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index e2e26c5c52b..10bdc2c7294 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -532,7 +532,7 @@ static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv) /** * Find the EdgeHalf representing the other end of e->e. - * \return Return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have + * \return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have * been constructed yet, in which case return NULL. */ static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert **r_bvother) diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index f72e81d82b9..23520364bf0 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -90,11 +90,10 @@ void CompositorOperation::deinit_execution() re = nullptr; } + Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result"); + BKE_image_partial_update_mark_full_update(image); BLI_thread_lock(LOCK_DRAW_IMAGE); - BKE_image_signal(G.main, - BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result"), - nullptr, - IMA_SIGNAL_FREE); + BKE_image_signal(G.main, image, nullptr, IMA_SIGNAL_FREE); BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index 1b23b0e001c..fa45034b00c 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -29,8 +29,10 @@ void GaussianBokehBlurOperation::init_data() const float width = this->get_width(); const float height = this->get_height(); - if (!sizeavailable_) { - update_size(); + if (execution_model_ == eExecutionModel::FullFrame) { + if (!sizeavailable_) { + update_size(); + } } radxf_ = size_ * (float)data_.sizex; @@ -96,6 +98,22 @@ void GaussianBokehBlurOperation::update_gauss() void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { + float result[4]; + input_size_->read_sampled(result, 0, 0, PixelSampler::Nearest); + size_ = result[0]; + + const float width = this->get_width(); + const float height = this->get_height(); + + radxf_ = size_ * (float)data_.sizex; + CLAMP(radxf_, 0.0f, width / 2.0f); + + radyf_ = size_ * (float)data_.sizey; + CLAMP(radyf_, 0.0f, height / 2.0f); + + radx_ = ceil(radxf_); + rady_ = ceil(radyf_); + float temp_color[4]; temp_color[0] = 0; temp_color[1] = 0; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 2a0d5ce9116..77597e0db06 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -335,16 +335,10 @@ void DepsgraphNodeBuilder::begin_build() * same as id_orig. Additionally, such ID might have been removed, which makes the check * for whether id_cow is expanded to access freed memory. In order to deal with this we * check whether CoW is needed based on a scalar value which does not lead to access of - * possibly deleted memory. - * Additionally, this saves some space in the map by skipping mapping for datablocks which - * do not need CoW, */ - if (!deg_copy_on_write_is_needed(id_node->id_type)) { - id_node->id_cow = nullptr; - continue; - } - + * possibly deleted memory. */ IDInfo *id_info = (IDInfo *)MEM_mallocN(sizeof(IDInfo), "depsgraph id info"); - if (deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) { + if (deg_copy_on_write_is_needed(id_node->id_type) && + deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) { id_info->id_cow = id_node->id_cow; } else { @@ -580,7 +574,7 @@ void DepsgraphNodeBuilder::build_id(ID *id) break; case ID_ME: case ID_MB: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_GD: case ID_CV: @@ -872,7 +866,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) /* type-specific data. */ switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: case OB_MBALL: @@ -1504,7 +1498,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata) op_node->set_as_entry(); break; } - case ID_CU: { + case ID_CU_LEGACY: { op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 25d7a0a6ac2..faad053e30c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -221,7 +221,8 @@ OperationCode bone_target_opcode(ID *target, bool object_have_geometry_component(const Object *object) { - return ELEM(object->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); + return ELEM( + object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); } } // namespace @@ -537,7 +538,7 @@ void DepsgraphRelationBuilder::build_id(ID *id) break; case ID_ME: case ID_MB: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_CV: case ID_PT: @@ -827,7 +828,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) /* type-specific data. */ switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: case OB_MBALL: @@ -983,7 +984,7 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object) add_relation(parent_key, object_transform_key, "Lattice Deform Parent"); add_relation(geom_key, object_transform_key, "Lattice Deform Parent Geom"); } - else if (object->parent->type == OB_CURVE) { + else if (object->parent->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)object->parent->data; if (cu->flag & CU_PATH) { @@ -2040,7 +2041,7 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key) * Therefore, each user of a piece of shared geometry data ends up evaluating * its own version of the stuff, complete with whatever modifiers it may use. * - * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU, ID_LT.) + * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU_LEGACY, ID_LT.) * are used for * 1) calculating the bounding boxes of the geometry data, * 2) aggregating inward links from other objects (e.g. for text on curve) @@ -2125,7 +2126,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) /* Materials. */ build_materials(object->mat, object->totcol); /* Geometry collision. */ - if (ELEM(object->type, OB_MESH, OB_CURVE, OB_LATTICE)) { + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) { // add geometry collider relations } /* Make sure uber update is the last in the dependencies. */ @@ -2220,7 +2221,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) break; case ID_MB: break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)obdata; if (cu->bevobj != nullptr) { ComponentKey bevob_geom_key(&cu->bevobj->id, NodeType::GEOMETRY); @@ -2362,8 +2363,9 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) /* light's nodetree */ if (lamp->nodetree != nullptr) { build_nodetree(lamp->nodetree); - ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::NTREE_OUTPUT); - add_relation(nodetree_key, shading_key, "NTree->Light Parameters"); + OperationKey ntree_key( + &lamp->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + add_relation(ntree_key, shading_key, "NTree->Light Parameters"); build_nested_nodetree(&lamp->id, lamp->nodetree); } } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b5968cbaeca..b8c85430f06 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -71,7 +71,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type) bool is_selectable_data_id_type(const ID_Type id_type) { - return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO); + return ELEM(id_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO); } void depsgraph_select_tag_to_component_opcode(const ID *id, @@ -332,7 +332,7 @@ void deg_graph_id_tag_legacy_compat( } break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *curve = (Curve *)id; if (curve->key != nullptr) { ID *key_id = &curve->key->id; @@ -483,6 +483,10 @@ void deg_graph_node_tag_zero(Main *bmain, if (comp_node->type == NodeType::ANIMATION) { continue; } + else if (comp_node->type == NodeType::COPY_ON_WRITE) { + id_node->is_cow_explicitly_tagged = true; + } + comp_node->tag_update(graph, update_source); } deg_graph_id_tag_legacy_compat(bmain, graph, id, (IDRecalcFlag)0, update_source); @@ -569,7 +573,7 @@ NodeType geometry_tag_to_component(const ID *id) const Object *object = (Object *)id; switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_LATTICE: @@ -586,7 +590,7 @@ NodeType geometry_tag_to_component(const ID *id) break; } case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_MB: case ID_CV: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 646e4d6d6d6..6346bab1fe8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -125,7 +125,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) SPECIAL_CASE(ID_WO, World, nodetree) SPECIAL_CASE(ID_SIM, Simulation, nodetree) - SPECIAL_CASE(ID_CU, Curve, key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) SPECIAL_CASE(ID_ME, Mesh, key) @@ -174,7 +174,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage SPECIAL_CASE(ID_WO, World, nodetree, world) SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation) - SPECIAL_CASE(ID_CU, Curve, key, curve) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key, curve) SPECIAL_CASE(ID_LT, Lattice, key, lattice) SPECIAL_CASE(ID_ME, Mesh, key, mesh) @@ -214,7 +214,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) SPECIAL_CASE(ID_WO, World, nodetree) SPECIAL_CASE(ID_SIM, Simulation, nodetree) - SPECIAL_CASE(ID_CU, Curve, key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) SPECIAL_CASE(ID_ME, Mesh, key) @@ -252,7 +252,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree) - SPECIAL_CASE(ID_CU, Curve, key, Key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key, Key) SPECIAL_CASE(ID_LT, Lattice, key, Key) SPECIAL_CASE(ID_ME, Mesh, key, Key) @@ -578,7 +578,7 @@ void update_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_orig, ID case ID_ME: update_mesh_edit_mode_pointers(id_orig, id_cow); break; - case ID_CU: + case ID_CU_LEGACY: update_curve_edit_mode_pointers(depsgraph, id_orig, id_cow); break; case ID_MB: @@ -953,7 +953,7 @@ void discard_edit_mode_pointers(ID *id_cow) case ID_ME: discard_mesh_edit_mode_pointers(id_cow); break; - case ID_CU: + case ID_CU_LEGACY: discard_curve_edit_mode_pointers(id_cow); break; case ID_MB: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 0992e242c7a..50012350036 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -81,7 +81,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) object->runtime = runtime; object->runtime.data_orig = data_orig; object->runtime.bb = bb; - if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVE, OB_FONT) && data_eval != nullptr) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT) && data_eval != nullptr) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* If geometry is tagged for update it means, that part of * evaluated mesh are not valid anymore. In this case we can not diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 4103d9a7087..98f75ad6106 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -221,6 +221,8 @@ set(SRC engines/image/image_space_image.hh engines/image/image_space_node.hh engines/image/image_space.hh + engines/image/image_texture_info.hh + engines/image/image_usage.hh engines/image/image_wrappers.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 58676caa6f9..253981d321b 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -27,25 +27,12 @@ static void eevee_motion_blur_mesh_data_free(void *val) { - EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val; - EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val; - switch (geom_mb->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int j = 0; j < hair_mb->psys_len; j++) { - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]); - } - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]); - } - } - break; - - case EEVEE_MOTION_DATA_MESH: - for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) { - GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]); - } - break; + EEVEE_ObjectMotionData *mb_data = (EEVEE_ObjectMotionData *)val; + if (mb_data->hair_data != NULL) { + MEM_freeN(mb_data->hair_data); + } + if (mb_data->geometry_data != NULL) { + MEM_freeN(mb_data->geometry_data); } MEM_freeN(val); } @@ -84,39 +71,57 @@ static bool eevee_object_key_cmp(const void *a, const void *b) return false; } +void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data) +{ + GPU_vertbuf_discard(step_data->hair_pos); + DRW_texture_free(step_data->hair_pos_tx); + MEM_freeN(step_data); +} + void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb) { if (mb->object == NULL) { mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion"); } - if (mb->geom == NULL) { - mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion"); + for (int i = 0; i < 2; i++) { + if (mb->position_vbo_cache[i] == NULL) { + mb->position_vbo_cache[i] = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE duplicate vbo cache"); + } + if (mb->hair_motion_step_cache[i] == NULL) { + mb->hair_motion_step_cache[i] = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE hair motion step cache"); + } } } void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb) { if (mb->object) { - BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN); + BLI_ghash_free(mb->object, MEM_freeN, eevee_motion_blur_mesh_data_free); mb->object = NULL; } - if (mb->geom) { - BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free); - mb->geom = NULL; + for (int i = 0; i < 2; i++) { + if (mb->position_vbo_cache[i]) { + BLI_ghash_free(mb->position_vbo_cache[i], NULL, (GHashValFreeFP)GPU_vertbuf_discard); + } + if (mb->hair_motion_step_cache[i]) { + BLI_ghash_free( + mb->hair_motion_step_cache[i], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free); + } } } -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair) +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob) { if (mb->object == NULL) { return NULL; } EEVEE_ObjectKey key, *key_p; - /* Small hack to avoid another comparison. */ - key.ob = (Object *)((char *)ob + hair); + /* Assumes that all instances have the same object pointer. This is currently the case because + * instance objects are temporary objects on the stack. */ + key.ob = ob; DupliObject *dup = DRW_object_get_dupli(ob); if (dup) { key.parent = DRW_object_get_dupli_parent(ob); @@ -139,53 +144,28 @@ EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData * return ob_step; } -static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair) +EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data) { - if (mb->geom == NULL) { - return NULL; + if (mb_data->geometry_data == NULL) { + EEVEE_GeometryMotionData *geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__); + geom_step->type = EEVEE_MOTION_DATA_MESH; + mb_data->geometry_data = geom_step; } - DupliObject *dup = DRW_object_get_dupli(ob); - void *key; - if (dup) { - key = dup->ob; - } - else { - key = ob; - } - /* Only use data for object that have no modifiers. */ - if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) { - key = ob->data; - } - key = (char *)key + (int)hair; - EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key); - if (geom_step == NULL) { - if (hair) { - EEVEE_HairMotionData *hair_step; - /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ - int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; - hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, - __func__); - hair_step->psys_len = psys_len; - geom_step = (EEVEE_GeometryMotionData *)hair_step; - geom_step->type = EEVEE_MOTION_DATA_HAIR; - } - else { - geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__); - geom_step->type = EEVEE_MOTION_DATA_MESH; - } - BLI_ghash_insert(mb->geom, key, geom_step); - } - return geom_step; + return mb_data->geometry_data; } -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob) +EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob) { - return motion_blur_deform_data_get(mb, ob, false); -} - -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob) -{ - return motion_blur_deform_data_get(mb, ob, true); + if (mb_data->hair_data == NULL) { + /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ + int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; + EEVEE_HairMotionData *hair_step = MEM_callocN( + sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__); + hair_step->psys_len = psys_len; + hair_step->type = EEVEE_MOTION_DATA_HAIR; + mb_data->hair_data = hair_step; + } + return mb_data->hair_data; } /* View Layer data. */ diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 39cfbb40318..ef4d88bd521 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -626,11 +626,6 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, "dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL); txl->dof_reduced_coc = GPU_texture_create_2d( "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL); - - /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation. - */ - GPU_texture_generate_mipmap(txl->dof_reduced_color); - GPU_texture_generate_mipmap(txl->dof_reduced_coc); } GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb, diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 93ffa2be9f3..fbc19a01a8b 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -226,15 +226,14 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } /* For now we assume hair objects are always moving. */ - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, true); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]); - EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob); + EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(mb_data, ob); int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0; if (psys_id >= mb_hair->psys_len) { @@ -252,8 +251,8 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); } - GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV]; - GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT]; + GPUTexture *tex_prev = mb_hair->psys[psys_id].step_data[MB_PREV].hair_pos_tx; + GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx; grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); @@ -265,7 +264,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } else { /* Store vertex position buffer. */ - mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md); + mb_hair->psys[psys_id].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md); mb_hair->use_deform = true; } } @@ -304,24 +303,14 @@ void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), return; } - const DupliObject *dup = DRW_object_get_dupli(ob); - if (dup != NULL && dup->ob->data != dup->ob_data) { - /* Geometry instances do not support motion blur correctly yet. The #key used in - * #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into - * account. Otherwise it can't find matching geometry instances at different points in time. */ - return; - } - - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, false); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); - EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur, - ob); + EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data); if (mb_step == MB_CURR) { GPUBatch *batch = DRW_cache_object_surface_get(ob); @@ -407,86 +396,93 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) DRW_cache_restart(); } - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); BLI_ghashIterator_done(&ghi) == false; BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; - - if (!mb_geom->use_deform) { - continue; - } - - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - if (mb_step == MB_CURR) { - /* TODO(fclem): Check if vertex count mismatch. */ - mb_hair->use_deform = true; - } - else { - for (int i = 0; i < mb_hair->psys_len; i++) { - if (mb_hair->psys[i].hair_pos[mb_step] == NULL) { - continue; - } - mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate( - mb_hair->psys[i].hair_pos[mb_step]); - + EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); + EEVEE_HairMotionData *mb_hair = mb_data->hair_data; + EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; + if (mb_hair != NULL && mb_hair->use_deform) { + if (mb_step == MB_CURR) { + /* TODO(fclem): Check if vertex count mismatch. */ + mb_hair->use_deform = true; + } + else { + for (int i = 0; i < mb_hair->psys_len; i++) { + GPUVertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos; + if (vbo == NULL) { + continue; + } + EEVEE_HairMotionStepData **step_data_cache_ptr; + if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step], + vbo, + (void ***)&step_data_cache_ptr)) { + EEVEE_HairMotionStepData *new_step_data = MEM_callocN(sizeof(EEVEE_HairMotionStepData), + __func__); + /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ + new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo); /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]); - - mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf( - "hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]); + GPU_vertbuf_use(new_step_data->hair_pos); + new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur", + new_step_data->hair_pos); + *step_data_cache_ptr = new_step_data; } + mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr; } - break; - - case EEVEE_MOTION_DATA_MESH: - if (mb_step == MB_CURR) { - /* Modify batch to have data from adjacent frames. */ - GPUBatch *batch = mb_geom->batch; - for (int i = 0; i < MB_CURR; i++) { - GPUVertBuf *vbo = mb_geom->vbo[i]; - if (vbo && batch) { - if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { - /* Vertex count mismatch, disable deform motion blur. */ - mb_geom->use_deform = false; - } - - if (mb_geom->use_deform == false) { - motion_blur_remove_vbo_reference_from_batch( - batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]); - break; - } + } + } + if (mb_geom != NULL && mb_geom->use_deform) { + if (mb_step == MB_CURR) { + /* Modify batch to have data from adjacent frames. */ + GPUBatch *batch = mb_geom->batch; + for (int i = 0; i < MB_CURR; i++) { + GPUVertBuf *vbo = mb_geom->vbo[i]; + if (vbo && batch) { + if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { + /* Vertex count mismatch, disable deform motion blur. */ + mb_geom->use_deform = false; + } + if (mb_geom->use_deform == false) { + motion_blur_remove_vbo_reference_from_batch( + batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); + break; + } + /* Avoid adding the same vbo more than once when the batch is used by multiple + * instances. */ + if (!GPU_batch_vertbuf_has(batch, vbo)) { + /* Currently, the code assumes that all objects that share the same mesh in the + * current frame also share the same mesh on other frames. */ GPU_batch_vertbuf_add_ex(batch, vbo, false); } } } - else { - GPUVertBuf *vbo = mb_geom->vbo[mb_step]; - if (vbo) { - /* Use the vbo to perform the copy on the GPU. */ - GPU_vertbuf_use(vbo); - /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ - mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo); + } + else { + GPUVertBuf *vbo = mb_geom->vbo[mb_step]; + if (vbo) { + /* Use the vbo to perform the copy on the GPU. */ + GPU_vertbuf_use(vbo); + /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ + GPUVertBuf **vbo_cache_ptr; + if (!BLI_ghash_ensure_p(effects->motion_blur.position_vbo_cache[mb_step], + vbo, + (void ***)&vbo_cache_ptr)) { + /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ + GPUVertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo); + *vbo_cache_ptr = duplicated_vbo; /* Find and replace "pos" attrib name. */ - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); + GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo); int attrib_id = GPU_vertformat_attr_id_get(format, "pos"); GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt"); } - else { - /* This might happen if the object visibility has been animated. */ - mb_geom->use_deform = false; - } + mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr; } - break; - - default: - BLI_assert(0); - break; + else { + /* This might happen if the object visibility has been animated. */ + mb_geom->use_deform = false; + } + } } } } @@ -503,54 +499,62 @@ void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata) /* Camera Data. */ effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT]; - /* Object Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); - BLI_ghashIterator_done(&ghi) == false; - BLI_ghashIterator_step(&ghi)) { - EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); - - copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); + /* Swap #position_vbo_cache pointers. */ + if (effects->motion_blur.position_vbo_cache[MB_PREV]) { + BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV], + NULL, + (GHashValFreeFP)GPU_vertbuf_discard); + } + effects->motion_blur.position_vbo_cache[MB_PREV] = + effects->motion_blur.position_vbo_cache[MB_NEXT]; + effects->motion_blur.position_vbo_cache[MB_NEXT] = NULL; + + /* Swap #hair_motion_step_cache pointers. */ + if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) { + BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV], + NULL, + (GHashValFreeFP)EEVEE_motion_hair_step_free); } + effects->motion_blur.hair_motion_step_cache[MB_PREV] = + effects->motion_blur.hair_motion_step_cache[MB_NEXT]; + effects->motion_blur.hair_motion_step_cache[MB_NEXT] = NULL; - /* Deformation Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); - BLI_ghashIterator_done(&ghi) == false; + /* Rename attributes in #position_vbo_cache. */ + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]); + !BLI_ghashIterator_done(&ghi); BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; + GPUVertBuf *vbo = BLI_ghashIterator_getValue(&ghi); + GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); + int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); + GPU_vertformat_attr_rename(format, attrib_id, "prv"); + } - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int i = 0; i < mb_hair->psys_len; i++) { - GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]); - DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]); - mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT]; - mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT]; - mb_hair->psys[i].hair_pos[MB_NEXT] = NULL; - mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL; - } - break; + /* Object Data. */ + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi); + BLI_ghashIterator_step(&ghi)) { + EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); + EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; + EEVEE_HairMotionData *mb_hair = mb_data->hair_data; - case EEVEE_MOTION_DATA_MESH: - if (mb_geom->batch != NULL) { - motion_blur_remove_vbo_reference_from_batch( - mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - } - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; - mb_geom->vbo[MB_NEXT] = NULL; - - if (mb_geom->vbo[MB_PREV]) { - GPUVertBuf *vbo = mb_geom->vbo[MB_PREV]; - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); - int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); - GPU_vertformat_attr_rename(format, attrib_id, "prv"); - } - break; + copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); - default: - BLI_assert(0); - break; + if (mb_hair != NULL) { + for (int i = 0; i < mb_hair->psys_len; i++) { + mb_hair->psys[i].step_data[MB_PREV].hair_pos = + mb_hair->psys[i].step_data[MB_NEXT].hair_pos; + mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx = + mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx; + mb_hair->psys[i].step_data[MB_NEXT].hair_pos = NULL; + mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = NULL; + } + } + if (mb_geom != NULL) { + if (mb_geom->batch != NULL) { + motion_blur_remove_vbo_reference_from_batch( + mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); + } + mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; + mb_geom->vbo[MB_NEXT] = NULL; } } } diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index e8828cc7494..2518ee53da3 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -618,8 +618,23 @@ enum { #define MB_CURR 2 typedef struct EEVEE_MotionBlurData { + /** + * Maps #EEVEE_ObjectKey to #EEVEE_ObjectMotionData. + */ struct GHash *object; - struct GHash *geom; + /** + * Maps original #GPUVertBuf to duplicated #GPUVertBuf. + * There are two maps for #MB_PREV and #MB_NEXT. + * Only the values are owned. + */ + struct GHash *position_vbo_cache[2]; + /** + * Maps original #GPUVertBuf to #EEVEE_HairMotionStepData. + * There are two maps for #MB_PREV and #MB_NEXT. + * Only the values are owned. + */ + struct GHash *hair_motion_step_cache[2]; + struct { float viewmat[4][4]; float persmat[4][4]; @@ -637,15 +652,16 @@ typedef struct EEVEE_ObjectKey { int id[8]; /* MAX_DUPLI_RECUR */ } EEVEE_ObjectKey; -typedef struct EEVEE_ObjectMotionData { - float obmat[3][4][4]; -} EEVEE_ObjectMotionData; - typedef enum eEEVEEMotionData { EEVEE_MOTION_DATA_MESH = 0, EEVEE_MOTION_DATA_HAIR, } eEEVEEMotionData; +typedef struct EEVEE_HairMotionStepData { + struct GPUVertBuf *hair_pos; + struct GPUTexture *hair_pos_tx; +} EEVEE_HairMotionStepData; + typedef struct EEVEE_HairMotionData { /** Needs to be first to ensure casting. */ eEEVEEMotionData type; @@ -653,8 +669,8 @@ typedef struct EEVEE_HairMotionData { /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */ int psys_len; struct { - struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */ - struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */ + /* The vbos and textures are not owned. */ + EEVEE_HairMotionStepData step_data[2]; /* Data for time = t +/- step. */ } psys[0]; } EEVEE_HairMotionData; @@ -664,10 +680,18 @@ typedef struct EEVEE_GeometryMotionData { /** To disable deform mb if vertcount mismatch. */ int use_deform; + /* The batch and vbos are not owned. */ struct GPUBatch *batch; /* Batch for time = t. */ struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */ } EEVEE_GeometryMotionData; +typedef struct EEVEE_ObjectMotionData { + float obmat[3][4][4]; + + EEVEE_GeometryMotionData *geometry_data; + EEVEE_HairMotionData *hair_data; +} EEVEE_ObjectMotionData; + /* ************ EFFECTS DATA ************* */ typedef enum EEVEE_EffectsFlag { @@ -1062,17 +1086,15 @@ typedef struct EEVEE_PrivateData { void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb); void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb); void EEVEE_view_layer_data_free(void *storage); +void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data); EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void); EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer); EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void); EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair); -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, - Object *ob); -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob); +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob); +EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data); +EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob); EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob); diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index b5e9a296c16..29d98f6795d 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -273,11 +273,9 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* Resize shcasters buffers if too big. */ if (frontbuffer->alloc_count - frontbuffer->count > SH_CASTER_ALLOC_CHUNK) { - frontbuffer->alloc_count = (frontbuffer->count / SH_CASTER_ALLOC_CHUNK) * + frontbuffer->alloc_count = divide_ceil_u(max_ii(1, frontbuffer->count), + SH_CASTER_ALLOC_CHUNK) * SH_CASTER_ALLOC_CHUNK; - frontbuffer->alloc_count += (frontbuffer->count % SH_CASTER_ALLOC_CHUNK != 0) ? - SH_CASTER_ALLOC_CHUNK : - 0; frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox, sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count); BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count); diff --git a/source/blender/draw/engines/image/image_buffer_cache.hh b/source/blender/draw/engines/image/image_buffer_cache.hh new file mode 100644 index 00000000000..ef11551c879 --- /dev/null +++ b/source/blender/draw/engines/image/image_buffer_cache.hh @@ -0,0 +1,131 @@ +/* + * 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 2022, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +struct FloatImageBuffer { + ImBuf *source_buffer = nullptr; + ImBuf *float_buffer = nullptr; + bool is_used = true; + + FloatImageBuffer(ImBuf *source_buffer, ImBuf *float_buffer) + : source_buffer(source_buffer), float_buffer(float_buffer) + { + } + + FloatImageBuffer(FloatImageBuffer &&other) noexcept + { + source_buffer = other.source_buffer; + float_buffer = other.float_buffer; + is_used = other.is_used; + other.source_buffer = nullptr; + other.float_buffer = nullptr; + } + + virtual ~FloatImageBuffer() + { + IMB_freeImBuf(float_buffer); + float_buffer = nullptr; + source_buffer = nullptr; + } + + FloatImageBuffer &operator=(FloatImageBuffer &&other) noexcept + { + this->source_buffer = other.source_buffer; + this->float_buffer = other.float_buffer; + is_used = other.is_used; + other.source_buffer = nullptr; + other.float_buffer = nullptr; + return *this; + } +}; + +struct FloatBufferCache { + private: + blender::Vector<FloatImageBuffer> cache_; + + public: + ImBuf *ensure_float_buffer(ImBuf *image_buffer) + { + /* Check if we can use the float buffer of the given image_buffer. */ + if (image_buffer->rect_float != nullptr) { + return image_buffer; + } + + /* Do we have a cached float buffer. */ + for (FloatImageBuffer &item : cache_) { + if (item.source_buffer == image_buffer) { + item.is_used = true; + return item.float_buffer; + } + } + + /* Generate a new float buffer. */ + IMB_float_from_rect(image_buffer); + ImBuf *new_imbuf = IMB_allocImBuf(image_buffer->x, image_buffer->y, image_buffer->planes, 0); + new_imbuf->rect_float = image_buffer->rect_float; + new_imbuf->flags |= IB_rectfloat; + new_imbuf->mall |= IB_rectfloat; + image_buffer->rect_float = nullptr; + image_buffer->flags &= ~IB_rectfloat; + image_buffer->mall &= ~IB_rectfloat; + + cache_.append(FloatImageBuffer(image_buffer, new_imbuf)); + return new_imbuf; + } + + void reset_usage_flags() + { + for (FloatImageBuffer &buffer : cache_) { + buffer.is_used = false; + } + } + + void mark_used(const ImBuf *image_buffer) + { + for (FloatImageBuffer &item : cache_) { + if (item.source_buffer == image_buffer) { + item.is_used = true; + return; + } + } + } + + void remove_unused_buffers() + { + for (int64_t i = cache_.size() - 1; i >= 0; i--) { + if (!cache_[i].is_used) { + cache_.remove_and_reorder(i); + } + } + } + + void clear() + { + cache_.clear(); + } +}; diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh index b3d6c3abd18..c091f800d95 100644 --- a/source/blender/draw/engines/image/image_drawing_mode.hh +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -157,6 +157,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD if (tile_buffer == nullptr) { continue; } + instance_data.float_buffers.mark_used(tile_buffer); BKE_image_release_ibuf(image, tile_buffer, lock); DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp); @@ -184,12 +185,14 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD switch (changes.get_result_code()) { case ePartialUpdateCollectResult::FullUpdateNeeded: instance_data.mark_all_texture_slots_dirty(); + instance_data.float_buffers.clear(); break; case ePartialUpdateCollectResult::NoChangesDetected: break; case ePartialUpdateCollectResult::PartialChangesDetected: /* Partial update when wrap repeat is enabled is not supported. */ if (instance_data.flags.do_tile_drawing) { + instance_data.float_buffers.clear(); instance_data.mark_all_texture_slots_dirty(); } else { @@ -200,6 +203,34 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD do_full_update_for_dirty_textures(instance_data, image_user); } + /** + * Update the float buffer in the region given by the partial update checker. + */ + void do_partial_update_float_buffer( + ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const + { + ImBuf *src = iterator.tile_data.tile_buffer; + BLI_assert(float_buffer->rect_float != nullptr); + BLI_assert(float_buffer->rect == nullptr); + BLI_assert(src->rect_float == nullptr); + BLI_assert(src->rect != nullptr); + + /* Calculate the overlap between the updated region and the buffer size. Partial Update Checker + * always returns a tile (256x256). Which could lay partially outside the buffer when using + * different resolutions. + */ + rcti buffer_rect; + BLI_rcti_init(&buffer_rect, 0, float_buffer->x, 0, float_buffer->y); + rcti clipped_update_region; + const bool has_overlap = BLI_rcti_isect( + &buffer_rect, &iterator.changed_region.region, &clipped_update_region); + if (!has_overlap) { + return; + } + + IMB_float_from_rect_ex(float_buffer, src, &clipped_update_region); + } + void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator, IMAGE_InstanceData &instance_data) const { @@ -208,7 +239,11 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD if (iterator.tile_data.tile_buffer == nullptr) { continue; } - ensure_float_buffer(*iterator.tile_data.tile_buffer); + ImBuf *tile_buffer = ensure_float_buffer(instance_data, iterator.tile_data.tile_buffer); + if (tile_buffer != iterator.tile_data.tile_buffer) { + do_partial_update_float_buffer(tile_buffer, iterator); + } + const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x); const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y); @@ -283,7 +318,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD &extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat); int offset = 0; - ImBuf *tile_buffer = iterator.tile_data.tile_buffer; for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax; y++) { float yf = y / (float)texture_height; @@ -372,16 +406,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD * rect_float as the reference-counter isn't 0. To work around this we destruct any created local * buffers ourself. */ - bool ensure_float_buffer(ImBuf &image_buffer) const + ImBuf *ensure_float_buffer(IMAGE_InstanceData &instance_data, ImBuf *image_buffer) const { - if (image_buffer.rect_float == nullptr) { - IMB_float_from_rect(&image_buffer); - return true; - } - return false; + return instance_data.float_buffers.ensure_float_buffer(image_buffer); } - void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data, + void do_full_update_texture_slot(IMAGE_InstanceData &instance_data, const TextureInfo &texture_info, ImBuf &texture_buffer, ImBuf &tile_buffer, @@ -389,10 +419,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD { const int texture_width = texture_buffer.x; const int texture_height = texture_buffer.y; - const bool float_buffer_created = ensure_float_buffer(tile_buffer); - /* TODO(jbakker): Find leak when rendering VSE and don't free here. */ - const bool do_free_float_buffer = float_buffer_created && - instance_data.image->type == IMA_TYPE_R_RESULT; + ImBuf *float_tile_buffer = ensure_float_buffer(instance_data, &tile_buffer); /* IMB_transform works in a non-consistent space. This should be documented or fixed!. * Construct a variant of the info_uv_to_texture that adds the texel space @@ -423,16 +450,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD transform_mode = IMB_TRANSFORM_MODE_CROP_SRC; } - IMB_transform(&tile_buffer, + IMB_transform(float_tile_buffer, &texture_buffer, transform_mode, IMB_FILTER_NEAREST, uv_to_texel, crop_rect_ptr); - - if (do_free_float_buffer) { - imb_freerectfloatImBuf(&tile_buffer); - } } public: @@ -451,6 +474,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD instance_data->partial_update.ensure_image(image); instance_data->clear_dirty_flag(); + instance_data->float_buffers.reset_usage_flags(); /* Step: Find out which screen space textures are needed to draw on the screen. Remove the * screen space textures that aren't needed. */ @@ -459,7 +483,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD method.update_screen_uv_bounds(); /* Check for changes in the image user compared to the last time. */ - instance_data->update_image_user(iuser); + instance_data->update_image_usage(iuser); /* Step: Update the GPU textures based on the changes in the image. */ instance_data->update_gpu_texture_allocations(); @@ -467,12 +491,16 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD /* Step: Add the GPU textures to the shgroup. */ instance_data->update_batches(); - add_depth_shgroups(*instance_data, image, iuser); + if (!instance_data->flags.do_tile_drawing) { + add_depth_shgroups(*instance_data, image, iuser); + } add_shgroups(instance_data); } - void draw_finish(IMAGE_Data *UNUSED(vedata)) const override + void draw_finish(IMAGE_Data *vedata) const override { + IMAGE_InstanceData *instance_data = vedata->instance_data; + instance_data->float_buffers.remove_unused_buffers(); } void draw_scene(IMAGE_Data *vedata) const override @@ -481,8 +509,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); GPU_framebuffer_bind(dfbl->default_fb); + static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0); + float clear_depth = instance_data->flags.do_tile_drawing ? 0.75 : 1.0f; + GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, clear_depth); DRW_view_set_active(instance_data->view); DRW_draw_pass(instance_data->passes.depth_pass); diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc index 180e9601cbd..e972d21cda4 100644 --- a/source/blender/draw/engines/image/image_engine.cc +++ b/source/blender/draw/engines/image/image_engine.cc @@ -107,6 +107,7 @@ class ImageEngine { space->release_buffer(instance_data->image, image_buffer, lock); ImageUser *iuser = space->get_image_user(); + BKE_image_multiview_index(instance_data->image, iuser); drawing_mode.cache_image(vedata, instance_data->image, iuser); } diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh index dcc3b7d15cb..682b93a80b3 100644 --- a/source/blender/draw/engines/image/image_instance_data.hh +++ b/source/blender/draw/engines/image/image_instance_data.hh @@ -8,10 +8,12 @@ #pragma once #include "image_batches.hh" +#include "image_buffer_cache.hh" #include "image_partial_updater.hh" #include "image_private.hh" #include "image_shader_params.hh" #include "image_texture_info.hh" +#include "image_usage.hh" #include "image_wrappers.hh" #include "DRW_render.h" @@ -25,8 +27,8 @@ constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 1; struct IMAGE_InstanceData { struct Image *image; - /** Copy of the last image user to detect iuser differences that require a full update. */ - struct ImageUser last_image_user; + /** Usage data of the previous time, to identify changes that require a full update. */ + ImageUsage last_usage; PartialImageUpdater partial_update; @@ -47,11 +49,18 @@ struct IMAGE_InstanceData { DRWPass *depth_pass; } passes; + /** + * Cache containing the float buffers when drawing byte images. + */ + FloatBufferCache float_buffers; + /** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */ float ss_to_texture[4][4]; TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN]; public: + virtual ~IMAGE_InstanceData() = default; + void clear_dirty_flag() { reset_dirty_flag(false); @@ -95,24 +104,13 @@ struct IMAGE_InstanceData { } } - void update_image_user(const ImageUser *image_user) + void update_image_usage(const ImageUser *image_user) { - short requested_pass = image_user ? image_user->pass : 0; - short requested_layer = image_user ? image_user->layer : 0; - short requested_view = image_user ? image_user->multi_index : 0; - /* There is room for 2 multiview textures. When a higher number is requested we should always - * target the first view slot. This is fine as multi view images aren't used together. */ - if (requested_view > 1) { - requested_view = 0; - } - - if (last_image_user.pass != requested_pass || last_image_user.layer != requested_layer || - last_image_user.multi_index != requested_view) { - - last_image_user.pass = requested_pass; - last_image_user.layer = requested_layer; - last_image_user.multi_index = requested_view; + ImageUsage usage(image, image_user, flags.do_tile_drawing); + if (last_usage != usage) { + last_usage = usage; reset_dirty_flag(true); + float_buffers.clear(); } } diff --git a/source/blender/draw/engines/image/image_usage.hh b/source/blender/draw/engines/image/image_usage.hh new file mode 100644 index 00000000000..bea5c3853b0 --- /dev/null +++ b/source/blender/draw/engines/image/image_usage.hh @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +/** + * ImageUsage contains data of the image and image user to identify changes that require a rebuild + * the texture slots. + */ +struct ImageUsage { + /** Render pass of the image that is used. */ + short pass = 0; + /** Layer of the image that is used.*/ + short layer = 0; + /** View of the image that is used. */ + short view = 0; + + ColorManagedColorspaceSettings colorspace_settings; + /** IMA_ALPHA_* */ + char alpha_mode; + bool last_tile_drawing; + + const void *last_image = nullptr; + + ImageUsage() = default; + ImageUsage(const struct Image *image, const struct ImageUser *image_user, bool do_tile_drawing) + { + pass = image_user ? image_user->pass : 0; + layer = image_user ? image_user->layer : 0; + view = image_user ? image_user->multi_index : 0; + colorspace_settings = image->colorspace_settings; + alpha_mode = image->alpha_mode; + last_image = static_cast<const void *>(image); + last_tile_drawing = do_tile_drawing; + } + + bool operator==(const ImageUsage &other) const + { + return memcmp(this, &other, sizeof(ImageUsage)) == 0; + } + bool operator!=(const ImageUsage &other) const + { + return !(*this == other); + } +}; diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b41d9ce69ef..ad0d939e99a 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -182,7 +182,9 @@ static void OVERLAY_cache_init(void *vedata) case CTX_MODE_WEIGHT_GPENCIL: OVERLAY_edit_gpencil_cache_init(vedata); break; + case CTX_MODE_SCULPT_CURVES: case CTX_MODE_OBJECT: + case CTX_MODE_EDIT_CURVES: break; default: BLI_assert_msg(0, "Draw mode invalid"); @@ -210,7 +212,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo { OVERLAY_DupliData **dupli_data = (OVERLAY_DupliData **)DRW_duplidata_get(vedata); *do_init = false; - if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVE, OB_FONT)) { + if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT)) { return NULL; } @@ -237,7 +239,7 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj return pd->ctx_mode == CTX_MODE_EDIT_MESH; case OB_ARMATURE: return pd->ctx_mode == CTX_MODE_EDIT_ARMATURE; - case OB_CURVE: + case OB_CURVES_LEGACY: return pd->ctx_mode == CTX_MODE_EDIT_CURVE; case OB_SURF: return pd->ctx_mode == CTX_MODE_EDIT_SURFACE; @@ -296,7 +298,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) (ob->sculpt->mode_type == OB_MODE_SCULPT); const bool has_surface = ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_MBALL, OB_FONT, @@ -366,7 +368,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_edit_armature_cache_populate(vedata, ob); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: OVERLAY_edit_curve_cache_populate(vedata, ob); break; case OB_SURF: @@ -661,6 +663,8 @@ static void OVERLAY_draw_scene(void *vedata) case CTX_MODE_WEIGHT_GPENCIL: OVERLAY_edit_gpencil_draw(vedata); break; + case CTX_MODE_SCULPT_CURVES: + break; default: break; } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index e370873c234..aae12e5513e 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -456,7 +456,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob_data; BKE_curve_texspace_ensure(cu); texcoloc = cu->loc; @@ -499,7 +499,7 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); float *color = DRW_color_background_blend_get(theme_id); PartDeflect *pd = ob->pd; - Curve *cu = (ob->type == OB_CURVE) ? ob->data : NULL; + Curve *cu = (ob->type == OB_CURVES_LEGACY) ? ob->data : NULL; union { float mat[4][4]; diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 58825923f37..aeba721e7ac 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -90,8 +90,8 @@ static void motion_path_get_frame_range_to_draw(bAnimVizSettings *avs, end = current_frame + avs->path_ac + 1; } else { - start = avs->path_sf; - end = avs->path_ef; + start = mpath->start_frame; + end = mpath->end_frame; } if (start > end) { diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 24eceb30441..2636d7876d5 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -196,14 +196,14 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, } } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); float *color; DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); struct GPUBatch *geom = NULL; switch (ob->type) { - case OB_CURVE: + case OB_CURVES_LEGACY: geom = DRW_cache_curve_edge_wire_get(ob); break; case OB_FONT: diff --git a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl index 4d0692039a4..ebaa898429d 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl @@ -15,7 +15,7 @@ void main() if (maskInvertStencil) { mask.rgb = 1.0 - mask.rgb; } - float mask_step = smoothstep(0, 3.0, mask.r + mask.g + mask.b); + float mask_step = smoothstep(0.0, 3.0, mask.r + mask.g + mask.b); mask.rgb *= maskColor; mask.a = mask_step * opacity; diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c index 82812ef98a5..7615b5bb39c 100644 --- a/source/blender/draw/engines/select/select_draw_utils.c +++ b/source/blender/draw/engines/select/select_draw_utils.c @@ -225,7 +225,7 @@ void select_id_draw_object(void *vedata, stl, ob, select_mode, initial_offset, r_vert_offset, r_edge_offset, r_face_offset); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; } diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c index 5e12d6a736c..191a2e6d1cc 100644 --- a/source/blender/draw/engines/workbench/workbench_opaque.c +++ b/source/blender/draw/engines/workbench/workbench_opaque.c @@ -73,11 +73,13 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_get(wpd, data); wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); @@ -85,6 +87,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_image_get(wpd, data, false); wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); @@ -92,6 +95,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_image_get(wpd, data, true); wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index f387d5371b5..bce001659b2 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -641,11 +641,6 @@ class Texture : NonCopyable { } if (tx_ == nullptr) { tx_ = create(w, h, d, mips, format, data, layered, cubemap); - if (mips > 1) { - /* TODO(@fclem): Remove once we have immutable storage or when mips are - * generated on creation. */ - GPU_texture_generate_mipmap(tx_); - } return true; } return false; diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index ce8d3136432..8fc97ddcfc2 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -813,7 +813,7 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_edge_detection_get(ob, r_is_manifold); @@ -837,7 +837,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_face_wireframe_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_face_wireframe_get(ob); @@ -864,7 +864,7 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_loose_edges_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_loose_edges_get(ob); @@ -888,7 +888,7 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_surface_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_surface_get(ob); @@ -915,7 +915,7 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob) switch (type) { case OB_MESH: return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data); - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: return DRW_curve_batch_cache_pos_vertbuf_get(ob->data); @@ -947,7 +947,7 @@ int DRW_cache_object_material_count_get(struct Object *ob) switch (type) { case OB_MESH: return DRW_mesh_material_count_get(ob, (me != NULL) ? me : ob->data); - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: return DRW_curve_material_count_get(ob->data); @@ -972,7 +972,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, switch (ob->type) { case OB_MESH: return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_surface_shaded_get(ob, gpumat_array, gpumat_array_len); @@ -2922,21 +2922,21 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob) GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob) { - BLI_assert(ob->type == OB_CURVE); + BLI_assert(ob->type == OB_CURVES_LEGACY); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_wire_edge(cu); } GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob) { - BLI_assert(ob->type == OB_CURVE); + BLI_assert(ob->type == OB_CURVES_LEGACY); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_normal_edge(cu); } GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_edit_edges(cu); @@ -2944,7 +2944,7 @@ GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob) GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_edit_verts(cu); @@ -3373,7 +3373,7 @@ void drw_batch_cache_validate(Object *ob) case OB_MESH: DRW_mesh_batch_cache_validate(ob, (Mesh *)ob->data); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: DRW_curve_batch_cache_validate((Curve *)ob->data); break; @@ -3423,7 +3423,7 @@ void drw_batch_cache_generate_requested(Object *ob) DRW_mesh_batch_cache_create_requested( DST.task_graph, ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: DRW_curve_batch_cache_create_requested(ob, scene); break; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 738a9029167..49e51d77f7b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -819,6 +819,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_data); /* Make sure UVs are computed before edituv stuffs. */ EXTRACT_ADD_REQUESTED(vbo, uv); + EXTRACT_ADD_REQUESTED(vbo, tan); EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area); EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); @@ -832,6 +833,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, return; } + mesh_render_data_update_looptris(mr, MR_ITER_LOOPTRI, MR_DATA_LOOPTRI); mesh_render_data_update_loose_geom(mr, mbc, MR_ITER_LEDGE | MR_ITER_LVERT, MR_DATA_LOOSE_GEOM); void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index abba3beb893..6a3d3fa5e9e 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -945,7 +945,7 @@ int DRW_curve_material_count_get(Curve *cu) void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scene) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)); Curve *cu = (Curve *)ob->data; CurveBatchCache *cache = curve_batch_cache_get(cu); diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index a779c694cd2..df1ac12605a 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -22,7 +22,7 @@ #include "DNA_curves_types.h" #include "DNA_object_types.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "GPU_batch.h" #include "GPU_material.h" @@ -133,12 +133,12 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves, { /* TODO: use hair radius layer if available. */ const int curve_size = curves->geometry.curve_size; - Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; - - Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size}; + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); + Span<float3> positions = geometry.positions(); for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + const IndexRange curve_range = geometry.range_for_curve(i); Span<float3> spline_positions = positions.slice(curve_range); float total_len = 0.0f; @@ -215,11 +215,11 @@ static void curves_batch_cache_fill_strands_data(Curves *curves, GPUVertBufRaw *data_step, GPUVertBufRaw *seg_step) { - const int curve_size = curves->geometry.curve_size; - Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + for (const int i : IndexRange(geometry.curves_size())) { + const IndexRange curve_range = geometry.range_for_curve(i); *(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start(); *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 8833a354c21..79a080cfccd 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -339,11 +339,7 @@ static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, /* Return true if all requests in b are in a. */ static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) { - if (a->num_requests != b->num_requests) { - return false; - } - - for (int i = 0; i < a->num_requests; i++) { + for (int i = 0; i < b->num_requests; i++) { if (!has_request(a, b->requests[i])) { return false; } @@ -1712,7 +1708,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const int required_mode = BKE_subsurf_modifier_eval_required_mode(DRW_state_is_scene_render(), is_editmode); - const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, required_mode); + const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, me, required_mode); MeshBufferList *mbuflist = &cache->final.buff; diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index ac2e5bbca2e..5d99478476c 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -67,7 +67,6 @@ enum { SHADER_BUFFER_NORMALS_ACCUMULATE, SHADER_BUFFER_NORMALS_FINALIZE, SHADER_PATCH_EVALUATION, - SHADER_PATCH_EVALUATION_LIMIT_NORMALS, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS, SHADER_COMP_CUSTOM_DATA_INTERP_1D, @@ -107,7 +106,6 @@ static const char *get_shader_code(int shader_type) return datatoc_common_subdiv_normals_finalize_comp_glsl; } case SHADER_PATCH_EVALUATION: - case SHADER_PATCH_EVALUATION_LIMIT_NORMALS: case SHADER_PATCH_EVALUATION_FVAR: case SHADER_PATCH_EVALUATION_FACE_DOTS: { return datatoc_common_subdiv_patch_evaluation_comp_glsl; @@ -159,9 +157,6 @@ static const char *get_shader_name(int shader_type) case SHADER_PATCH_EVALUATION: { return "subdiv patch evaluation"; } - case SHADER_PATCH_EVALUATION_LIMIT_NORMALS: { - return "subdiv patch evaluation limit normals"; - } case SHADER_PATCH_EVALUATION_FVAR: { return "subdiv patch evaluation face-varying"; } @@ -199,13 +194,7 @@ static GPUShader *get_patch_evaluation_shader(int shader_type) const char *compute_code = get_shader_code(shader_type); const char *defines = nullptr; - if (shader_type == SHADER_PATCH_EVALUATION_LIMIT_NORMALS) { - defines = - "#define OSD_PATCH_BASIS_GLSL\n" - "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" - "#define LIMIT_NORMALS\n"; - } - else if (shader_type == SHADER_PATCH_EVALUATION_FVAR) { + if (shader_type == SHADER_PATCH_EVALUATION_FVAR) { defines = "#define OSD_PATCH_BASIS_GLSL\n" "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" @@ -246,7 +235,6 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines) { if (ELEM(shader_type, SHADER_PATCH_EVALUATION, - SHADER_PATCH_EVALUATION_LIMIT_NORMALS, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS)) { return get_patch_evaluation_shader(shader_type); @@ -592,6 +580,67 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache) SUBDIV_COARSE_FACE_FLAG_ACTIVE) \ << SUBDIV_COARSE_FACE_FLAG_OFFSET) +static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act) +{ + if (f == nullptr) { + /* May happen during mapped extraction. */ + return 0; + } + + uint32_t flag = 0; + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; + } + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; + } + if (f == efa_act) { + flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; + } + const int loopstart = BM_elem_index_get(f->l_first); + return (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); +} + +static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm, + BMFace *efa_act, + uint32_t *flags_data) +{ + BMFace *f; + BMIter iter; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int index = BM_elem_index_get(f); + flags_data[index] = compute_coarse_face_flag(f, efa_act); + } +} + +static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data) +{ + for (int i = 0; i < mesh->totpoly; i++) { + uint32_t flag = 0; + if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) { + flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH; + } + flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); + } +} + +static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh, + BMesh *bm, + MeshRenderData *mr, + uint32_t *flags_data) +{ + if (bm == nullptr) { + draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + return; + } + + for (int i = 0; i < mesh->totpoly; i++) { + BMFace *f = bm_original_face_get(mr, i); + flags_data[i] = compute_coarse_face_flag(f, mr->efa_act); + } +} + static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cache, Mesh *mesh, MeshRenderData *mr) @@ -611,56 +660,13 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach uint32_t *flags_data = (uint32_t *)(GPU_vertbuf_get_data(cache->extra_coarse_face_data)); if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = cache->bm; - BMFace *f; - BMIter iter; - - /* Ensure all current elements follow new customdata layout. */ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int index = BM_elem_index_get(f); - uint32_t flag = 0; - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; - } - if (f == mr->efa_act) { - flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; - } - const int loopstart = BM_elem_index_get(f->l_first); - flags_data[index] = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } + draw_subdiv_cache_extra_coarse_face_data_bm(cache->bm, mr->efa_act, flags_data); } else if (mr->extract_type == MR_EXTRACT_MAPPED) { - for (int i = 0; i < mesh->totpoly; i++) { - BMFace *f = bm_original_face_get(mr, i); - uint32_t flag = 0; - - if (f) { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; - } - if (f == mr->efa_act) { - flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; - } - const int loopstart = BM_elem_index_get(f->l_first); - flag = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } - flags_data[i] = flag; - } + draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data); } else { - for (int i = 0; i < mesh->totpoly; i++) { - uint32_t flag = 0; - if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) { - flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } + draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); } /* Make sure updated data is re-uploaded. */ @@ -1176,9 +1182,7 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache, GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1); } -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, - GPUVertBuf *pos_nor, - const bool do_limit_normals) +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor) { Subdiv *subdiv = cache->subdiv; OpenSubdiv_Evaluator *evaluator = subdiv->evaluator; @@ -1203,8 +1207,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader( - do_limit_normals ? SHADER_PATCH_EVALUATION_LIMIT_NORMALS : SHADER_PATCH_EVALUATION); + GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); GPU_vertbuf_bind_as_ssbo(src_buffer, 0); @@ -1299,7 +1302,8 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, GPUVertBuf *src_data, GPUVertBuf *dst_data, int dimensions, - int dst_offset) + int dst_offset, + bool compress_to_u16) { GPUShader *shader = nullptr; @@ -1319,10 +1323,17 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, "#define DIMENSIONS 3\n"); } else if (dimensions == 4) { - shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, - "#define SUBDIV_POLYGON_OFFSET\n" - "#define DIMENSIONS 4\n" - "#define GPU_FETCH_U16_TO_FLOAT\n"); + if (compress_to_u16) { + shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, + "#define SUBDIV_POLYGON_OFFSET\n" + "#define DIMENSIONS 4\n" + "#define GPU_FETCH_U16_TO_FLOAT\n"); + } + else { + shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, + "#define SUBDIV_POLYGON_OFFSET\n" + "#define DIMENSIONS 4\n"); + } } else { /* Crash if dimensions are not supported. */ @@ -1376,6 +1387,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, GPUVertBuf *pos_nor, GPUVertBuf *face_adjacency_offsets, GPUVertBuf *face_adjacency_lists, + GPUVertBuf *vertex_loop_map, GPUVertBuf *vertex_normals) { GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_NORMALS_ACCUMULATE, nullptr); @@ -1386,6 +1398,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); GPU_vertbuf_bind_as_ssbo(face_adjacency_offsets, binding_point++); GPU_vertbuf_bind_as_ssbo(face_adjacency_lists, binding_point++); + GPU_vertbuf_bind_as_ssbo(vertex_loop_map, binding_point++); GPU_vertbuf_bind_as_ssbo(vertex_normals, binding_point++); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_verts); @@ -1785,9 +1798,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool UNUSED(use_subsurf_fdots), + const bool /*use_subsurf_fdots*/, const ToolSettings *ts, - const bool UNUSED(use_hide), + const bool /*use_hide*/, OpenSubdiv_EvaluatorCache *evaluator_cache) { SubsurfModifierData *smd = BKE_object_get_last_subsurf_modifier(ob); @@ -1833,8 +1846,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, draw_cache->subdiv = subdiv; draw_cache->optimal_display = optimal_display; draw_cache->num_subdiv_triangles = tris_count_from_number_of_loops(draw_cache->num_subdiv_loops); - /* We can only evaluate limit normals if the patches are adaptive. */ - draw_cache->do_limit_normals = settings.is_adaptive; draw_cache->use_custom_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh_eval->flag & ME_AUTOSMOOTH) && diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index fcfaf404fc2..2897234f4dc 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -412,7 +412,7 @@ bool DRW_object_is_flat(Object *ob, int *r_axis) if (!ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_MBALL, diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 440f74af64b..2886fe53879 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2774,7 +2774,7 @@ void DRW_draw_depth_object( GPU_uniformbuf_free(ubo); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; } diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 3b35b8c1f9d..95691a0df68 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -514,7 +514,7 @@ static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4]) case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob_data; BKE_curve_texspace_ensure(cu); texcoloc = cu->loc; diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 6714ba571e5..bd02df6d48b 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -51,7 +51,6 @@ typedef struct DRWSubdivCache { struct BMesh *bm; struct Subdiv *subdiv; bool optimal_display; - bool do_limit_normals; bool use_custom_loop_normals; /* Coordinates used to evaluate patches for UVs, positions, and normals. */ @@ -165,6 +164,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor, struct GPUVertBuf *face_adjacency_offsets, struct GPUVertBuf *face_adjacency_lists, + struct GPUVertBuf *vertex_loop_map, struct GPUVertBuf *vertex_normals); void draw_subdiv_finalize_normals(const DRWSubdivCache *cache, @@ -176,15 +176,14 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache, GPUVertBuf *src_custom_normals, GPUVertBuf *pos_nor); -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, - struct GPUVertBuf *pos_nor, - bool do_limit_normals); +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor); void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, struct GPUVertBuf *src_data, struct GPUVertBuf *dst_data, int dimensions, - int dst_offset); + int dst_offset, + bool compress_to_u16); void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, struct GPUVertBuf *uvs, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index d5e34bc082e..4f4aa764fbc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -402,7 +402,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, /* Ensure data is uploaded properly. */ GPU_vertbuf_tag_dirty(src_data); draw_subdiv_interp_custom_data( - subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0); + subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0, false); GPU_vertbuf_discard(src_data); } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index bd7f1ba0128..22fda284a74 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -217,14 +217,12 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, void *UNUSED(data)) { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer); - const bool do_limit_normals = subdiv_cache->do_limit_normals && - !subdiv_cache->use_custom_loop_normals; /* Initialize the vertex buffer, it was already allocated. */ GPU_vertbuf_init_build_on_device( vbo, get_pos_nor_format(), subdiv_cache->num_subdiv_loops + mr->loop_loose_len); - draw_subdiv_extract_pos_nor(subdiv_cache, vbo, do_limit_normals); + draw_subdiv_extract_pos_nor(subdiv_cache, vbo); if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; @@ -243,14 +241,15 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device( dst_custom_normals, get_custom_normals_format(), subdiv_cache->num_subdiv_loops); - draw_subdiv_interp_custom_data(subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0); + draw_subdiv_interp_custom_data( + subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0, false); draw_subdiv_finalize_custom_normals(subdiv_cache, dst_custom_normals, vbo); GPU_vertbuf_discard(src_custom_normals); GPU_vertbuf_discard(dst_custom_normals); } - else if (!do_limit_normals) { + else { /* We cannot evaluate vertex normals using the limit surface, so compute them manually. */ GPUVertBuf *subdiv_loop_subdiv_vert_index = draw_subdiv_build_origindex_buffer( subdiv_cache->subdiv_loop_subdiv_vert_index, subdiv_cache->num_subdiv_loops); @@ -263,6 +262,7 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, vbo, subdiv_cache->subdiv_vertex_face_adjacency_offsets, subdiv_cache->subdiv_vertex_face_adjacency, + subdiv_loop_subdiv_vert_index, vertex_normals); draw_subdiv_finalize_normals(subdiv_cache, vertex_normals, subdiv_loop_subdiv_vert_index, vbo); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index 78c215845e0..96595df9276 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -151,7 +151,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device( subdiv_mask_vbo, &mask_format, subdiv_cache->num_subdiv_loops); - draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0); + draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0, false); } /* Then, gather face sets. */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 209168750e7..225d1676151 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -16,22 +16,26 @@ #include "extract_mesh.h" +#include "draw_subdivision.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +static void extract_tan_init_common(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertFormat *format, + GPUVertCompType comp_type, + GPUVertFetchMode fetch_mode, + CustomData *r_loop_data, + int *r_v_len, + int *r_tan_len, + char r_tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME], + bool *r_use_orco_tan) { - GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; - GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; - - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); + GPU_vertformat_deinterleave(format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; @@ -41,7 +45,6 @@ static void extract_tan_ex_init(const MeshRenderData *mr, bool use_orco_tan = cache->cd_used.tan_orco != 0; int tan_len = 0; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; /* FIXME(T91838): This is to avoid a crash when orco tangent was requested but there are valid * uv layers. It would be better to fix the root cause. */ @@ -57,17 +60,17 @@ static void extract_tan_ex_init(const MeshRenderData *mr, GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); /* Tangent layer name. */ BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode); /* Active render layer name. */ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "t"); + GPU_vertformat_alias_add(format, "t"); } /* Active display layer name. */ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "at"); + GPU_vertformat_alias_add(format, "at"); } - BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); + BLI_strncpy(r_tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); } } if (use_orco_tan && orco == nullptr) { @@ -94,20 +97,19 @@ static void extract_tan_ex_init(const MeshRenderData *mr, } /* Start Fresh */ - CustomData loop_data; - CustomData_reset(&loop_data); + CustomData_reset(r_loop_data); if (tan_len != 0 || use_orco_tan) { short tangent_mask = 0; bool calc_active_tangent = false; if (mr->extract_type == MR_EXTRACT_BMESH) { BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, calc_active_tangent, - tangent_names, + r_tangent_names, tan_len, mr->poly_normals, mr->loop_normals, orco, - &loop_data, + r_loop_data, mr->loop_len, &tangent_mask); } @@ -120,13 +122,13 @@ static void extract_tan_ex_init(const MeshRenderData *mr, mr->tri_len, cd_ldata, calc_active_tangent, - tangent_names, + r_tangent_names, tan_len, mr->vert_normals, mr->poly_normals, mr->loop_normals, orco, - &loop_data, + r_loop_data, mr->loop_len, &tangent_mask); } @@ -134,12 +136,12 @@ static void extract_tan_ex_init(const MeshRenderData *mr, if (use_orco_tan) { char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); + const char *layer_name = CustomData_get_layer_name(r_loop_data, CD_TANGENT, 0); GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - GPU_vertformat_alias_add(&format, "t"); - GPU_vertformat_alias_add(&format, "at"); + GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_alias_add(format, "t"); + GPU_vertformat_alias_add(format, "at"); } if (orco_allocated) { @@ -147,12 +149,42 @@ static void extract_tan_ex_init(const MeshRenderData *mr, } int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + if (format->attr_len == 0) { + GPU_vertformat_attr_add(format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); /* VBO will not be used, only allocate minimum of memory. */ v_len = 1; } + *r_use_orco_tan = use_orco_tan; + *r_v_len = v_len; + *r_tan_len = tan_len; +} + +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) +{ + GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; + GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; + + GPUVertFormat format = {0}; + CustomData loop_data; + int v_len = 0; + int tan_len = 0; + bool use_orco_tan; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + extract_tan_init_common(mr, + cache, + &format, + comp_type, + fetch_mode, + &loop_data, + &v_len, + &tan_len, + tangent_names, + &use_orco_tan); + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -211,10 +243,92 @@ static void extract_tan_init(const MeshRenderData *mr, extract_tan_ex_init(mr, cache, vbo, false); } +static GPUVertFormat *get_coarse_tan_format() +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "tan", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + return &format; +} + +static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, + const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *UNUSED(data)) +{ + GPUVertCompType comp_type = GPU_COMP_F32; + GPUVertFetchMode fetch_mode = GPU_FETCH_FLOAT; + GPUVertFormat format = {0}; + CustomData loop_data; + int coarse_len = 0; + int tan_len = 0; + bool use_orco_tan; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + extract_tan_init_common(mr, + cache, + &format, + comp_type, + fetch_mode, + &loop_data, + &coarse_len, + &tan_len, + tangent_names, + &use_orco_tan); + + GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); + GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); + + GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); + /* Dynamic as we upload and interpolate layers one at a time. */ + GPU_vertbuf_init_with_format_ex(coarse_vbo, get_coarse_tan_format(), GPU_USAGE_DYNAMIC); + GPU_vertbuf_data_alloc(coarse_vbo, coarse_len); + + /* Index of the tangent layer in the compact buffer. Used layers are stored in a single buffer. + */ + int pack_layer_index = 0; + for (int i = 0; i < tan_len; i++) { + float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(&loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + copy_v3_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; + tan_data++; + } + + /* Ensure data is uploaded properly. */ + GPU_vertbuf_tag_dirty(coarse_vbo); + /* Include stride in offset. */ + const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++; + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false); + } + if (use_orco_tan) { + float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + copy_v3_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; + tan_data++; + } + + /* Ensure data is uploaded properly. */ + GPU_vertbuf_tag_dirty(coarse_vbo); + /* Include stride in offset. */ + const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++; + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true); + } + + CustomData_free(&loop_data, mr->loop_len); + GPU_vertbuf_discard(coarse_vbo); +} + constexpr MeshExtract create_extractor_tan() { MeshExtract extractor = {nullptr}; extractor.init = extract_tan_init; + extractor.init_subdiv = extract_tan_init_subdiv; extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI; extractor.data_size = 0; extractor.use_threading = false; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 138ff9fd1ff..7a8f4a9a17e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -164,7 +164,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, /* Ensure data is uploaded properly. */ GPU_vertbuf_tag_dirty(src_data); - draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset); + draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc index 2e30d6bdfcf..89aa16ca0c7 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc @@ -153,10 +153,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, } static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buffer, - void *UNUSED(data)) + void *_data) { Mesh *coarse_mesh = subdiv_cache->mesh; GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer); @@ -168,32 +168,24 @@ static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device(vbo, &format, subdiv_cache->num_subdiv_loops); GPUVertBuf *coarse_weights = GPU_vertbuf_calloc(); - GPU_vertbuf_init_with_format(coarse_weights, &format); - GPU_vertbuf_data_alloc(coarse_weights, coarse_mesh->totloop); - float *coarse_weights_data = static_cast<float *>(GPU_vertbuf_get_data(coarse_weights)); + extract_weights_init(mr, cache, coarse_weights, _data); - const DRW_MeshWeightState *wstate = &cache->weight_state; - const MDeformVert *dverts = static_cast<const MDeformVert *>( - CustomData_get_layer(&coarse_mesh->vdata, CD_MDEFORMVERT)); - - for (int i = 0; i < coarse_mesh->totpoly; i++) { - const MPoly *mpoly = &coarse_mesh->mpoly[i]; - - for (int loop_index = mpoly->loopstart; loop_index < mpoly->loopstart + mpoly->totloop; - loop_index++) { - const MLoop *ml = &coarse_mesh->mloop[loop_index]; - - if (dverts != nullptr) { - const MDeformVert *dvert = &dverts[ml->v]; - coarse_weights_data[loop_index] = evaluate_vertex_weight(dvert, wstate); - } - else { - coarse_weights_data[loop_index] = evaluate_vertex_weight(nullptr, wstate); - } + if (mr->extract_type != MR_EXTRACT_BMESH) { + for (int i = 0; i < coarse_mesh->totpoly; i++) { + const MPoly *mpoly = &coarse_mesh->mpoly[i]; + extract_weights_iter_poly_mesh(mr, mpoly, i, _data); + } + } + else { + BMIter f_iter; + BMFace *efa; + int face_index = 0; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, face_index) { + extract_weights_iter_poly_bm(mr, efa, face_index, _data); } } - draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0); + draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0, false); GPU_vertbuf_discard(coarse_weights); } diff --git a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl index df0016761e2..097ae0b3913 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly restrict buffer sourceBuffer { diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl index f11c0f6427e..3cbb9f980f3 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputEdgeOrigIndex { diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl index 3257ebdae17..3dccc82541e 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ /* Generate triangles from subdivision quads indices. */ diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl index e6538d80111..5d71c5e4bb8 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl @@ -140,6 +140,13 @@ void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor) set_vertex_nor(vertex_data, nor, 0); } +void add_newell_cross_v3_v3v3(inout vec3 n, vec3 v_prev, vec3 v_curr) +{ + n[0] += (v_prev[1] - v_curr[1]) * (v_prev[2] + v_curr[2]); + n[1] += (v_prev[2] - v_curr[2]) * (v_prev[0] + v_curr[0]); + n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]); +} + #define ORIGINDEX_NONE -1 #ifdef SUBDIV_POLYGON_OFFSET diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl index 575090472b1..0665cadfd2d 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVertexData { @@ -16,11 +16,33 @@ layout(std430, binding = 2) readonly buffer faceAdjacencyLists uint face_adjacency_lists[]; }; -layout(std430, binding = 3) writeonly buffer vertexNormals +layout(std430, binding = 3) readonly buffer vertexLoopMap +{ + uint vert_loop_map[]; +}; + +layout(std430, binding = 4) writeonly buffer vertexNormals { vec3 normals[]; }; +void find_prev_and_next_vertex_on_face( + uint face_index, uint vertex_index, out uint curr, out uint next, out uint prev) +{ + uint start_loop_index = face_index * 4; + + for (uint i = 0; i < 4; i++) { + uint subdiv_vert_index = vert_loop_map[start_loop_index + i]; + + if (subdiv_vert_index == vertex_index) { + curr = i; + next = (i + 1) % 4; + prev = (i + 4 - 1) % 4; + break; + } + } +} + void main() { uint vertex_index = get_global_invocation_index(); @@ -39,18 +61,37 @@ void main() uint adjacent_face = face_adjacency_lists[first_adjacent_face_offset + i]; uint start_loop_index = adjacent_face * 4; - /* Compute face normal. */ - vec3 adjacent_verts[3]; - for (uint j = 0; j < 3; j++) { - adjacent_verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]); + /* Compute the face normal using Newell's method. */ + vec3 verts[4]; + for (uint j = 0; j < 4; j++) { + verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]); } - vec3 face_normal = normalize( - cross(adjacent_verts[1] - adjacent_verts[0], adjacent_verts[2] - adjacent_verts[0])); - accumulated_normal += face_normal; + vec3 face_normal = vec3(0.0); + add_newell_cross_v3_v3v3(face_normal, verts[0], verts[1]); + add_newell_cross_v3_v3v3(face_normal, verts[1], verts[2]); + add_newell_cross_v3_v3v3(face_normal, verts[2], verts[3]); + add_newell_cross_v3_v3v3(face_normal, verts[3], verts[0]); + + /* Accumulate angle weighted normal. */ + uint curr_vert = 0; + uint next_vert = 0; + uint prev_vert = 0; + find_prev_and_next_vertex_on_face( + adjacent_face, vertex_index, curr_vert, next_vert, prev_vert); + + vec3 curr_co = verts[curr_vert]; + vec3 prev_co = verts[next_vert]; + vec3 next_co = verts[prev_vert]; + + vec3 edvec_prev = normalize(prev_co - curr_co); + vec3 edvec_next = normalize(curr_co - next_co); + + float fac = acos(-dot(edvec_prev, edvec_next)); + + accumulated_normal += face_normal * fac; } - float weight = 1.0 / float(number_of_adjacent_faces); vec3 normal = normalize(accumulated_normal); normals[vertex_index] = normal; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl index c2e0e752783..e6a56ff02c7 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ #ifdef CUSTOM_NORMALS struct CustomNormal { diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl index 5dd7decf663..65cf4ebb90f 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ /* Source buffer. */ layout(std430, binding = 0) buffer src_buffer @@ -394,12 +394,8 @@ void main() evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, pos, du, dv); -# if defined(LIMIT_NORMALS) - vec3 nor = normalize(cross(du, dv)); -# else /* This will be computed later. */ vec3 nor = vec3(0.0); -# endif int origindex = input_vert_origindex[loop_index]; uint flag = 0; diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl index 6c76cd41ca4..2161f0b28a9 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVertexData { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl index ea73b9482d3..a8c9b7183eb 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVerts { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl index e897fb3f3c0..230484048b1 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly buffer inputCoarseData { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl index 41a8df3cf82..b7e04e240fb 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly buffer inputVertexData { @@ -38,13 +38,18 @@ void main() } } else { - /* Face is flat shaded, compute flat face normal from an inscribed triangle. */ - vec3 verts[3]; - for (int i = 0; i < 3; i++) { - verts[i] = get_vertex_pos(pos_nor[start_loop_index + i]); - } - - vec3 face_normal = normalize(cross(verts[1] - verts[0], verts[2] - verts[0])); + vec3 v0 = get_vertex_pos(pos_nor[start_loop_index + 0]); + vec3 v1 = get_vertex_pos(pos_nor[start_loop_index + 1]); + vec3 v2 = get_vertex_pos(pos_nor[start_loop_index + 2]); + vec3 v3 = get_vertex_pos(pos_nor[start_loop_index + 3]); + + vec3 face_normal = vec3(0.0); + add_newell_cross_v3_v3v3(face_normal, v0, v1); + add_newell_cross_v3_v3v3(face_normal, v1, v2); + add_newell_cross_v3_v3v3(face_normal, v2, v3); + add_newell_cross_v3_v3v3(face_normal, v3, v0); + + face_normal = normalize(face_normal); for (int i = 0; i < 4; i++) { output_lnor[start_loop_index + i] = face_normal; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl index 7182ce57ad3..77b599f6252 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ struct SculptData { uint face_set_color; diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 4780352e5dc..edb6d188ab8 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -668,7 +668,7 @@ static int acf_object_icon(bAnimListElem *ale) return ICON_OUTLINER_OB_MESH; case OB_CAMERA: return ICON_OUTLINER_OB_CAMERA; - case OB_CURVE: + case OB_CURVES_LEGACY: return ICON_OUTLINER_OB_CURVE; case OB_MBALL: return ICON_OUTLINER_OB_META; @@ -4601,7 +4601,7 @@ void ANIM_channel_draw( /* Draw slider: * - Even if we can draw sliders for this view, * we must also check that the channel-type supports them - * (only only F-Curves really can support them for now). + * (only F-Curves really can support them for now). * - Slider should start before the toggles (if they're visible) * to keep a clean line down the side. */ @@ -5336,7 +5336,7 @@ void ANIM_channel_draw_widgets(const bContext *C, /* Draw slider: * - Even if we can draw sliders for this view, we must also check that the channel-type - * supports them (only only F-Curves really can support them for now). + * supports them (only F-Curves really can support them for now). * - To make things easier, we use RNA-autobuts for this so that changes are * reflected immediately, wherever they occurred. * BUT, we don't use the layout engine, otherwise we'd get wrong alignment, diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index d6163f21c79..0389e57627a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -2530,9 +2530,9 @@ static size_t animdata_filter_ds_obdata( expanded = FILTER_LAM_OBJD(la); break; } - case OB_CURVE: /* ------- Curve ---------- */ - case OB_SURF: /* ------- Nurbs Surface ---------- */ - case OB_FONT: /* ------- Text Curve ---------- */ + case OB_CURVES_LEGACY: /* ------- Curve ---------- */ + case OB_SURF: /* ------- Nurbs Surface ---------- */ + case OB_FONT: /* ------- Text Curve ---------- */ { Curve *cu = (Curve *)ob->data; diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 9566402ad85..95125516fe8 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -700,7 +700,7 @@ static void MARKER_OT_add(wmOperatorType *ot) typedef struct MarkerMove { SpaceLink *slink; ListBase *markers; - int event_type; /* store invoke-event, to verify */ + short event_type, event_val; /* store invoke-event, to verify */ int *oldframe, evtx, firstx; NumInput num; } MarkerMove; @@ -844,6 +844,7 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve mm->evtx = event->xy[0]; mm->firstx = event->xy[0]; mm->event_type = event->type; + mm->event_val = event->val; /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -941,7 +942,7 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even case EVT_PADENTER: case LEFTMOUSE: case MIDDLEMOUSE: - if (WM_event_is_modal_tweak_exit(event, mm->event_type)) { + if (WM_event_is_modal_drag_exit(event, mm->event_type, mm->event_val)) { ed_marker_move_exit(C, op); WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); @@ -960,7 +961,13 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even mm->evtx = event->xy[0]; fac = ((float)(event->xy[0] - mm->firstx) * dx); - apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); + apply_keyb_grid((event->modifier & KM_SHIFT) != 0, + (event->modifier & KM_CTRL) != 0, + &fac, + 0.0, + FPS, + 0.1 * FPS, + 0); RNA_int_set(op->ptr, "frames", (int)fac); ed_marker_move_apply(C, op); diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 539227933cf..2c99cd1cc1f 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -340,6 +340,55 @@ static void motionpath_free_free_tree_data(ListBase *targets) } } +void animviz_motionpath_compute_range(Object *ob, Scene *scene) +{ + struct AnimKeylist *keylist = ED_keylist_create(); + bAnimVizSettings *avs; + if (ob->mode == OB_MODE_POSE) { + avs = &ob->pose->avs; + bArmature *arm = ob->data; + + if (!ELEM(NULL, ob->adt, ob->adt->action, arm->adt)) { + /* Loop through all the fcurves and get only the keylists for the bone location fcurves */ + LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) { + if (strstr(fcu->rna_path, "pose.bones[") && strstr(fcu->rna_path, "location")) { + fcurve_to_keylist(arm->adt, fcu, keylist, 0); + } + } + } + } + else { + avs = &ob->avs; + + if (!ELEM(NULL, ob->adt, ob->adt->action)) { + /* Loop through all the fcurves and get only the keylists for the location fcurves */ + LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) { + if (strcmp(fcu->rna_path, "location") == 0) { + fcurve_to_keylist(ob->adt, fcu, keylist, 0); + } + } + } + } + + if (ED_keylist_is_empty(keylist) || (avs->path_range == MOTIONPATH_RANGE_SCENE)) { + /* Apply scene frame range if no keys where found or if scene range is selected */ + avs->path_sf = PSFRA; + avs->path_ef = PEFRA; + } + else { + /* Compute keys range */ + Range2f frame_range; + const bool only_selected = avs->path_range == MOTIONPATH_RANGE_KEYS_SELECTED; + /* Get range for all keys if selected_only is false or if no keys are selected */ + if (!(only_selected && ED_keylist_selected_keys_frame_range(keylist, &frame_range))) { + ED_keylist_all_keys_frame_range(keylist, &frame_range); + } + avs->path_sf = frame_range.min; + avs->path_ef = frame_range.max; + } + ED_keylist_free(keylist); +} + void animviz_calc_motionpaths(Depsgraph *depsgraph, Main *bmain, Scene *scene, diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 3f9592fb4ae..0b795fea278 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -304,7 +304,21 @@ const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) return &keylist->key_columns; } -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +static void keylist_first_last(const struct AnimKeylist *keylist, + const struct ActKeyColumn **first_column, + const struct ActKeyColumn **last_column) +{ + if (keylist->is_runtime_initialized) { + *first_column = &keylist->runtime.key_columns[0]; + *last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; + } + else { + *first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first); + *last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last); + } +} + +bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) { BLI_assert(r_frame_range); @@ -314,13 +328,33 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_ const ActKeyColumn *first_column; const ActKeyColumn *last_column; - if (keylist->is_runtime_initialized) { - first_column = &keylist->runtime.key_columns[0]; - last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; + keylist_first_last(keylist, &first_column, &last_column); + r_frame_range->min = first_column->cfra; + r_frame_range->max = last_column->cfra; + + return true; +} + +bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, + Range2f *r_frame_range) +{ + BLI_assert(r_frame_range); + + if (ED_keylist_is_empty(keylist)) { + return false; } - else { - first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first); - last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last); + + const ActKeyColumn *first_column; + const ActKeyColumn *last_column; + keylist_first_last(keylist, &first_column, &last_column); + while (first_column && !(first_column->sel & SELECT)) { + first_column = first_column->next; + } + while (last_column && !(last_column->sel & SELECT)) { + last_column = last_column->prev; + } + if (!first_column || !last_column || first_column == last_column) { + return false; } r_frame_range->min = first_column->cfra; r_frame_range->max = last_column->cfra; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index e227e69f9e1..128126e515e 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -220,8 +220,8 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven bAnimVizSettings *avs = &ob->pose->avs; PointerRNA avs_ptr; - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); + RNA_enum_set(op->ptr, "display_type", avs->path_type); + RNA_enum_set(op->ptr, "range", avs->path_range); RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); @@ -229,7 +229,7 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven /* show popup dialog to allow editing of range... */ /* FIXME: hard-coded dimensions here are just arbitrary. */ - return WM_operator_props_dialog_popup(C, op, 200); + return WM_operator_props_dialog_popup(C, op, 270); } /* For the object with pose/action: create path curves for selected bones @@ -249,8 +249,9 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) bAnimVizSettings *avs = &ob->pose->avs; PointerRNA avs_ptr; - avs->path_sf = RNA_int_get(op->ptr, "start_frame"); - avs->path_ef = RNA_int_get(op->ptr, "end_frame"); + avs->path_type = RNA_enum_get(op->ptr, "display_type"); + avs->path_range = RNA_enum_get(op->ptr, "range"); + animviz_motionpath_compute_range(ob, scene); RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); @@ -258,7 +259,6 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) /* set up path data for bones being calculated */ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) { - /* verify makes sure that the selected bone has a bone with the appropriate settings */ animviz_verify_motionpaths(op->reports, scene, ob, pchan); } CTX_DATA_END; @@ -281,6 +281,19 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool pose_calculate_paths_poll(bContext *C) +{ + if (!ED_operator_posemode_exclusive(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + bArmature *arm = ob->data; + if (ELEM(NULL, ob, arm, ob->pose)) { + return false; + } + return true; +} + void POSE_OT_paths_calculate(wmOperatorType *ot) { /* identifiers */ @@ -291,30 +304,24 @@ void POSE_OT_paths_calculate(wmOperatorType *ot) /* api callbacks */ ot->invoke = pose_calculate_paths_invoke; ot->exec = pose_calculate_paths_exec; - ot->poll = ED_operator_posemode_exclusive; + ot->poll = pose_calculate_paths_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_int(ot->srna, - "start_frame", - 1, - MINAFRAME, - MAXFRAME, - "Start", - "First frame to calculate bone paths on", - MINFRAME, - MAXFRAME / 2.0); - RNA_def_int(ot->srna, - "end_frame", - 250, - MINAFRAME, - MAXFRAME, - "End", - "Last frame to calculate bone paths on", - MINFRAME, - MAXFRAME / 2.0); + RNA_def_enum(ot->srna, + "display_type", + rna_enum_motionpath_display_type_items, + MOTIONPATH_TYPE_RANGE, + "Display type", + ""); + RNA_def_enum(ot->srna, + "range", + rna_enum_motionpath_range_items, + MOTIONPATH_RANGE_SCENE, + "Computation Range", + ""); RNA_def_enum(ot->srna, "bake_location", @@ -336,7 +343,7 @@ static bool pose_update_paths_poll(bContext *C) return false; } -static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) +static int pose_update_paths_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); Scene *scene = CTX_data_scene(C); @@ -344,6 +351,13 @@ static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) if (ELEM(NULL, ob, scene)) { return OPERATOR_CANCELLED; } + animviz_motionpath_compute_range(ob, scene); + + /* set up path data for bones being calculated */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) { + animviz_verify_motionpaths(op->reports, scene, ob, pchan); + } + CTX_DATA_END; /* Calculate the bones that now have motion-paths. */ /* TODO: only make for the selected bones? */ @@ -427,7 +441,7 @@ static int pose_clear_paths_exec(bContext *C, wmOperator *op) /* operator callback/wrapper */ static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return pose_clear_paths_exec(C, op); diff --git a/source/blender/editors/asset/intern/asset_library_reference.cc b/source/blender/editors/asset/intern/asset_library_reference.cc index 04f77821114..5096b9d653d 100644 --- a/source/blender/editors/asset/intern/asset_library_reference.cc +++ b/source/blender/editors/asset/intern/asset_library_reference.cc @@ -17,9 +17,9 @@ AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryRef bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) { - return (a.type == b.type) && (a.type == ASSET_LIBRARY_CUSTOM) ? - (a.custom_library_index == b.custom_library_index) : - true; + return (a.type == b.type) && + ((a.type == ASSET_LIBRARY_CUSTOM) ? (a.custom_library_index == b.custom_library_index) : + true); } uint64_t AssetLibraryReferenceWrapper::hash() const diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 20a2251b6e1..a33fbb29f85 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -72,7 +72,7 @@ static bool curve_delete_vertices(Object *obedit, View3D *v3d); ListBase *object_editcurve_get(Object *ob) { - if (ob && ELEM(ob->type, OB_CURVE, OB_SURF)) { + if (ob && ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; return &cu->editnurb->nurbs; } @@ -1238,7 +1238,7 @@ void ED_curve_editnurb_load(Main *bmain, Object *obedit) return; } - if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; ListBase newnurb = {NULL, NULL}, oldnurb = cu->nurb; @@ -1273,7 +1273,7 @@ void ED_curve_editnurb_make(Object *obedit) EditNurb *editnurb = cu->editnurb; KeyBlock *actkey; - if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { actkey = BKE_keyblock_from_object(obedit); if (actkey) { @@ -5637,7 +5637,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) } /* First test: curve? */ - if (obedit->type != OB_CURVE) { + if (obedit->type != OB_CURVES_LEGACY) { LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) { as_curve = true; @@ -5646,7 +5646,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) } } - if (obedit->type == OB_CURVE || as_curve) { + if (obedit->type == OB_CURVES_LEGACY || as_curve) { changed = ed_editcurve_extrude(cu, editnurb, v3d); } else { @@ -6715,7 +6715,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; ListBase *editnurb = object_editcurve_get(obedit); - if (obedit->type != OB_CURVE) { + if (obedit->type != OB_CURVES_LEGACY) { continue; } @@ -6874,7 +6874,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) cu = ob_active->data; BLI_movelisttolist(&cu->nurb, &tempbase); - if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) { + if (ob_active->type == OB_CURVES_LEGACY && CU_IS_2D(cu)) { /* Account for mixed 2D/3D curves when joining */ BKE_curve_dimension_update(cu); } @@ -6984,7 +6984,7 @@ static bool match_texture_space_poll(bContext *C) { Object *object = CTX_data_active_object(C); - return object && ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT); + return object && ELEM(object->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT); } static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 2aaebf494a6..d7201495f75 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -53,25 +53,25 @@ static const char *get_curve_defname(int type) if ((type & CU_TYPE) == CU_BEZIER) { switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCircle"); case CU_PRIM_PATH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "CurvePath"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "CurvePath"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve"); } } else { switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCircle"); case CU_PRIM_PATH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsPath"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsPath"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve"); } } } @@ -82,17 +82,17 @@ static const char *get_surf_defname(int type) switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle"); case CU_PRIM_PATCH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfPatch"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch"); case CU_PRIM_SPHERE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfSphere"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere"); case CU_PRIM_DONUT: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfTorus"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfTorus"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Surface"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Surface"); } } @@ -510,11 +510,11 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } if (!isSurf) { /* adding curve */ - if (obedit == NULL || obedit->type != OB_CURVE) { + if (obedit == NULL || obedit->type != OB_CURVES_LEGACY) { const char *name = get_curve_defname(type); Curve *cu; - obedit = ED_object_add_type(C, OB_CURVE, name, loc, rot, true, local_view_bits); + obedit = ED_object_add_type(C, OB_CURVES_LEGACY, name, loc, rot, true, local_view_bits); newob = true; cu = (Curve *)obedit->data; diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 7b68c859b43..888bb2169e0 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -162,7 +162,7 @@ static Object *editcurve_object_from_context(bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; if (BKE_curve_editNurbs_get(cu) != NULL) { return obedit; diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index b2af0643e2c..02c7f3856e8 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1640,7 +1640,9 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) EditFont *ef = cu->editfont; static int accentcode = 0; uintptr_t ascii = event->ascii; - int alt = event->alt, shift = event->shift, ctrl = event->ctrl; + const bool alt = event->modifier & KM_ALT; + const bool shift = event->modifier & KM_SHIFT; + const bool ctrl = event->modifier & KM_CTRL; int event_type = event->type, event_val = event->val; char32_t inserted_text[2] = {0}; diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index d2b7dacbc20..1731d224b3e 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -6,6 +6,7 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../functions ../../makesdna ../../makesrna ../../windowmanager @@ -13,6 +14,7 @@ set(INC ) set(SRC + intern/curves_add.cc intern/curves_ops.cc ) diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc new file mode 100644 index 00000000000..9cde23451dc --- /dev/null +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edcurves + */ + +#include "BLI_rand.hh" + +#include "BKE_curves.hh" + +#include "ED_curves.h" + +namespace blender::ed::curves { + +bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve) +{ + bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size); + + MutableSpan<int> offsets = curves.offsets(); + MutableSpan<float3> positions = curves.positions(); + + float *radius_data = (float *)CustomData_add_layer_named( + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + MutableSpan<float> radii{radius_data, curves.points_size()}; + + for (const int i : offsets.index_range()) { + offsets[i] = points_per_curve * i; + } + + RandomNumberGenerator rng; + + for (const int i : curves.curves_range()) { + const IndexRange curve_range = curves.range_for_curve(i); + MutableSpan<float3> curve_positions = positions.slice(curve_range); + MutableSpan<float> curve_radii = radii.slice(curve_range); + + const float theta = 2.0f * M_PI * rng.get_float(); + const float phi = saacosf(2.0f * rng.get_float() - 1.0f); + + float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; + no = math::normalize(no); + + float3 co = no; + for (int key = 0; key < points_per_curve; key++) { + float t = key / (float)(points_per_curve - 1); + curve_positions[key] = co; + curve_radii[key] = 0.02f * (1.0f - t); + + float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; + co += (offset + no) / points_per_curve; + } + } + + return curves; +} + +} // namespace blender::ed::curves diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index fdda8e636f7..52a55ae1760 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -4,67 +4,8 @@ * \ingroup edcurves */ -#include "BLI_utildefines.h" - #include "ED_curves.h" -#include "ED_object.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "BKE_context.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_types.h" - -static bool curves_sculptmode_toggle_poll(bContext *C) -{ - Object *ob = CTX_data_active_object(C); - if (ob == nullptr) { - return false; - } - if (ob->type != OB_CURVES) { - return false; - } - return true; -} - -static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES; - - if (is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - if (is_mode_set) { - ob->mode = OB_MODE_OBJECT; - } - else { - ob->mode = OB_MODE_SCULPT_CURVES; - } - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); - return OPERATOR_CANCELLED; -} - -static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) -{ - ot->name = "Curve Sculpt Mode Toggle"; - ot->idname = "CURVES_OT_sculptmode_toggle"; - ot->description = "Enter/Exit sculpt mode for curves"; - - ot->exec = curves_sculptmode_toggle_exec; - ot->poll = curves_sculptmode_toggle_poll; - - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; -} void ED_operatortypes_curves() { - WM_operatortype_append(CURVES_OT_sculptmode_toggle); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index aea6d41202e..447fe1005a1 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -147,7 +147,7 @@ static void move3d_get_translate(const wmGizmo *gz, float co_delta[3]) { MoveInteraction *inter = gz->interaction_data; - const float mval_delta[2] = { + const float xy_delta[2] = { event->mval[0] - inter->init.mval[0], event->mval[1] - inter->init.mval[1], }; @@ -155,9 +155,9 @@ static void move3d_get_translate(const wmGizmo *gz, RegionView3D *rv3d = region->regiondata; float co_ref[3]; mul_v3_mat3_m4v3(co_ref, gz->matrix_space, inter->init.prop_co); - const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, co_ref); - ED_view3d_win_to_delta(region, mval_delta, co_delta, zfac); + ED_view3d_win_to_delta(region, xy_delta, zfac, co_delta); float matrix_space_inv[3][3]; copy_m3_m4(matrix_space_inv, gz->matrix_space); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 163b5657326..5ab4a663efe 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -326,8 +326,7 @@ static void annotation_stroke_convertcoords(tGPsdata *p, } else { float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. In general, this @@ -339,13 +338,14 @@ static void annotation_stroke_convertcoords(tGPsdata *p, */ annotation_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec); if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - float mval_f[2]; - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, mval); + ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { @@ -2060,7 +2060,7 @@ static void annotation_draw_apply_event( p->mval[1] = (float)event->mval[1] - y; /* Key to toggle stabilization. */ - if (event->shift && p->paintmode == GP_PAINTMODE_DRAW) { + if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) { /* Using permanent stabilization, shift will deactivate the flag. */ if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { @@ -2075,7 +2075,7 @@ static void annotation_draw_apply_event( } } /* verify key status for straight lines */ - else if (event->ctrl || event->alt) { + else if (event->modifier & (KM_CTRL | KM_ALT)) { if (p->straight[0] == 0) { int dx = abs((int)(p->mval[0] - p->mvalo[0])); int dy = abs((int)(p->mval[1] - p->mvalo[1])); @@ -2299,7 +2299,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } - else if (event->shift) { + else if (event->modifier & KM_SHIFT) { p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 63239fd6341..d4518f21586 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1300,8 +1300,8 @@ static void gpencil_layer_to_curve(bContext *C, /* init the curve object (remove rotation and get curve data from it) * - must clear transforms set on object, as those skew our results */ - ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); - cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); + ob = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, gpl->info); + cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVES_LEGACY); BKE_collection_object_add(bmain, collection, ob); base_new = BKE_view_layer_base_find(view_layer, ob); DEG_relations_tag_update(bmain); /* added object */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 680b313c47b..d734fb2678e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4356,6 +4356,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); const float length = RNA_float_get(op->ptr, "length"); + const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold"); /* sanity checks */ if (ELEM(NULL, gpd)) { @@ -4365,7 +4366,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) /* Go through each editable + selected stroke */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_stroke_sample(gpd, gps, length, true); + BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold); } } GP_EDITABLE_STROKES_END(gpstroke_iter); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 8be34a35ca9..069493025dc 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1097,7 +1097,7 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf) /** * Naive dilate * - * Expand green areas into enclosing red areas. + * Expand green areas into enclosing red or transparent areas. * Using stack prevents creep when replacing colors directly. * <pre> * ----------- @@ -1110,8 +1110,8 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf) */ static bool dilate_shape(ImBuf *ibuf) { -#define IS_RED (color[0] == 1.0f) #define IS_GREEN (color[1] == 1.0f) +#define IS_NOT_GREEN (color[1] != 1.0f) bool done = false; @@ -1140,7 +1140,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v - 1 >= 0) { index = v - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); lt = index; } @@ -1149,7 +1149,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v + 1 <= maxpixel) { index = v + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); rt = index; } @@ -1158,7 +1158,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v + ibuf->x <= max_size) { index = v + ibuf->x; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); tp = index; } @@ -1167,7 +1167,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v - ibuf->x >= 0) { index = v - ibuf->x; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); bm = index; } @@ -1176,7 +1176,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && lt) { index = tp - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1184,7 +1184,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && rt) { index = tp + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1192,7 +1192,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && lt) { index = bm - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1200,7 +1200,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && rt) { index = bm + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1218,8 +1218,8 @@ static bool dilate_shape(ImBuf *ibuf) return done; -#undef IS_RED #undef IS_GREEN +#undef IS_NOT_GREEN } /** @@ -1239,7 +1239,7 @@ static bool contract_shape(ImBuf *ibuf) const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; const int max_size = (ibuf->x * ibuf->y) - 1; - /* Detect if pixel is near of no green pixels and mark green to be cleared. */ + /* Detect if pixel is near of no green pixels and mark green pixel to be cleared. */ for (int row = 0; row < ibuf->y; row++) { if (!is_row_filled(ibuf, row)) { continue; @@ -2172,7 +2172,8 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN; - const bool is_inverted = (is_brush_inv && !event->ctrl) || (!is_brush_inv && event->ctrl); + const bool is_inverted = (is_brush_inv && (event->modifier & KM_CTRL) == 0) || + (!is_brush_inv && (event->modifier & KM_CTRL) != 0); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd); const bool do_extend = (tgpf->fill_extend_fac > 0.0f); const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || @@ -2313,7 +2314,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PAGEUPKEY: case WHEELUPMOUSE: if (tgpf->oldkey == 1) { - tgpf->fill_extend_fac -= (event->shift) ? 0.01f : 0.1f; + tgpf->fill_extend_fac -= (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; CLAMP_MIN(tgpf->fill_extend_fac, 0.0f); gpencil_update_extend(tgpf); } @@ -2321,7 +2322,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PAGEDOWNKEY: case WHEELDOWNMOUSE: if (tgpf->oldkey == 1) { - tgpf->fill_extend_fac += (event->shift) ? 0.01f : 0.1f; + tgpf->fill_extend_fac += (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; CLAMP_MAX(tgpf->fill_extend_fac, 100.0f); gpencil_update_extend(tgpf); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a6691a12505..5409cea2a2a 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -235,7 +235,7 @@ typedef struct tGPsdata { /** key used for invoking the operator */ short keymodifier; /** shift modifier flag */ - short shift; + bool shift; /** size in pixels for uv calculation */ float totpixlen; /** Special mode for fill brush. */ @@ -447,21 +447,21 @@ static void gpencil_stroke_convertcoords(tGPsdata *p, } float mval_prj[2]; - float rvec[3], dvec[3]; - float mval_f[2]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. In general, this * works OK, but it could of course be improved. */ gpencil_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec); if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, mval); + ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { @@ -2841,11 +2841,11 @@ static void gpencil_draw_apply_event(bContext *C, * add any x,y override position */ copy_v2fl_v2i(p->mval, event->mval); - p->shift = event->shift; + p->shift = (event->modifier & KM_SHIFT) != 0; /* verify direction for straight lines and guides */ if ((is_speed_guide) || - (event->alt && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { + ((event->modifier & KM_ALT) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { if (p->straight == 0) { int dx = (int)fabsf(p->mval[0] - p->mvali[0]); int dy = (int)fabsf(p->mval[1] - p->mvali[1]); @@ -2886,13 +2886,13 @@ static void gpencil_draw_apply_event(bContext *C, /* special eraser modes */ if (p->paintmode == GP_PAINTMODE_ERASER) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { p->flags |= GP_PAINTFLAG_HARD_ERASER; } else { p->flags &= ~GP_PAINTFLAG_HARD_ERASER; } - if (event->alt) { + if (event->modifier & KM_ALT) { p->flags |= GP_PAINTFLAG_STROKE_ERASER; } else { @@ -3116,11 +3116,11 @@ static void gpencil_guide_event_handling(bContext *C, else if ((event->type == EVT_LKEY) && (event->val == KM_RELEASE)) { add_notifier = true; guide->use_guide = true; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { guide->angle = 0.0f; guide->type = GP_GUIDE_PARALLEL; } - else if (event->alt) { + else if (event->modifier & KM_ALT) { guide->type = GP_GUIDE_PARALLEL; guide->angle = RNA_float_get(op->ptr, "guide_last_angle"); } @@ -3150,10 +3150,10 @@ static void gpencil_guide_event_handling(bContext *C, add_notifier = true; float angle = guide->angle; float adjust = (float)M_PI / 180.0f; - if (event->alt) { + if (event->modifier & KM_ALT) { adjust *= 45.0f; } - else if (!event->shift) { + else if ((event->modifier & KM_SHIFT) == 0) { adjust *= 15.0f; } angle += (event->type == EVT_JKEY) ? adjust : -adjust; @@ -3633,7 +3633,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) */ } else if (event->type == EVT_ZKEY) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 2d761dd6c91..57a184b0e8d 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1494,7 +1494,7 @@ static void gpencil_primitive_edit_event_handling( float dy = (tgpi->mval[1] - tgpi->mvalo[1]); tgpi->cp1[0] += dx; tgpi->cp1[1] += dy; - if (event->shift) { + if (event->modifier & KM_SHIFT) { copy_v2_v2(tgpi->cp2, tgpi->cp1); } } @@ -1503,7 +1503,7 @@ static void gpencil_primitive_edit_event_handling( float dy = (tgpi->mval[1] - tgpi->mvalo[1]); tgpi->cp2[0] += dx; tgpi->cp2[1] += dy; - if (event->shift) { + if (event->modifier & KM_SHIFT) { copy_v2_v2(tgpi->cp1, tgpi->cp2); } } @@ -1692,7 +1692,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); copy_v2_v2(tgpi->end, tgpi->mval); - if (event->shift) { + if (event->modifier & KM_SHIFT) { gpencil_primitive_constrain(tgpi, true); } @@ -1722,7 +1722,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_FKEY: /* brush thickness/ brush strength */ { if ((event->val == KM_PRESS)) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; } @@ -1900,7 +1900,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_FKEY: /* brush thickness/ brush strength */ { if ((event->val == KM_PRESS)) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; } @@ -1954,12 +1954,12 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e copy_v2_v2(tgpi->origin, tgpi->mval); } /* Keep square if shift key */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { gpencil_primitive_constrain( tgpi, (ELEM(tgpi->type, GP_STROKE_LINE, GP_STROKE_POLYLINE) || tgpi->curve)); } /* Center primitive if alt key */ - if (event->alt && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { + if ((event->modifier & KM_ALT) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]); tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]); } diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index e8f097d0018..67325e8a3d1 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -507,7 +507,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso) /* Convert mouse-movements to movement vector */ RegionView3D *rv3d = gso->region->regiondata; float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, rvec); float mval_f[2]; @@ -525,7 +525,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso) copy_v2_v2(mval_f, r); } - ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac); + ED_view3d_win_to_delta(gso->region, mval_f, zfac, gso->dvec); } /* Apply grab transform to all relevant points of the affected strokes */ @@ -624,17 +624,16 @@ static void gpencil_brush_calc_midpoint(tGP_BrushEditData *gso) */ RegionView3D *rv3d = gso->region->regiondata; const float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, rvec); - float mval_f[2]; - copy_v2_v2(mval_f, gso->mval); float mval_prj[2]; - float dvec[3]; if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, gso->mval); + ED_view3d_win_to_delta(gso->region, xy_delta, zfac, dvec); sub_v3_v3v3(gso->dvec, rvec, dvec); } else { @@ -830,10 +829,10 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso, /* 3D: Project to 3D space */ bool flip; RegionView3D *rv3d = gso->region->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + const float zfac = ED_view3d_calc_zfac_ex(rv3d, &pt->x, &flip); if (flip == false) { float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac); + ED_view3d_win_to_delta(gso->gsc.region, svec, zfac, dvec); add_v3_v3(&pt->x, dvec); /* compute lock axis */ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt); @@ -1883,7 +1882,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", (event->modifier & KM_CTRL) != 0); RNA_boolean_set(&itemptr, "is_start", gso->first); /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ @@ -1895,7 +1894,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const } RNA_float_set(&itemptr, "pressure", pressure); - if (event->shift) { + if (event->modifier & KM_SHIFT) { gso->brush_prev = gso->brush; gso->brush = gpencil_sculpt_get_smooth_brush(gso); diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index da263d44fc6..fca4ff84dc5 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -2659,7 +2659,7 @@ static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *eve RNA_int_set_array(op->ptr, "location", event->mval); if (!RNA_struct_property_is_set(op->ptr, "use_shift_extend")) { - RNA_boolean_set(op->ptr, "use_shift_extend", event->shift); + RNA_boolean_set(op->ptr, "use_shift_extend", event->modifier & KM_SHIFT); } return gpencil_select_exec(C, op); diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index 10be8c3e91e..735759d40ec 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -314,7 +314,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, if (sample > 0.0f) { /* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because * the sample function already call that. */ - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } else { BKE_gpencil_stroke_geometry_update(gpd, gps); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index c0777ac3105..9a658b68f21 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -822,17 +822,16 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc, ED_gpencil_drawing_reference_get(scene, gsc->ob, scene->toolsettings->gpencil_v3d_align, rvec); - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + float zfac = ED_view3d_calc_zfac(rv3d, rvec); - float mval_f[2], mval_prj[2]; - float dvec[3]; - - copy_v2_v2(mval_f, screen_co); + float mval_prj[2]; if (ED_view3d_project_float_global(gsc->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gsc->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, screen_co); + ED_view3d_win_to_delta(gsc->region, xy_delta, zfac, dvec); sub_v3_v3v3(r_out, rvec, dvec); return true; @@ -863,21 +862,21 @@ void gpencil_stroke_convertcoords_tpoint(Scene *scene, */ } else { - float mval_f[2] = {UNPACK2(point2D->m_xy)}; float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. */ ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, rvec); - zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec); if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); sub_v3_v3v3(r_out, rvec, dvec); } else { @@ -2005,19 +2004,19 @@ static void gpencil_stroke_convertcoords(ARegion *region, const float origin[3], float out[3]) { - float mval_f[2] = {UNPACK2(point2D->m_xy)}; float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; copy_v3_v3(rvec, origin); - zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec); if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index 1d1b00e3f08..244942a87ba 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -1241,7 +1241,7 @@ static void gpencil_vertexpaint_brush_apply_event(bContext *C, RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL); RNA_boolean_set(&itemptr, "is_start", gso->first); /* Handle pressure sensitivity (which is supplied by tablets). */ diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 8fe4cba7021..01e73cd2abd 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -698,7 +698,7 @@ static void gpencil_weightpaint_brush_apply_event(bContext *C, RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL); RNA_boolean_set(&itemptr, "is_start", gso->first); /* Handle pressure sensitivity (which is supplied by tablets). */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index e6a68f7fab7..4b6f5e4cac6 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -1090,6 +1090,15 @@ void animviz_calc_motionpaths(struct Depsgraph *depsgraph, bool restore); /** + * Update motion path computation range (in `ob.avs` or `armature.avs`) from user choice in + * `ob.avs.path_range` or `arm.avs.path_range`, depending on active user mode. + * + * \param ob: Object to compute range for (must be provided). + * \param scene: Used when scene range is chosen. + */ +void animviz_motionpath_compute_range(struct Object *ob, struct Scene *scene); + +/** * Get list of motion paths to be baked for the given object. * - assumes the given list is ready to be used. */ diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 7316b045646..9233b65b2ce 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -15,3 +15,14 @@ void ED_operatortypes_curves(void); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +# include "BKE_curves.hh" + +namespace blender::ed::curves { + +bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve); + +} +#endif diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h new file mode 100644 index 00000000000..8aab1533e25 --- /dev/null +++ b/source/blender/editors/include/ED_curves_sculpt.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editors + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void ED_operatortypes_sculpt_curves(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 6a8820d0083..fd3d35e1df7 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -131,7 +131,13 @@ const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist const Range2f frame_range); bool ED_keylist_is_empty(const struct AnimKeylist *keylist); const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist); -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); +bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); +/** + * Return the selected key-frame's range. + * \return False If none are selected and does not affect the frame range. + */ +bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, + Range2f *r_frame_range); const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist); int64_t ED_keylist_array_len(const struct AnimKeylist *keylist); @@ -183,10 +189,10 @@ void mask_to_keylist(struct bDopeSheet *ads, /* ActKeyColumn API ---------------- */ -/* Checks if ActKeyColumn has any block data */ +/** Checks if #ActKeyColumn has any block data. */ bool actkeyblock_is_valid(const ActKeyColumn *ac); -/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ +/** Checks if #ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds"). */ int actkeyblock_get_valid_hold(const ActKeyColumn *ac); #ifdef __cplusplus diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index e1b6a935d6d..ee40cc75e1e 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -24,6 +24,7 @@ struct Scene; struct ScrArea; struct bContext; struct bScreen; +struct PreviewImage; struct wmWindow; struct wmWindowManager; @@ -87,16 +88,13 @@ void ED_preview_shader_job(const struct bContext *C, ePreviewRenderMethod method); void ED_preview_icon_render(const struct bContext *C, struct Scene *scene, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey); + enum eIconSizes icon_size); void ED_preview_icon_job(const struct bContext *C, - void *owner, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey, + enum eIconSizes icon_size, bool delay); void ED_preview_restart_queue_free(void); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 5b439cb0978..c89df9c3789 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -250,6 +250,12 @@ void ED_area_offscreen_free(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); +/** + * Search all screens, even non-active or overlapping (multiple windows), return the most-likely + * area of interest. xy is relative to active window, like all similar functions. + */ +ScrArea *ED_area_find_under_cursor(const struct bContext *C, int spacetype, const int xy[2]); + ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen); ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /** diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index bd3a6bce8e8..f235f696ccc 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -106,7 +106,7 @@ void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value); * \note Shift/Control are not configurable key-bindings. */ void apply_keyb_grid( - int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert); + bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert); /* where else to go ? */ void unpack_menu(struct bContext *C, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 46c9a1973eb..b1435e76eb2 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -507,9 +507,18 @@ float ED_view3d_pixel_size(const struct RegionView3D *rv3d, const float co[3]); float ED_view3d_pixel_size_no_ui_scale(const struct RegionView3D *rv3d, const float co[3]); /** - * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta + * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta. + * + * \param r_flip: Set to `zfac < 0.0` before the value is made signed. + * Since it's important in some cases to know if the value was flipped. + * + * \return The unsigned depth component of `co` multiplied by `rv3d->persmat` matrix, + * with additional sanitation to ensure the result is never negative + * as this isn't useful for tool-code. */ -float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3], bool *r_flip); +float ED_view3d_calc_zfac_ex(const struct RegionView3D *rv3d, const float co[3], bool *r_flip); +/** See #ED_view3d_calc_zfac_ex doc-string. */ +float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3]); /** * Calculate a depth value from `co` (result should only be used for comparison). */ @@ -627,16 +636,24 @@ bool ED_view3d_win_to_3d_on_plane_int(const struct ARegion *region, float r_out[3]); /** * Calculate a 3d difference vector from 2d window offset. - * note that #ED_view3d_calc_zfac() must be called first to determine + * + * \note that #ED_view3d_calc_zfac() must be called first to determine * the depth used to calculate the delta. + * + * When the `zfac` is calculated based on a world-space location directly under the cursor, + * the value of `r_out` can be subtracted from #RegionView3D.ofs to pan the view + * with the contents following the cursor perfectly (without sliding). + * * \param region: The region (used for the window width and height). - * \param mval: The area relative 2d difference (such as `event->mval[0] - other_x`). - * \param out: The resulting world-space delta. + * \param xy_delta: 2D difference (in pixels) such as `event->mval[0] - other_x`. + * \param zfac: The depth result typically calculated by by #ED_view3d_calc_zfac + * (see it's doc-string for details). + * \param r_out: The resulting world-space delta. */ void ED_view3d_win_to_delta(const struct ARegion *region, - const float mval[2], - float out[3], - float zfac); + const float xy_delta[2], + float zfac, + float r_out[3]); /** * Calculate a 3d origin from 2d window coordinates. * \note Orthographic views have a less obvious origin, @@ -645,23 +662,23 @@ void ED_view3d_win_to_delta(const struct ARegion *region, * * \param region: The region (used for the window width and height). * \param mval: The area relative 2d location (such as event->mval converted to floats). - * \param out: The resulting normalized world-space direction vector. + * \param r_out: The resulting normalized world-space direction vector. */ -void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float out[3]); +void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float r_out[3]); /** * Calculate a 3d direction vector from 2d window coordinates. * This direction vector starts and the view in the direction of the 2d window coordinates. * In orthographic view all window coordinates yield the same vector. * - * \note doesn't rely on ED_view3d_calc_zfac + * \note doesn't rely on #ED_view3d_calc_zfac * for perspective view, get the vector direction to * the mouse cursor as a normalized vector. * * \param region: The region (used for the window width and height). * \param mval: The area relative 2d location (such as event->mval converted to floats). - * \param out: The resulting normalized world-space direction vector. + * \param r_out: The resulting normalized world-space direction vector. */ -void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float out[3]); +void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float r_out[3]); /** * Calculate a 3d segment from 2d window coordinates. * This ray_start is located at the viewpoint, ray_end is a far point. @@ -1078,6 +1095,20 @@ bool ED_view3d_persp_ensure(const struct Depsgraph *depsgraph, struct View3D *v3d, struct ARegion *region); +/* Camera view functions. */ + +/** + * Utility to scale zoom level when in camera-view #RegionView3D.camzoom and apply limits. + * \return true a change was made. + */ +bool ED_view3d_camera_view_zoom_scale(struct RegionView3D *rv3d, const float scale); +/** + * Utility to pan when in camera view. + * \param event_ofs: The offset the pan in screen (pixel) coordinates. + * \return true when a change was made. + */ +bool ED_view3d_camera_view_pan(struct ARegion *region, const float event_ofs[2]); + /* Camera lock functions */ /** diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7bbc8249a97..060c9dc33d6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2902,7 +2902,7 @@ uiBut *UI_context_active_but_prop_get(const struct bContext *C, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, int *r_index); -void UI_context_active_but_prop_handle(struct bContext *C); +void UI_context_active_but_prop_handle(struct bContext *C, bool handle_undo); void UI_context_active_but_clear(struct bContext *C, struct wmWindow *win, struct ARegion *region); struct wmOperator *UI_context_active_operator_get(const struct bContext *C); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 4819e8ed82c..3619a7ce317 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1418,7 +1418,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, } /* This version is only for finding hotkeys for properties. - * These are set set via a data-path which is appended to the context, + * These are set via a data-path which is appended to the context, * manipulated using operators (see #ctx_toggle_opnames). */ if (ptr->owner_id) { @@ -3087,11 +3087,11 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) PointerRNA rptr; /* This is kind of hackish, in theory think we could only ever use the second member of - * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when + * this if/else, since #ui_searchbox_apply() is supposed to always set that pointer when * we are storing pointers... But keeping str search first for now, * to try to break as little as possible existing code. All this is band-aids anyway. - * Fact remains, using editstr as main 'reference' over whole search button thingy - * is utterly weak and should be redesigned imho, but that's not a simple task. */ + * Fact remains, using `editstr` as main 'reference' over whole search button thingy + * is utterly weak and should be redesigned IMHO, but that's not a simple task. */ if (search_but && search_but->rnasearchprop && RNA_property_collection_lookup_string( &search_but->rnasearchpoin, search_but->rnasearchprop, str, &rptr)) { @@ -6529,7 +6529,7 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) event.type = EVT_BUT_OPEN; event.val = KM_PRESS; - event.is_repeat = false; + event.flag = 0; event.customdata = but; event.customdata_free = false; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 34a20b91172..07efcdcbe38 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -201,7 +201,7 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *region, void *arg) /* XXX this guess_opname can potentially return a different keymap * than being found on adding later... */ wmKeyMap *km = WM_keymap_guess_opname(C, idname); - wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0); + wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0, KM_ANY); const int kmi_id = kmi->id; /* This takes ownership of prop, or prop can be NULL for reset. */ diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index e72381821e8..eaec1e249b7 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -147,8 +147,7 @@ void datadropper_win_area_find( *r_win = CTX_wm_window(C); *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval); if (*r_area == NULL) { - wmWindowManager *wm = CTX_wm_manager(C); - *r_win = WM_window_find_under_cursor(wm, NULL, *r_win, mval, r_mval); + *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval); if (*r_win) { screen = WM_window_get_active_screen(*r_win); *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval); diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index d6f529f9f94..f3c70e6a96a 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -222,9 +222,9 @@ static void eyedropper_add_palette_color(bContext *C, const float col_conv[4]) static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) { - const bool only_stroke = ((!event->ctrl) && (!event->shift)); - const bool only_fill = ((!event->ctrl) && (event->shift)); - const bool both = ((event->ctrl) && (event->shift)); + const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0; + const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT)); + const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT)); float col_conv[4]; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bbb6bfabdd1..e0b64dcd4d6 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -275,7 +275,7 @@ static void ui_selectcontext_apply(bContext *C, const double value, const double value_orig); -# define IS_ALLSELECT_EVENT(event) ((event)->alt != 0) +# define IS_ALLSELECT_EVENT(event) (((event)->modifier & KM_ALT) != 0) /** just show a tinted color so users know its activated */ # define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE @@ -708,7 +708,8 @@ enum eSnapType { static enum eSnapType ui_event_to_snap(const wmEvent *event) { - return (event->ctrl) ? (event->shift) ? SNAP_ON_SMALL : SNAP_ON : SNAP_OFF; + return (event->modifier & KM_CTRL) ? (event->modifier & KM_SHIFT) ? SNAP_ON_SMALL : SNAP_ON : + SNAP_OFF; } static bool ui_event_is_snap(const wmEvent *event) @@ -1937,7 +1938,7 @@ static void ui_selectcontext_apply(bContext *C, /* could check for 'handle_layer_buttons' */ but->func) { wmWindow *win = CTX_wm_window(C); - if (!win->eventstate->shift) { + if ((win->eventstate->modifier & KM_SHIFT) == 0) { const int len = RNA_property_array_length(&but->rnapoin, prop); bool *tmparray = MEM_callocN(sizeof(bool) * len, __func__); @@ -3747,11 +3748,11 @@ static void ui_do_but_textedit( case EVT_XKEY: case EVT_CKEY: #if defined(__APPLE__) - if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) || - (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) { + if (ELEM(event->modifier, KM_OSKEY, KM_CTRL)) #else - if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) { + if (event->modifier == KM_CTRL) #endif + { if (event->type == EVT_VKEY) { changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE); } @@ -3769,16 +3770,16 @@ static void ui_do_but_textedit( ui_textedit_move(but, data, STRCUR_DIR_NEXT, - event->shift != 0, - event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + event->modifier & KM_SHIFT, + (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case EVT_LEFTARROWKEY: ui_textedit_move(but, data, STRCUR_DIR_PREV, - event->shift != 0, - event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + event->modifier & KM_SHIFT, + (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case WHEELDOWNMOUSE: @@ -3795,7 +3796,7 @@ static void ui_do_but_textedit( } ATTR_FALLTHROUGH; case EVT_ENDKEY: - ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->shift != 0, STRCUR_JUMP_ALL); + ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL); retval = WM_UI_HANDLER_BREAK; break; case WHEELUPMOUSE: @@ -3812,7 +3813,7 @@ static void ui_do_but_textedit( } ATTR_FALLTHROUGH; case EVT_HOMEKEY: - ui_textedit_move(but, data, STRCUR_DIR_PREV, event->shift != 0, STRCUR_JUMP_ALL); + ui_textedit_move(but, data, STRCUR_DIR_PREV, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL); retval = WM_UI_HANDLER_BREAK; break; case EVT_PADENTER: @@ -3822,13 +3823,13 @@ static void ui_do_but_textedit( break; case EVT_DELKEY: changed = ui_textedit_delete( - but, data, 1, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + but, data, 1, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case EVT_BACKSPACEKEY: changed = ui_textedit_delete( - but, data, 0, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + but, data, 0, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; @@ -3837,10 +3838,9 @@ static void ui_do_but_textedit( /* Ctrl-A: Select all. */ #if defined(__APPLE__) /* OSX uses Command-A system-wide, so add it. */ - if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) || - (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) + if (ELEM(event->modifier, KM_OSKEY, KM_CTRL)) #else - if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) + if (event->modifier == KM_CTRL) #endif { ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_ALL); @@ -3859,9 +3859,9 @@ static void ui_do_but_textedit( button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (!IS_EVENT_MOD(event, ctrl, alt, oskey)) { + else if ((event->modifier & (KM_CTRL | KM_ALT | KM_OSKEY)) == 0) { /* Use standard keys for cycling through buttons Tab, Shift-Tab to reverse. */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { ui_textedit_prev_but(block, but, data); } else { @@ -3874,12 +3874,12 @@ static void ui_do_but_textedit( case EVT_ZKEY: { /* Ctrl-Z or Ctrl-Shift-Z: Undo/Redo (allowing for OS-Key on Apple). */ - const bool is_redo = (event->shift != 0); + const bool is_redo = (event->modifier & KM_SHIFT); if ( #if defined(__APPLE__) - (event->oskey && !IS_EVENT_MOD(event, alt, ctrl)) || + ((event->modifier & KM_OSKEY) && ((event->modifier & (KM_ALT | KM_CTRL)) == 0)) || #endif - (event->ctrl && !IS_EVENT_MOD(event, alt, oskey))) { + ((event->modifier & KM_CTRL) && ((event->modifier & (KM_ALT | KM_OSKEY)) == 0))) { int undo_pos; const char *undo_str = ui_textedit_undo( data->undo_stack_text, is_redo ? 1 : -1, &undo_pos); @@ -4542,19 +4542,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, } /* always set */ - but->modifier_key = 0; - if (event->shift) { - but->modifier_key |= KM_SHIFT; - } - if (event->alt) { - but->modifier_key |= KM_ALT; - } - if (event->ctrl) { - but->modifier_key |= KM_CTRL; - } - if (event->oskey) { - but->modifier_key |= KM_OSKEY; - } + but->modifier_key = event->modifier; ui_but_update(but); ED_region_tag_redraw(data->region); @@ -4633,7 +4621,8 @@ static int ui_do_but_TAB( const int rna_type = but->rnaprop ? RNA_property_type(but->rnaprop) : 0; if (is_property && ELEM(rna_type, PROP_POINTER, PROP_STRING) && (but->custom_data != NULL) && - (event->type == LEFTMOUSE) && ((event->val == KM_DBL_CLICK) || event->ctrl)) { + (event->type == LEFTMOUSE) && + ((event->val == KM_DBL_CLICK) || (event->modifier & KM_CTRL))) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); return WM_UI_HANDLER_BREAK; } @@ -4666,7 +4655,8 @@ static int ui_do_but_TEX( if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) { /* pass - allow filesel, enter to execute */ } - else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) && !event->ctrl) { + else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) && + ((event->modifier & KM_CTRL) == 0)) { /* pass */ } else { @@ -4735,7 +4725,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { /* Support Ctrl-Wheel to cycle values on expanded enum rows. */ if (but->type == UI_BTYPE_ROW) { int type = event->type; @@ -5325,24 +5315,24 @@ static int ui_do_but_NUM( } /* XXX hardcoded keymap check.... */ - if (type == MOUSEPAN && event->ctrl) { + if (type == MOUSEPAN && (event->modifier & KM_CTRL)) { /* allow accumulating values, otherwise scrolling gets preference */ retval = WM_UI_HANDLER_BREAK; } - else if (type == WHEELDOWNMOUSE && event->ctrl) { + else if (type == WHEELDOWNMOUSE && (event->modifier & KM_CTRL)) { mx = but->rect.xmin; but->drawflag &= ~UI_BUT_ACTIVE_RIGHT; but->drawflag |= UI_BUT_ACTIVE_LEFT; click = 1; } - else if (type == WHEELUPMOUSE && event->ctrl) { + else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmax; but->drawflag &= ~UI_BUT_ACTIVE_LEFT; but->drawflag |= UI_BUT_ACTIVE_RIGHT; click = 1; } else if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -5402,7 +5392,7 @@ static int ui_do_but_NUM( #endif fac = 1.0f; - if (event->shift) { + if (event->modifier & KM_SHIFT) { fac /= 10.0f; } @@ -5668,27 +5658,27 @@ static int ui_do_but_SLI( } /* XXX hardcoded keymap check.... */ - if (type == MOUSEPAN && event->ctrl) { + if ((type == MOUSEPAN) && (event->modifier & KM_CTRL)) { /* allow accumulating values, otherwise scrolling gets preference */ retval = WM_UI_HANDLER_BREAK; } - else if (type == WHEELDOWNMOUSE && event->ctrl) { + else if ((type == WHEELDOWNMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmin; click = 2; } - else if (type == WHEELUPMOUSE && event->ctrl) { + else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmax; click = 2; } else if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } #ifndef USE_ALLSELECT /* alt-click on sides to get "arrows" like in UI_BTYPE_NUM buttons, * and match wheel usage above */ - else if (event->type == LEFTMOUSE && event->alt) { + else if ((event->type == LEFTMOUSE) && (event->modifier & KM_ALT)) { int halfpos = BLI_rctf_cent_x(&but->rect); click = 2; if (mx < halfpos) { @@ -5754,8 +5744,13 @@ static int ui_do_but_SLI( data->multi_data.drag_dir[0] += abs(data->draglastx - mx); data->multi_data.drag_dir[1] += abs(data->draglasty - my); #endif - if (ui_numedit_but_SLI( - but, data, mx, true, is_motion, event->ctrl != 0, event->shift != 0)) { + if (ui_numedit_but_SLI(but, + data, + mx, + true, + is_motion, + event->modifier & KM_CTRL, + event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -5981,8 +5976,8 @@ static int ui_do_but_LISTROW(bContext *C, /* hack to pass on ctrl+click and double click to overlapping text * editing field for editing list item names */ - if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS && - event->ctrl) || + if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_PRESS) && + (event->modifier & KM_CTRL)) || (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) { uiBut *labelbut = ui_but_list_row_text_activate( C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING); @@ -6023,7 +6018,8 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_BREAK; } if (ui_but_supports_cycling(but)) { - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && + (event->modifier & KM_CTRL)) { int type = event->type; int val = event->val; @@ -6210,7 +6206,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; } - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { ColorPicker *cpicker = but->custom_data; float hsv_static[3] = {0.0f}; float *hsv = cpicker ? cpicker->hsv_perceptual : hsv_static; @@ -6269,7 +6265,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (color_but->is_pallete_color) { - if (!event->ctrl) { + if ((event->modifier & KM_CTRL) == 0) { float color[3]; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); @@ -6639,7 +6635,7 @@ static int ui_do_but_HSVCUBE( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -6650,7 +6646,7 @@ static int ui_do_but_HSVCUBE( const wmNDOFMotionData *ndof = event->customdata; const enum eSnapType snap = ui_event_to_snap(event); - ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->shift != 0); + ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->modifier & KM_SHIFT); button_activate_state(C, but, BUTTON_STATE_EXIT); ui_apply_but(C, but->block, but, data, true); @@ -6702,7 +6698,7 @@ static int ui_do_but_HSVCUBE( if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); - if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -6914,7 +6910,7 @@ static int ui_do_but_HSVCIRCLE( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -6925,7 +6921,7 @@ static int ui_do_but_HSVCIRCLE( const enum eSnapType snap = ui_event_to_snap(event); const wmNDOFMotionData *ndof = event->customdata; - ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->shift != 0); + ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->modifier & KM_SHIFT); button_activate_state(C, but, BUTTON_STATE_EXIT); ui_apply_but(C, but->block, but, data, true); @@ -6987,7 +6983,7 @@ static int ui_do_but_HSVCIRCLE( if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); - if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7037,7 +7033,7 @@ static int ui_do_but_COLORBAND( if (event->type == LEFTMOUSE && event->val == KM_PRESS) { ColorBand *coba = (ColorBand *)but->poin; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* insert new key on mouse location */ const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); BKE_colorband_element_add(coba, pos); @@ -7237,7 +7233,7 @@ static int ui_do_but_CURVE( float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */ int sel = -1; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { float f_xy[2]; BLI_rctf_transform_pt_v(&cumap->curr, &but->rect, f_xy, m_xy); @@ -7301,7 +7297,7 @@ static int ui_do_but_CURVE( if (sel != -1) { /* ok, we move a point */ /* deselect all if this one is deselect. except if we hold shift */ - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } @@ -7336,8 +7332,8 @@ static int ui_do_but_CURVE( data, event->xy[0], event->xy[1], - event->ctrl != 0, - event->shift != 0)) { + event->modifier & KM_CTRL, + event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7350,7 +7346,7 @@ static int ui_do_but_CURVE( if (data->dragchange == false) { /* deselect all, select one */ - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } @@ -7539,7 +7535,7 @@ static int ui_do_but_CURVEPROFILE( if (event->type == LEFTMOUSE && event->val == KM_PRESS) { const float m_xy[2] = {mx, my}; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { float f_xy[2]; BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy); @@ -7616,7 +7612,7 @@ static int ui_do_but_CURVEPROFILE( /* Change the flag for the point(s) if one was selected or added. */ if (i_selected != -1) { /* Deselect all if this one is deselected, except if we hold shift. */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { pts[i_selected].flag ^= selection_type; } else { @@ -7647,7 +7643,7 @@ static int ui_do_but_CURVEPROFILE( if (event->type == MOUSEMOVE) { if (mx != data->draglastx || my != data->draglasty) { if (ui_numedit_but_CURVEPROFILE( - block, but, data, mx, my, event->ctrl != 0, event->shift != 0)) { + block, but, data, mx, my, event->modifier & KM_CTRL, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7871,7 +7867,7 @@ static int ui_do_but_TRACKPREVIEW( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) { + if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -7888,7 +7884,7 @@ static int ui_do_but_TRACKPREVIEW( } else if (event->type == MOUSEMOVE) { if (mx != data->draglastx || my != data->draglasty) { - if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) { + if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7918,8 +7914,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ - bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && - !event->shift; + bool is_press_ctrl_but_no_shift = (event->val == KM_PRESS) && + (event->modifier & (KM_CTRL | KM_OSKEY)) && + (event->modifier & KM_SHIFT) == 0; const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; @@ -7934,12 +7931,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* do copy first, because it is the only allowed operator when disabled */ if (do_copy) { - ui_but_copy(C, but, event->alt); + ui_but_copy(C, but, event->modifier & KM_ALT); return WM_UI_HANDLER_BREAK; } /* handle menu */ - if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && + + if ((event->type == RIGHTMOUSE) && + (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0 && (event->val == KM_PRESS)) { /* For some button types that are typically representing entire sets of data, right-clicking * to spawn the context menu should also activate the item. This makes it clear which item @@ -7960,7 +7959,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } if (do_paste) { - ui_but_paste(C, but, data, event->alt); + ui_but_paste(C, but, data, event->modifier & KM_ALT); return WM_UI_HANDLER_BREAK; } @@ -8793,7 +8792,7 @@ uiBut *UI_context_active_but_prop_get(const bContext *C, return activebut; } -void UI_context_active_but_prop_handle(bContext *C) +void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo) { uiBut *activebut = ui_context_rna_button_active(C); if (activebut) { @@ -8804,6 +8803,11 @@ void UI_context_active_but_prop_handle(bContext *C) if (block->handle_func) { block->handle_func(C, block->handle_func_arg, activebut->retval); } + if (handle_undo) { + /* Update the button so the undo text uses the correct value. */ + ui_but_update(activebut); + ui_apply_but_undo(activebut); + } } } @@ -8942,7 +8946,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); - if (event->alt && but->active) { + if ((event->modifier & KM_ALT) && but->active) { /* Display tool-tips if holding Alt on mouse-over when tool-tips are disabled in the * preferences. */ but->active->tooltip_force = true; @@ -8970,7 +8974,7 @@ void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) wm_event_init_from_window(win, &event); event.type = EVT_BUT_OPEN; event.val = KM_PRESS; - event.is_repeat = false; + event.flag = 0; event.customdata = but; event.customdata_free = false; @@ -9394,7 +9398,7 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9421,7 +9425,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { @@ -9438,7 +9442,7 @@ static int ui_list_handle_click_drag(bContext *C, ARegion *region, const wmEvent *event) { - if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + if (event->type != LEFTMOUSE) { return WM_HANDLER_CONTINUE; } @@ -9448,7 +9452,7 @@ static int ui_list_handle_click_drag(bContext *C, bool activate = false; bool activate_dragging = false; - if (event->type == EVT_TWEAK_L) { + if (event->val == KM_CLICK_DRAG) { if (is_draggable) { activate_dragging = true; activate = true; @@ -9458,7 +9462,7 @@ static int ui_list_handle_click_drag(bContext *C, * regular events (including mouse presses to start dragging) and this part only kicks in if it * hasn't handled the release event. Note that if there's no overlaid button, the row selects * on the press event already via regular #UI_BTYPE_LISTROW handling. */ - else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) { + else if (event->val == KM_CLICK) { activate = true; } @@ -9534,7 +9538,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi ui_pan_to_scroll(event, &type, &val); /* 'ui_pan_to_scroll' gives the absolute direction. */ - if (event->is_direction_inverted) { + if (event->flag & WM_EVENT_SCROLL_INVERT) { scroll_dir = -1; } @@ -9545,14 +9549,14 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } } - if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + if (event->type == LEFTMOUSE) { retval = ui_list_handle_click_drag(C, ui_list, region, event); } else if (val == KM_PRESS) { if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && - !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || - ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && - !IS_EVENT_MOD(event, shift, alt, oskey)))) { + (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0) || + ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_CTRL) && + (event->modifier & (KM_SHIFT | KM_ALT | KM_OSKEY)) == 0))) { const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); int value, min, max; @@ -9609,7 +9613,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } retval = WM_UI_HANDLER_BREAK; } - else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) { + else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_SHIFT)) { /* We now have proper grip, but keep this anyway! */ if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) { ui_list->list_grip = dyn_data->visual_height; @@ -10263,7 +10267,7 @@ static int ui_handle_menu_event(bContext *C, /* Smooth scrolling for popovers. */ case MOUSEPAN: { - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (!ui_block_is_menu(block)) { @@ -10285,7 +10289,7 @@ static int ui_handle_menu_event(bContext *C, } case WHEELUPMOUSE: case WHEELDOWNMOUSE: { - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (!ui_block_is_menu(block)) { @@ -10308,7 +10312,7 @@ static int ui_handle_menu_event(bContext *C, case EVT_HOMEKEY: case EVT_ENDKEY: /* Arrow-keys: only handle for block_loop blocks. */ - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (inside || (block->flag & UI_BLOCK_LOOP)) { @@ -10455,11 +10459,11 @@ static int ui_handle_menu_event(bContext *C, /* Only respond to explicit press to avoid the event that opened the menu * activating an item when the key is held. */ - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (event->alt) { + if (event->modifier & KM_ALT) { act += 10; } @@ -10539,10 +10543,10 @@ static int ui_handle_menu_event(bContext *C, case EVT_YKEY: case EVT_ZKEY: { if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK) && - !IS_EVENT_MOD(event, shift, ctrl, oskey) && + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0) && /* Only respond to explicit press to avoid the event that opened the menu * activating an item when the key is held. */ - !event->is_repeat) { + (event->flag & WM_EVENT_IS_REPEAT) == 0) { if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) { break; } @@ -11066,7 +11070,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case EVT_YKEY: case EVT_ZKEY: { if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) && - !IS_EVENT_MOD(event, shift, ctrl, oskey)) { + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0)) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->menu_key == event->type) { ui_but_pie_button_activate(C, but, menu); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index e277aa2e629..9dfc9be2a30 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -573,18 +573,6 @@ int UI_icon_from_event_type(short event_type, short event_value) else if (event_type == EVT_RIGHTALTKEY) { event_type = EVT_LEFTALTKEY; } - else if (event_type == EVT_TWEAK_L) { - event_type = LEFTMOUSE; - event_value = KM_CLICK_DRAG; - } - else if (event_type == EVT_TWEAK_M) { - event_type = MIDDLEMOUSE; - event_value = KM_CLICK_DRAG; - } - else if (event_type == EVT_TWEAK_R) { - event_type = RIGHTMOUSE; - event_value = KM_CLICK_DRAG; - } DrawInfo *di = g_di_event_list; do { @@ -1399,19 +1387,17 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - prv_img->flag[size] |= PRV_RENDERING; if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) { /* Job (background) version */ - ED_preview_icon_job( - C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); + ED_preview_icon_job(C, prv_img, id, size, delay); } else { if (!scene) { scene = CTX_data_scene(C); } /* Immediate version */ - ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]); + ED_preview_icon_render(C, scene, prv_img, id, size); } } @@ -2271,7 +2257,7 @@ int UI_icon_from_idcode(const int idcode) return ICON_CAMERA_DATA; case ID_CF: return ICON_FILE; - case ID_CU: + case ID_CU_LEGACY: return ICON_CURVE_DATA; case ID_GD: return ICON_OUTLINER_DATA_GREASEPENCIL; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 54a5b496048..bd55d2d9d81 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -492,7 +492,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; const int index = POINTER_AS_INT(arg_index); - const int shift = win->eventstate->shift; + const bool shift = win->eventstate->modifier & KM_SHIFT; const int len = RNA_property_array_length(ptr, prop); if (!shift) { @@ -752,7 +752,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2) { wmWindow *win = CTX_wm_window(C); - if (!win->eventstate->shift) { + if ((win->eventstate->modifier & KM_SHIFT) == 0) { uiBut *but = (uiBut *)arg1; const int enum_value = POINTER_AS_INT(arg2); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index a96f14d7435..498c22748ce 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -314,7 +314,7 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert RNA_property_update(C, ptr, prop); /* as if we pressed the button */ - UI_context_active_but_prop_handle(C); + UI_context_active_but_prop_handle(C, false); /* Since we don't want to undo _all_ edits to settings, eg window * edits on the screen or on operator settings. @@ -326,6 +326,19 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert return OPERATOR_CANCELLED; } +static int operator_button_property_finish_with_undo(bContext *C, + PointerRNA *ptr, + PropertyRNA *prop) +{ + /* Perform updates required for this property. */ + RNA_property_update(C, ptr, prop); + + /* As if we pressed the button. */ + UI_context_active_but_prop_handle(C, true); + + return OPERATOR_FINISHED; +} + static bool reset_default_button_poll(bContext *C) { PointerRNA ptr; @@ -350,7 +363,7 @@ static int reset_default_button_exec(bContext *C, wmOperator *op) /* if there is a valid property that is editable... */ if (ptr.data && prop && RNA_property_editable(&ptr, prop)) { if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) { - return operator_button_property_finish(C, &ptr, prop); + return operator_button_property_finish_with_undo(C, &ptr, prop); } } @@ -369,7 +382,9 @@ static void UI_OT_reset_default_button(wmOperatorType *ot) ot->exec = reset_default_button_exec; /* flags */ - ot->flag = OPTYPE_UNDO; + /* Don't set #OPTYPE_UNDO because #operator_button_property_finish_with_undo + * is responsible for the undo push. */ + ot->flag = 0; /* properties */ RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 2cb0f256b71..c7f2eb230cb 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2061,8 +2061,8 @@ static void ui_handle_panel_header(const bContext *C, const uiBlock *block, const int mx, const int event_type, - const short ctrl, - const short shift) + const bool ctrl, + const bool shift) { Panel *panel = block->panel; ARegion *region = CTX_wm_region(C); @@ -2274,7 +2274,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, (event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin)); /* If mouse is inside non-tab region, ctrl key is required. */ - if (is_mousewheel && !event->ctrl && !inside_tabregion) { + if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) { return WM_UI_HANDLER_CONTINUE; } @@ -2291,7 +2291,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev; } else { - const bool backwards = event->shift; + const bool backwards = event->modifier & KM_SHIFT; pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next; if (!pc_dyn) { /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */ @@ -2349,7 +2349,7 @@ int ui_handler_panel_region(bContext *C, retval = WM_UI_HANDLER_BREAK; } } - else if ((event->type == EVT_TABKEY && event->ctrl) || + else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) || ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { /* Cycle tabs. */ retval = ui_handle_panel_category_cycling(event, region, active_but); @@ -2386,9 +2386,11 @@ int ui_handler_panel_region(bContext *C, /* The panel collapse / expand key "A" is special as it takes priority over * active button handling. */ - if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->type == EVT_AKEY && + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0)) { retval = WM_UI_HANDLER_BREAK; - ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + ui_handle_panel_header( + C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT); break; } } @@ -2402,7 +2404,8 @@ int ui_handler_panel_region(bContext *C, /* All mouse clicks inside panel headers should return in break. */ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) { retval = WM_UI_HANDLER_BREAK; - ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + ui_handle_panel_header( + C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT); } else if (event->type == RIGHTMOUSE) { retval = WM_UI_HANDLER_BREAK; diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8a945c8c913..4703367671d 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -310,7 +310,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->xy, event->ctrl != 0, NULL, NULL); + return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 09faf493ce7..29553ff65d1 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -701,7 +701,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* Keymap */ /* This is too handy not to expose somehow, let's be sneaky for now. */ - if ((is_label == false) && CTX_wm_window(C)->eventstate->shift) { + if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { const char *expr_imports[] = {"bpy", "bl_ui", NULL}; char expr[256]; SNPRINTF(expr, diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 40675da71a9..6139ac8e702 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -598,7 +598,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha } /** - * \note Note that \a layout_type may be null. + * \note that \a layout_type may be null. */ static uiList *ui_list_ensure(bContext *C, uiListType *ui_list_type, diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 2b7ca1f8b71..32b3bb5e926 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -609,7 +609,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_update(C, &template_ui->ptr, template_ui->prop); - if (id && CTX_wm_window(C)->eventstate->shift) { + if (id && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { /* only way to force-remove data (on save) */ id_us_clear_real(id); id_fake_user_clear(id); @@ -635,7 +635,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { Main *bmain = CTX_data_main(C); - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { if (ID_IS_OVERRIDABLE_LIBRARY(id)) { /* Only remap that specific ID usage to overriding local data-block. */ ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); @@ -731,7 +731,7 @@ static const char *template_id_browse_tip(const StructRNA *type) return N_("Browse Object to be linked"); case ID_ME: return N_("Browse Mesh Data to be linked"); - case ID_CU: + case ID_CU_LEGACY: return N_("Browse Curve Data to be linked"); case ID_MB: return N_("Browse Metaball Data to be linked"); @@ -844,7 +844,7 @@ static uiBut *template_id_def_new_but(uiBlock *block, BLT_I18NCONTEXT_ID_SCENE, BLT_I18NCONTEXT_ID_OBJECT, BLT_I18NCONTEXT_ID_MESH, - BLT_I18NCONTEXT_ID_CURVE, + BLT_I18NCONTEXT_ID_CURVE_LEGACY, BLT_I18NCONTEXT_ID_METABALL, BLT_I18NCONTEXT_ID_MATERIAL, BLT_I18NCONTEXT_ID_TEXTURE, @@ -5539,7 +5539,7 @@ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) uiBut *but = arg1; const int cur = POINTER_AS_INT(arg2); wmWindow *win = CTX_wm_window(C); - const int shift = win->eventstate->shift; + const bool shift = win->eventstate->modifier & KM_SHIFT; if (!shift) { const int tot = RNA_property_array_length(&but->rnapoin, but->rnaprop); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index fbbf3c6fdf1..d1f3843c643 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2579,7 +2579,7 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp * * A lot of places of the UI like the Node Editor or panels are zoomable. In most cases we can * get the zoom factor from the aspect, but in some cases like popups we need to fall back to - * using the the size of the element. The latter method relies on the element always being the same + * using the size of the element. The latter method relies on the element always being the same * size. * \{ */ @@ -4293,7 +4293,7 @@ static void widget_tab( const bool is_active = (state & UI_SELECT); /* Draw shaded outline - Disabled for now, - * seems incorrect and also looks nicer without it imho ;) */ + * seems incorrect and also looks nicer without it IMHO ;). */ // #define USE_TAB_SHADED_HIGHLIGHT uchar theme_col_tab_highlight[3]; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 09d57d3ea99..28a025ee581 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1449,7 +1449,7 @@ static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* tune these until it feels right */ const float zoom_sensitivity = 0.5f; const float speed = 10.0f; /* match view3d ortho */ - const bool has_translate = (ndof->tvec[0] && ndof->tvec[1]) && view_pan_poll(C); + const bool has_translate = !is_zero_v2(ndof->tvec) && view_pan_poll(C); const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C); if (has_translate) { diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 28e14a14f5f..1c821eebdee 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -38,12 +38,12 @@ static const EnumPropertyItem io_obj_transform_axis_forward[] = { {OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"}, {OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"}, {OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"}, - {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z (Default)", "Negative Z axis"}, + {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"}, {0, NULL, 0, NULL, NULL}}; static const EnumPropertyItem io_obj_transform_axis_up[] = { {OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"}, - {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y (Default)", "Positive Y axis"}, + {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"}, {OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"}, {OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"}, {OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"}, @@ -55,7 +55,7 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { {DAG_EVAL_VIEWPORT, "DAG_EVAL_VIEWPORT", 0, - "Viewport (Default)", + "Viewport", "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 429db50f321..e53dda1760e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -546,7 +546,7 @@ static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event) value = value_start[vmode] + value * opdata->scale[vmode]; /* Fake shift-transform... */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { if (opdata->shift_value[vmode] < 0.0f) { opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ? opdata->segments : diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 58bd906101c..7b251b77750 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -77,14 +77,14 @@ static void mesh_bisect_interactive_calc(bContext *C, const float *co_ref = rv3d->ofs; float co_a_ss[2] = {x_start, y_start}, co_b_ss[2] = {x_end, y_end}, co_delta_ss[2]; float co_a[3], co_b[3]; - const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, co_ref); /* view vector */ ED_view3d_win_to_vector(region, co_a_ss, co_a); /* view delta */ sub_v2_v2v2(co_delta_ss, co_a_ss, co_b_ss); - ED_view3d_win_to_delta(region, co_delta_ss, co_b, zfac); + ED_view3d_win_to_delta(region, co_delta_ss, zfac, co_b); /* cross both to get a normal */ cross_v3_v3v3(plane_no, co_a, co_b); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 84bda411d4a..bce46dd7cf7 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -58,7 +58,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, } me_eval_needs_free = false; } - else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); me_eval = BKE_mesh_new_nomain_from_curve(ob_eval); me_eval_needs_free = true; diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 72844908685..c9fc48c3568 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -581,7 +581,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) handled = true; break; case MOUSEPAN: - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts += 0.02f * (event->xy[1] - event->prev_xy[1]); if (cuts < 1 && lcd->cuts >= 1) { cuts = 1; @@ -598,7 +598,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) { break; } - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts += 1; } else { @@ -612,7 +612,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) { break; } - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts = max_ff(cuts - 1, 1); } else { @@ -755,7 +755,8 @@ void MESH_OT_loopcut(wmOperatorType *ot) RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_enum_default(prop, PROP_INVSQUARE); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ /* For redo only. */ prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", 0, INT_MAX); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index fc1d60fc768..d8fc7a4f9d4 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1367,10 +1367,10 @@ static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *e /* detecting these options based on shift/ctrl here is weak, but it's done * to make this work when clicking buttons or menus */ if (!RNA_struct_property_is_set(op->ptr, "use_extend")) { - RNA_boolean_set(op->ptr, "use_extend", event->shift); + RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT); } if (!RNA_struct_property_is_set(op->ptr, "use_expand")) { - RNA_boolean_set(op->ptr, "use_expand", event->ctrl); + RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL); } return edbm_select_mode_exec(C, op); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index a1e661cf2ac..2577218d6a9 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -269,7 +269,8 @@ static void mesh_operator_edgering_props(wmOperatorType *ot, RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_enum_default(prop, PROP_SMOOTH); RNA_def_property_ui_text(prop, "Profile Shape", "Shape of the profile"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSubdProps *op_props) @@ -9640,13 +9641,13 @@ static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__); - /* This is weird choice of operation, taking all loops of faces of current vertex. - * Could lead to some rather far away loops weighting as much as very close ones + /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current + * vertex. Could lead to some rather far away loops weighting as much as very close ones * (topologically speaking), with complex polygons. * Using topological distance here (rather than geometrical one) - * makes sense imho, but would rather go with a more consistent and flexible code, - * we could even add max topological distance to take into account, * and a weighting curve. - * Would do that later though, think for now we can live with that choice. --mont29. */ + * makes sense IMHO, but would rather go with a more consistent and flexible code, + * we could even add max topological distance to take into account, and a weighting curve. + * Would do that later though, think for now we can live with that choice. */ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { l = lnor_ed->loop; diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 92f2f859965..417fdca4988 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -632,7 +632,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo return um; } -static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *key) +static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em) { BMEditMesh *em_tmp; BMesh *bm; @@ -688,29 +688,6 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; - /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens - * if the active is a basis for any other. */ - if (key && (key->type == KEY_RELATIVE)) { - /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume - * shapenr from restored bmesh and keyblock indices are in sync. */ - const int kb_act_idx = ob->shapenr - 1; - - /* If it is, let's patch the current mesh key block to its restored value. - * Else, the offsets won't be computed and it won't matter. */ - if (BKE_keyblock_is_basis(key, kb_act_idx)) { - KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); - - if (kb_act->totelem != um->me.totvert) { - /* The current mesh has some extra/missing verts compared to the undo, adjust. */ - MEM_SAFE_FREE(kb_act->data); - kb_act->data = MEM_mallocN((size_t)(key->elemsize) * bm->totvert, __func__); - kb_act->totelem = um->me.totvert; - } - - BKE_keyblock_update_from_mesh(&um->me, kb_act); - } - } - ob->shapenr = um->shapenr; MEM_freeN(em_tmp); @@ -858,7 +835,7 @@ static void mesh_undosys_step_decode(struct bContext *C, continue; } BMEditMesh *em = me->edit_mesh; - undomesh_to_editmesh(&elem->data, obedit, em, me->key); + undomesh_to_editmesh(&elem->data, obedit, em); em->needs_flush_to_id = 1; DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 49a5345d048..b3f90880388 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -21,6 +21,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_report.h" #include "DEG_depsgraph.h" @@ -1110,6 +1111,8 @@ static void mesh_add_verts(Mesh *mesh, int len) mesh->vdata = vdata; BKE_mesh_update_customdata_pointers(mesh, false); + BKE_mesh_runtime_clear_cache(mesh); + /* scan the input list and insert the new vertices */ /* set default flags */ @@ -1146,6 +1149,8 @@ static void mesh_add_edges(Mesh *mesh, int len) mesh->edata = edata; BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */ + BKE_mesh_runtime_clear_cache(mesh); + /* set default flags */ medge = &mesh->medge[mesh->totedge]; for (i = 0; i < len; i++, medge++) { @@ -1174,6 +1179,8 @@ static void mesh_add_loops(Mesh *mesh, int len) CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); } + BKE_mesh_runtime_clear_cache(mesh); + CustomData_free(&mesh->ldata, mesh->totloop); mesh->ldata = ldata; BKE_mesh_update_customdata_pointers(mesh, true); @@ -1205,6 +1212,8 @@ static void mesh_add_polys(Mesh *mesh, int len) mesh->pdata = pdata; BKE_mesh_update_customdata_pointers(mesh, true); + BKE_mesh_runtime_clear_cache(mesh); + /* set default flags */ mpoly = &mesh->mpoly[mesh->totpoly]; for (i = 0; i < len; i++, mpoly++) { diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 5a8708c84b6..f3782c17845 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -412,6 +412,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * Even though this mesh wont typically have run-time data, the Python API can for e.g. * create loop-triangle cache here, which is confusing when left in the mesh, see: T90798. */ BKE_mesh_runtime_clear_geometry(me); + BKE_mesh_clear_derived_normals(me); /* new material indices and material array */ if (totmat) { diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 54db59dc2fa..39ccadd1445 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -8,6 +8,7 @@ set(INC ../../blentranslation ../../bmesh ../../depsgraph + ../../functions ../../gpencil_modifiers ../../gpu ../../ikplugin @@ -28,7 +29,7 @@ set(INC ) set(SRC - object_add.c + object_add.cc object_bake.c object_bake_api.c object_collection.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.cc index c2d811f56dc..7befad3b8d7 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.cc @@ -5,9 +5,9 @@ * \ingroup edobj */ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> +#include <cctype> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -93,6 +93,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_curves.h" #include "ED_gpencil.h" #include "ED_mball.h" #include "ED_mesh.h" @@ -122,7 +123,7 @@ const EnumPropertyItem rna_enum_light_type_items[] = { {LA_SUN, "SUN", ICON_LIGHT_SUN, "Sun", "Constant direction parallel ray light source"}, {LA_SPOT, "SPOT", ICON_LIGHT_SPOT, "Spot", "Directional cone light source"}, {LA_AREA, "AREA", ICON_LIGHT_AREA, "Area", "Directional area light source"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /* copy from rna_object_force.c */ @@ -140,7 +141,7 @@ static const EnumPropertyItem field_type_items[] = { {PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", ""}, {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", ""}, {PFIELD_FLUIDFLOW, "FLUID", ICON_FORCE_FLUIDFLOW, "Fluid Flow", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static EnumPropertyItem lightprobe_type_items[] = { @@ -159,7 +160,7 @@ static EnumPropertyItem lightprobe_type_items[] = { ICON_LIGHTPROBE_GRID, "Irradiance Volume", "Irradiance probe to capture diffuse indirect lighting"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; enum { @@ -172,7 +173,7 @@ static const EnumPropertyItem align_options[] = { {ALIGN_WORLD, "WORLD", 0, "World", "Align the new object to the world"}, {ALIGN_VIEW, "VIEW", 0, "View", "Align the new object to the view"}, {ALIGN_CURSOR, "CURSOR", 0, "3D Cursor", "Use the 3D cursor orientation for the new object"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /** \} */ @@ -198,7 +199,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot) "X-coordinate (screen space) to place the new object under", INT_MIN, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); prop = RNA_def_int(ot->srna, "drop_y", 0, @@ -208,7 +209,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot) "Y-coordinate (screen space) to place the new object under", INT_MIN, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } static bool object_add_drop_xy_is_set(const wmOperator *op) @@ -343,13 +344,13 @@ float ED_object_new_primitive_matrix(bContext *C, invert_m3_m3(imat, mat); mul_m3_v3(imat, r_primmat[3]); - if (scale != NULL) { + if (scale != nullptr) { rescale_m4(r_primmat, scale); } { - const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : - ED_scene_grid_scale(scene, NULL); + const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) : + ED_scene_grid_scale(scene, nullptr); return dia; } @@ -393,20 +394,20 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) if (do_editmode) { prop = RNA_def_boolean(ot->srna, "enter_editmode", - 0, + false, "Enter Edit Mode", "Enter edit mode when adding this object"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } /* NOTE: this property gets hidden for add-camera operator. */ prop = RNA_def_enum( ot->srna, "align", align_options, ALIGN_WORLD, "Align", "The alignment of the new object"); - RNA_def_property_update_runtime(prop, view_align_update); + RNA_def_property_update_runtime(prop, (void *)view_align_update); prop = RNA_def_float_vector_xyz(ot->srna, "location", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Location", @@ -417,7 +418,7 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) prop = RNA_def_float_rotation(ot->srna, "rotation", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Rotation", @@ -429,14 +430,14 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) prop = RNA_def_float_vector_xyz(ot->srna, "scale", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Scale", "Scale for the newly added object", -1000.0f, 1000.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } void ED_object_add_mesh_props(wmOperatorType *ot) @@ -461,11 +462,11 @@ bool ED_object_add_generic_get_opts(bContext *C, r_enter_editmode = &_enter_editmode; } /* Only to ensure the value is _always_ set. - * Typically the property will exist when the argument is non-NULL. */ + * Typically the property will exist when the argument is non-nullptr. */ *r_enter_editmode = false; PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode"); - if (prop != NULL) { + if (prop != nullptr) { if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) { *r_enter_editmode = RNA_property_boolean_get(op->ptr, prop); } @@ -571,7 +572,7 @@ bool ED_object_add_generic_get_opts(bContext *C, copy_v3_fl(r_scale, 1.0f); PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale"); - if (prop != NULL) { + if (prop != nullptr) { if (RNA_property_is_set(op->ptr, prop)) { RNA_property_float_get_array(op->ptr, prop, r_scale); } @@ -600,19 +601,19 @@ Object *ED_object_add_type_with_obdata(bContext *C, { Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit != NULL) { + if (obedit != nullptr) { ED_object_editmode_exit_ex(bmain, scene, obedit, EM_FREEDATA); } } /* deselects all, sets active object */ Object *ob; - if (obdata != NULL) { + if (obdata != nullptr) { BLI_assert(type == BKE_object_obdata_to_type(obdata)); ob = BKE_object_add_for_data(bmain, view_layer, type, name, obdata, true); const short *materials_len_p = BKE_id_material_len_p(obdata); if (materials_len_p && *materials_len_p > 0) { - BKE_object_materials_test(bmain, ob, ob->data); + BKE_object_materials_test(bmain, ob, static_cast<ID *>(ob->data)); } } else { @@ -630,7 +631,7 @@ Object *ED_object_add_type_with_obdata(bContext *C, */ DEG_id_type_tag(bmain, ID_OB); DEG_relations_tag_update(bmain); - if (ob->data != NULL) { + if (ob->data != nullptr) { DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } @@ -657,7 +658,7 @@ Object *ED_object_add_type(bContext *C, const ushort local_view_bits) { return ED_object_add_type_with_obdata( - C, type, name, loc, rot, enter_editmode, local_view_bits, NULL); + C, type, name, loc, rot, enter_editmode, local_view_bits, nullptr); } /* for object add operator */ @@ -668,12 +669,12 @@ static int object_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3], radius; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } radius = RNA_float_get(op->ptr, "radius"); Object *ob = ED_object_add_type( - C, RNA_enum_get(op->ptr, "type"), NULL, loc, rot, enter_editmode, local_view_bits); + C, RNA_enum_get(op->ptr, "type"), nullptr, loc, rot, enter_editmode, local_view_bits); if (ob->type == OB_LATTICE) { /* lattice is a special case! @@ -737,7 +738,7 @@ static int lightprobe_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } int type = RNA_enum_get(op->ptr, "type"); @@ -829,26 +830,25 @@ static int effector_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - int type = RNA_enum_get(op->ptr, "type"); + const ePFieldType type = static_cast<ePFieldType>(RNA_enum_get(op->ptr, "type")); float dia = RNA_float_get(op->ptr, "radius"); Object *ob; if (type == PFIELD_GUIDE) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Curve *cu; ob = ED_object_add_type( - C, OB_CURVE, get_effector_defname(type), loc, rot, false, local_view_bits); + C, OB_CURVES_LEGACY, get_effector_defname(type), loc, rot, false, local_view_bits); - cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); cu->flag |= CU_PATH | CU_3D; ED_object_editmode_enter_ex(bmain, scene, ob, 0); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat); mul_mat3_m4_fl(mat, dia); BLI_addtail(&cu->editnurb->nurbs, ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); @@ -909,22 +909,23 @@ static int object_camera_add_exec(bContext *C, wmOperator *op) bool enter_editmode; float loc[3], rot[3]; if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *ob = ED_object_add_type(C, OB_CAMERA, NULL, loc, rot, false, local_view_bits); + Object *ob = ED_object_add_type(C, OB_CAMERA, nullptr, loc, rot, false, local_view_bits); if (v3d) { - if (v3d->camera == NULL) { + if (v3d->camera == nullptr) { v3d->camera = ob; } - if (v3d->scenelock && scene->camera == NULL) { + if (v3d->scenelock && scene->camera == nullptr) { scene->camera = ob; } } - Camera *cam = ob->data; - cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); + Camera *cam = static_cast<Camera *>(ob->data); + cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) : + ED_scene_grid_scale(scene, nullptr); return OPERATOR_FINISHED; } @@ -969,14 +970,14 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } bool newob = false; Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit == NULL || obedit->type != OB_MBALL) { - obedit = ED_object_add_type(C, OB_MBALL, NULL, loc, rot, true, local_view_bits); + if (obedit == nullptr || obedit->type != OB_MBALL) { + obedit = ED_object_add_type(C, OB_MBALL, nullptr, loc, rot, true, local_view_bits); newob = true; } else { @@ -984,7 +985,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) } float mat[4][4]; - ED_object_new_primitive_matrix(C, obedit, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, obedit, loc, rot, nullptr, mat); /* Halving here is done to account for constant values from #BKE_mball_element_add. * While the default radius of the resulting meta element is 2, * we want to pass in 1 so other values such as resolution are scaled by 1.0. */ @@ -1040,14 +1041,14 @@ static int object_add_text_exec(bContext *C, wmOperator *op) WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } if (obedit && obedit->type == OB_FONT) { return OPERATOR_CANCELLED; } - obedit = ED_object_add_type(C, OB_FONT, NULL, loc, rot, enter_editmode, local_view_bits); + obedit = ED_object_add_type(C, OB_FONT, nullptr, loc, rot, enter_editmode, local_view_bits); BKE_object_obdata_size_init(obedit, RNA_float_get(op->ptr, "radius")); return OPERATOR_FINISHED; @@ -1094,11 +1095,11 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - if ((obedit == NULL) || (obedit->type != OB_ARMATURE)) { - obedit = ED_object_add_type(C, OB_ARMATURE, NULL, loc, rot, true, local_view_bits); + if ((obedit == nullptr) || (obedit->type != OB_ARMATURE)) { + obedit = ED_object_add_type(C, OB_ARMATURE, nullptr, loc, rot, true, local_view_bits); ED_object_editmode_enter_ex(bmain, scene, obedit, 0); newob = true; } @@ -1106,7 +1107,7 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } - if (obedit == NULL) { + if (obedit == nullptr) { BKE_report(op->reports, RPT_ERROR, "Cannot create editmode armature"); return OPERATOR_CANCELLED; } @@ -1155,10 +1156,11 @@ static int object_empty_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - ob = ED_object_add_type(C, OB_EMPTY, NULL, loc, rot, false, local_view_bits); + ob = ED_object_add_type(C, OB_EMPTY, nullptr, loc, rot, false, local_view_bits); BKE_object_empty_draw_type_set(ob, type); BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); @@ -1192,7 +1194,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv { Scene *scene = CTX_data_scene(C); - Image *ima = NULL; + Image *ima = nullptr; ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); if (!ima) { @@ -1201,7 +1203,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv /* handled below */ id_us_min(&ima->id); - Object *ob = NULL; + Object *ob = nullptr; Object *ob_cursor = ED_view3d_give_object_under_cursor(C, event->mval); /* either change empty under cursor or create a new empty */ @@ -1216,10 +1218,10 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv float rot[3]; if (!ED_object_add_generic_get_opts( - C, op, 'Z', NULL, rot, NULL, NULL, &local_view_bits, NULL)) { + C, op, 'Z', nullptr, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, rot, false, local_view_bits); + ob = ED_object_add_type(C, OB_EMPTY, nullptr, nullptr, rot, false, local_view_bits); ED_object_location_from_view(C, ob->loc); ED_view3d_cursor3d_position(C, event->mval, false, ob->loc); @@ -1229,9 +1231,9 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv BKE_object_empty_draw_type_set(ob, OB_EMPTY_IMAGE); - id_us_min(ob->data); + id_us_min(static_cast<ID *>(ob->data)); ob->data = ima; - id_us_plus(ob->data); + id_us_plus(static_cast<ID *>(ob->data)); return OPERATOR_FINISHED; } @@ -1253,16 +1255,17 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "Filepath", "Path to image file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string(ot->srna, "filepath", nullptr, FILE_MAX, "Filepath", "Path to image file"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Image name to assign"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); ED_object_add_generic_props(ot, false); } @@ -1277,7 +1280,7 @@ static bool object_gpencil_add_poll(bContext *C) Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); - if ((scene == NULL) || (ID_IS_LINKED(scene))) { + if ((scene == nullptr) || (ID_IS_LINKED(scene))) { return false; } @@ -1293,7 +1296,7 @@ static bool object_gpencil_add_poll(bContext *C) static int object_gpencil_add_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C), *ob_orig = ob; - bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL; + bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? static_cast<bGPdata *>(ob->data) : nullptr; const int type = RNA_enum_get(op->ptr, "type"); const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); @@ -1307,12 +1310,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* NOTE: We use 'Y' here (not 'Z'), as. */ WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Y', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } /* Add new object if not currently editing a GP object. */ - if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) { - const char *ob_name = NULL; + if ((gpd == nullptr) || (GPENCIL_ANY_MODE(gpd) == false)) { + const char *ob_name = nullptr; switch (type) { case GP_EMPTY: { ob_name = "GPencil"; @@ -1338,12 +1342,12 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) } ob = ED_object_add_type(C, OB_GPENCIL, ob_name, loc, rot, true, local_view_bits); - gpd = ob->data; + gpd = static_cast<bGPdata *>(ob->data); newob = true; } else { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, nullptr); } /* create relevant geometry */ @@ -1351,7 +1355,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_EMPTY: { float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat); ED_gpencil_create_blank(C, ob, mat); break; } @@ -1389,7 +1393,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) ED_gpencil_create_lineart(C, ob); - gpd = ob->data; + gpd = static_cast<bGPdata *>(ob->data); /* Add Line Art modifier */ LineartGpencilModifierData *md = (LineartGpencilModifierData *)BKE_gpencil_modifier_new( @@ -1458,21 +1462,21 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiLayoutSetPropSep(layout, true); - uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "radius", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "align", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "location", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "rotation", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "type", 0, nullptr, ICON_NONE); int type = RNA_enum_get(op->ptr, "type"); if (ELEM(type, GP_LRT_COLLECTION, GP_LRT_OBJECT, GP_LRT_SCENE)) { - uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_lights", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "use_in_front", 0, nullptr, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, !in_front); - uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE); - uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_offset", 0, nullptr, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_order", 0, nullptr, ICON_NONE); } } @@ -1483,7 +1487,7 @@ static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = { "2D Layers", "Display strokes using grease pencil layers to define order"}, {GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; void OBJECT_OT_gpencil_add(wmOperatorType *ot) @@ -1565,7 +1569,8 @@ static int object_light_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } ob = ED_object_add_type(C, OB_LAMP, get_light_defname(type), loc, rot, false, local_view_bits); @@ -1648,7 +1653,8 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) update_location_if_necessary = true; } else { - collection = BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")); + collection = static_cast<Collection *>( + BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection"))); } if (update_location_if_necessary) { @@ -1660,11 +1666,12 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) } } - if (collection == NULL) { + if (collection == nullptr) { return OPERATOR_CANCELLED; } - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } @@ -1735,7 +1742,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) "Session UUID of the collection to add", INT32_MIN, INT32_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); object_add_drop_xy_props(ot); } @@ -1751,7 +1758,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) static int object_data_instance_add_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - ID *id = NULL; + ID *id = nullptr; ushort local_view_bits; float loc[3], rot[3]; @@ -1767,7 +1774,7 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) char name[MAX_ID_NAME - 2]; RNA_property_string_get(op->ptr, prop_name, name); id = BKE_libblock_find_name(bmain, id_type, name); - if (id == NULL) { + if (id == nullptr) { return OPERATOR_CANCELLED; } const int object_type = BKE_object_obdata_to_type(id); @@ -1782,7 +1789,8 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) RNA_property_float_set_array(op->ptr, prop_location, loc); } - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } @@ -1829,10 +1837,11 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *ob = ED_object_add_type(C, OB_SPEAKER, NULL, loc, rot, false, local_view_bits); + Object *ob = ED_object_add_type(C, OB_SPEAKER, nullptr, loc, rot, false, local_view_bits); const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ob); /* To make it easier to start using this immediately in NLA, a default sound clip is created @@ -1840,8 +1849,8 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) { /* create new data for NLA hierarchy */ AnimData *adt = BKE_animdata_ensure_id(&ob->id); - NlaTrack *nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); - NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, ob->data); + NlaTrack *nlt = BKE_nlatrack_add(adt, nullptr, is_liboverride); + NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, static_cast<Speaker *>(ob->data)); strip->start = CFRA; strip->end += strip->start; @@ -1852,7 +1861,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) BLI_strncpy(nlt->name, DATA_("SoundTrack"), sizeof(nlt->name)); BKE_nlastrip_validate_name(adt, strip); - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, nullptr); } return OPERATOR_FINISHED; @@ -1891,15 +1900,21 @@ static bool object_hair_curves_add_poll(bContext *C) static int object_hair_curves_add_exec(bContext *C, wmOperator *op) { + using namespace blender; + ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *object = ED_object_add_type(C, OB_CURVES, NULL, loc, rot, false, local_view_bits); + Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ + Curves *curves_id = static_cast<Curves *>(object->data); + bke::CurvesGeometry::wrap(curves_id->geometry) = ed::curves::primitive_random_sphere(500, 8); + return OPERATOR_FINISHED; } @@ -1938,11 +1953,12 @@ static int object_pointcloud_add_exec(bContext *C, wmOperator *op) { ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *object = ED_object_add_type(C, OB_POINTCLOUD, NULL, loc, rot, false, local_view_bits); + Object *object = ED_object_add_type(C, OB_POINTCLOUD, nullptr, loc, rot, false, local_view_bits); object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ return OPERATOR_FINISHED; @@ -2062,11 +2078,11 @@ static int object_delete_exec(bContext *C, wmOperator *op) /* FIXME: this will also remove parent from grease pencil from other scenes. */ /* Remove from Grease Pencil parent */ - for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { + LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - if (gpl->parent != NULL) { + if (gpl->parent != nullptr) { if (gpl->parent == ob) { - gpl->parent = NULL; + gpl->parent = nullptr; } } } @@ -2123,8 +2139,8 @@ void OBJECT_OT_delete(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_boolean( - ot->srna, "use_global", 0, "Delete Globally", "Remove object from all scenes"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "use_global", false, "Delete Globally", "Remove object from all scenes"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); WM_operator_properties_confirm_or_exec(ot); } @@ -2188,7 +2204,7 @@ static void copy_object_set_idnew(bContext *C) */ static uint dupliobject_hash(const void *ptr) { - const DupliObject *dob = ptr; + const DupliObject *dob = static_cast<const DupliObject *>(ptr); uint hash = BLI_ghashutil_ptrhash(dob->ob); if (dob->type == OB_DUPLICOLLECTION) { @@ -2210,7 +2226,7 @@ static uint dupliobject_hash(const void *ptr) */ static uint dupliobject_instancer_hash(const void *ptr) { - const DupliObject *dob = ptr; + const DupliObject *dob = static_cast<const DupliObject *>(ptr); uint hash = BLI_ghashutil_inthash(dob->persistent_id[0]); for (int i = 1; (i < MAX_DUPLI_RECUR) && dob->persistent_id[i] != INT_MAX; i++) { hash ^= (dob->persistent_id[i] ^ i); @@ -2221,8 +2237,8 @@ static uint dupliobject_instancer_hash(const void *ptr) /* Compare function that matches dupliobject_hash */ static bool dupliobject_cmp(const void *a_, const void *b_) { - const DupliObject *a = a_; - const DupliObject *b = b_; + const DupliObject *a = static_cast<const DupliObject *>(a_); + const DupliObject *b = static_cast<const DupliObject *>(b_); if (a->ob != b->ob) { return true; @@ -2255,8 +2271,8 @@ static bool dupliobject_cmp(const void *a_, const void *b_) /* Compare function that matches dupliobject_instancer_hash. */ static bool dupliobject_instancer_cmp(const void *a_, const void *b_) { - const DupliObject *a = a_; - const DupliObject *b = b_; + const DupliObject *a = static_cast<const DupliObject *>(a_); + const DupliObject *b = static_cast<const DupliObject *>(b_); for (int i = 0; (i < MAX_DUPLI_RECUR); i++) { if (a->persistent_id[i] != b->persistent_id[i]) { @@ -2280,7 +2296,7 @@ static void make_object_duplilist_real(bContext *C, { Main *bmain = CTX_data_main(C); ViewLayer *view_layer = CTX_data_view_layer(C); - GHash *parent_gh = NULL, *instancer_gh = NULL; + GHash *parent_gh = nullptr, *instancer_gh = nullptr; Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object); @@ -2308,19 +2324,19 @@ static void make_object_duplilist_real(bContext *C, LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) { Object *ob_src = DEG_get_original_object(dob->ob); - Object *ob_dst = ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id)); + Object *ob_dst = static_cast<Object *>(ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id))); id_us_min(&ob_dst->id); /* font duplis can have a totcol without material, we get them from parent * should be implemented better... */ - if (ob_dst->mat == NULL) { + if (ob_dst->mat == nullptr) { ob_dst->totcol = 0; } BKE_collection_object_add_from(bmain, scene, base->object, ob_dst); Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst); - BLI_assert(base_dst != NULL); + BLI_assert(base_dst != nullptr); ED_object_base_select(base_dst, BA_SELECT); DEG_id_tag_update(&ob_dst->id, ID_RECALC_SELECT); @@ -2329,17 +2345,17 @@ static void make_object_duplilist_real(bContext *C, /* make sure apply works */ BKE_animdata_free(&ob_dst->id, true); - ob_dst->adt = NULL; + ob_dst->adt = nullptr; - ob_dst->parent = NULL; + ob_dst->parent = nullptr; BKE_constraints_free(&ob_dst->constraints); - ob_dst->runtime.curve_cache = NULL; + ob_dst->runtime.curve_cache = nullptr; const bool is_dupli_instancer = (ob_dst->transflag & OB_DUPLI) != 0; ob_dst->transflag &= ~OB_DUPLI; /* Remove instantiated collection, it's annoying to keep it here * (and get potentially a lot of usages of it then...). */ id_us_min((ID *)ob_dst->instance_collection); - ob_dst->instance_collection = NULL; + ob_dst->instance_collection = nullptr; copy_m4_m4(ob_dst->obmat, dob->mat); BKE_object_apply_mat4(ob_dst, ob_dst->obmat, false, false); @@ -2365,7 +2381,7 @@ static void make_object_duplilist_real(bContext *C, LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) { Object *ob_src = dob->ob; - Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); + Object *ob_dst = static_cast<Object *>(BLI_ghash_lookup(dupli_gh, dob)); /* Remap new object to itself, and clear again newid pointer of orig object. */ BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0); @@ -2375,7 +2391,7 @@ static void make_object_duplilist_real(bContext *C, if (use_hierarchy) { /* original parents */ Object *ob_src_par = ob_src->parent; - Object *ob_dst_par = NULL; + Object *ob_dst_par = nullptr; /* find parent that was also made real */ if (ob_src_par) { @@ -2392,7 +2408,7 @@ static void make_object_duplilist_real(bContext *C, else { dob_key.persistent_id[0] = dob->persistent_id[0]; } - ob_dst_par = BLI_ghash_lookup(parent_gh, &dob_key); + ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(parent_gh, &dob_key)); } if (ob_dst_par) { @@ -2408,10 +2424,10 @@ static void make_object_duplilist_real(bContext *C, ob_dst->parent = ob_dst_par; } } - if (use_base_parent && ob_dst->parent == NULL) { - Object *ob_dst_par = NULL; + if (use_base_parent && ob_dst->parent == nullptr) { + Object *ob_dst_par = nullptr; - if (instancer_gh != NULL) { + if (instancer_gh != nullptr) { /* OK to keep most of the members uninitialized, * they won't be read, this is simply for a hash lookup. */ DupliObject dob_key; @@ -2421,10 +2437,10 @@ static void make_object_duplilist_real(bContext *C, memcpy(&dob_key.persistent_id[0], &dob->persistent_id[1], sizeof(dob_key.persistent_id[0]) * (MAX_DUPLI_RECUR - 1)); - ob_dst_par = BLI_ghash_lookup(instancer_gh, &dob_key); + ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(instancer_gh, &dob_key)); } - if (ob_dst_par == NULL) { + if (ob_dst_par == nullptr) { /* Default to parenting to root object... * Always the case when use_hierarchy is false. */ ob_dst_par = base->object; @@ -2445,18 +2461,18 @@ static void make_object_duplilist_real(bContext *C, } if (base->object->transflag & OB_DUPLICOLLECTION && base->object->instance_collection) { - base->object->instance_collection = NULL; + base->object->instance_collection = nullptr; } ED_object_base_select(base, BA_DESELECT); DEG_id_tag_update(&base->object->id, ID_RECALC_SELECT); - BLI_ghash_free(dupli_gh, NULL, NULL); + BLI_ghash_free(dupli_gh, nullptr, nullptr); if (parent_gh) { - BLI_ghash_free(parent_gh, NULL, NULL); + BLI_ghash_free(parent_gh, nullptr, nullptr); } if (instancer_gh) { - BLI_ghash_free(instancer_gh, NULL, NULL); + BLI_ghash_free(instancer_gh, nullptr, nullptr); } free_object_duplilist(lb_duplis); @@ -2488,7 +2504,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE, scene); - WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -2511,11 +2527,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_base_parent", - 0, + false, "Parent", "Parent newly created objects to the original instancer"); RNA_def_boolean( - ot->srna, "use_hierarchy", 0, "Keep Hierarchy", "Maintain parent child relationships"); + ot->srna, "use_hierarchy", false, "Keep Hierarchy", "Maintain parent child relationships"); } /** \} */ @@ -2525,7 +2541,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot) * \{ */ static const EnumPropertyItem convert_target_items[] = { - {OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "Curve from Mesh or Text objects"}, + {OB_CURVES_LEGACY, + "CURVE", + ICON_OUTLINER_OB_CURVE, + "Curve", + "Curve from Mesh or Text objects"}, {OB_MESH, "MESH", ICON_OUTLINER_OB_MESH, @@ -2547,19 +2567,19 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { - if (ob->runtime.curve_cache == NULL) { + if (ob->runtime.curve_cache == nullptr) { /* Force creation. This is normally not needed but on operator * redo we might end up with an object which isn't evaluated yet. * Also happens in case we are working on a copy of the object * (all its caches have been nuked then). */ - if (ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { - /* We need 'for render' ON here, to enable computing bevel dipslist if needed. + if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)) { + /* We need 'for render' ON here, to enable computing bevel #DispList if needed. * Also makes sense anyway, we would not want e.g. to lose hidden parts etc. */ BKE_displist_make_curveTypes(depsgraph, scene, ob, true); } @@ -2572,10 +2592,10 @@ static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene * static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Curve *curve = ob->data; + Curve *curve = static_cast<Curve *>(ob->data); Mesh *mesh = BKE_mesh_new_from_object_to_bmain(bmain, depsgraph, object_eval, true); - if (mesh == NULL) { + if (mesh == nullptr) { /* Unable to convert the curve to a mesh. */ return; } @@ -2606,9 +2626,9 @@ static bool object_convert_poll(bContext *C) { Scene *scene = CTX_data_scene(C); Base *base_act = CTX_data_active_base(C); - Object *obact = base_act ? base_act->object : NULL; + Object *obact = base_act ? base_act->object : nullptr; - if (obact == NULL || obact->data == NULL || ID_IS_LINKED(obact) || + if (obact == nullptr || obact->data == nullptr || ID_IS_LINKED(obact) || ID_IS_OVERRIDE_LIBRARY(obact) || ID_IS_OVERRIDE_LIBRARY(obact->data)) { return false; } @@ -2621,7 +2641,7 @@ static bool object_convert_poll(bContext *C) static Base *duplibase_for_convert( Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, Base *base, Object *ob) { - if (ob == NULL) { + if (ob == nullptr) { ob = base->object; } @@ -2636,14 +2656,14 @@ static Base *duplibase_for_convert( /* XXX: An ugly hack needed because if we re-run depsgraph with some new meta-ball objects * having same 'family name' as orig ones, they will affect end result of meta-ball computation. - * For until we get rid of that name-based thingy in MBalls, that should do the trick - * (this is weak, but other solution (to change name of `obn`) is even worse imho). + * For until we get rid of that name-based thingy in meta-balls, that should do the trick + * (this is weak, but other solution (to change name of `obn`) is even worse IMHO). * See T65996. */ const bool is_meta_ball = (obn->type == OB_MBALL); void *obdata = obn->data; if (is_meta_ball) { obn->type = OB_EMPTY; - obn->data = NULL; + obn->data = nullptr; } /* XXX Doing that here is stupid, it means we update and re-evaluate the whole depsgraph every @@ -2675,7 +2695,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - Base *basen = NULL, *basact = NULL; + Base *basen = nullptr, *basact = nullptr; Object *ob1, *obact = CTX_data_active_object(C); const short target = RNA_enum_get(op->ptr, "target"); bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); @@ -2723,7 +2743,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) * on other objects data masks too, see: T50950. */ { LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) { - Base *base = link->ptr.data; + Base *base = static_cast<Base *>(link->ptr.data); Object *ob = base->object; /* The way object type conversion works currently (enforcing conversion of *all* objects @@ -2750,8 +2770,8 @@ static int object_convert_exec(bContext *C, wmOperator *op) } LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) { - Object *newob = NULL; - Base *base = link->ptr.data; + Object *newob = nullptr; + Base *base = static_cast<Base *>(link->ptr.data); Object *ob = base->object; if (ob->flag & OB_DONE || !IS_TAGGED(ob->data)) { @@ -2773,15 +2793,15 @@ static int object_convert_exec(bContext *C, wmOperator *op) } } } - else if (ob->type == OB_MESH && target == OB_CURVE) { + else if (ob->type == OB_MESH && target == OB_CURVES_LEGACY) { ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2793,9 +2813,9 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_mesh_to_curve(bmain, depsgraph, scene, newob); - if (newob->type == OB_CURVE) { + if (newob->type == OB_CURVES_LEGACY) { BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */ - if (newob->rigidbody_object != NULL) { + if (newob->rigidbody_object != nullptr) { ED_rigidbody_object_remove(bmain, scene, newob); } } @@ -2851,11 +2871,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2876,11 +2896,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2912,50 +2932,58 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original curve's usage count. */ id_us_min(&((Curve *)newob->data)->id); /* Make a new copy of the curve. */ - newob->data = BKE_id_copy(bmain, ob->data); + newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data)); } else { newob = ob; } - Curve *cu = newob->data; + Curve *cu = static_cast<Curve *>(newob->data); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_vfont_to_curve_ex(ob_eval, ob_eval->data, FO_EDIT, &cu->nurb, NULL, NULL, NULL, NULL); - - newob->type = OB_CURVE; - cu->type = OB_CURVE; + BKE_vfont_to_curve_ex(ob_eval, + static_cast<Curve *>(ob_eval->data), + FO_EDIT, + &cu->nurb, + nullptr, + nullptr, + nullptr, + nullptr); + + newob->type = OB_CURVES_LEGACY; + cu->type = OB_CURVES_LEGACY; if (cu->vfont) { id_us_min(&cu->vfont->id); - cu->vfont = NULL; + cu->vfont = nullptr; } if (cu->vfontb) { id_us_min(&cu->vfontb->id); - cu->vfontb = NULL; + cu->vfontb = nullptr; } if (cu->vfonti) { id_us_min(&cu->vfonti->id); - cu->vfonti = NULL; + cu->vfonti = nullptr; } if (cu->vfontbi) { id_us_min(&cu->vfontbi->id); - cu->vfontbi = NULL; + cu->vfontbi = nullptr; } if (!keep_original) { /* other users */ if (ID_REAL_USERS(&cu->id) > 1) { - for (ob1 = bmain->objects.first; ob1; ob1 = ob1->id.next) { + for (ob1 = static_cast<Object *>(bmain->objects.first); ob1; + ob1 = static_cast<Object *>(ob1->id.next)) { if (ob1->data == ob->data) { - ob1->type = OB_CURVE; + ob1->type = OB_CURVES_LEGACY; DEG_id_tag_update(&ob1->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } @@ -2985,22 +3013,22 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f); gpencilConverted = true; gpencilCurveConverted = true; - basen = NULL; + basen = nullptr; } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ob->flag |= OB_DONE; if (target == OB_MESH) { if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original curve's usage count. */ id_us_min(&((Curve *)newob->data)->id); /* make a new copy of the curve */ - newob->data = BKE_id_copy(bmain, ob->data); + newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data)); } else { newob = ob; @@ -3013,7 +3041,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_object_free_curve_cache(newob); } else if (target == OB_GPENCIL) { - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { ob->flag &= ~OB_DONE; BKE_report(op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported"); } @@ -3047,23 +3075,24 @@ static int object_convert_exec(bContext *C, wmOperator *op) basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, baseob); newob = basen->object; - MetaBall *mb = newob->data; + MetaBall *mb = static_cast<MetaBall *>(newob->data); id_us_min(&mb->id); newob->data = BKE_mesh_add(bmain, "Mesh"); newob->type = OB_MESH; - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); me->totcol = mb->totcol; if (newob->totcol) { - me->mat = MEM_dupallocN(mb->mat); + me->mat = static_cast<Material **>(MEM_dupallocN(mb->mat)); for (a = 0; a < newob->totcol; a++) { id_us_plus((ID *)me->mat[a]); } } object_data_convert_ensure_curve_cache(depsgraph, scene, baseob); - BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data); + BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, + static_cast<Mesh *>(newob->data)); if (obact->type == OB_MBALL) { basact = basen; @@ -3077,11 +3106,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original point cloud's usage count. */ - PointCloud *pointcloud = newob->data; + PointCloud *pointcloud = static_cast<PointCloud *>(newob->data); id_us_min(&pointcloud->id); /* Make a new copy of the point cloud. */ @@ -3104,7 +3133,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Ensure new object has consistent material data with its new obdata. */ if (newob) { - BKE_object_materials_test(bmain, newob, newob->data); + BKE_object_materials_test(bmain, newob, static_cast<ID *>(newob->data)); } /* tag obdata if it was been changed */ @@ -3116,7 +3145,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) basact = basen; } - basen = NULL; + basen = nullptr; } if (!keep_original && (ob->flag & OB_DONE)) { @@ -3137,7 +3166,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) * their basis happens to be removed first. */ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_mball) { if (ob_mball->type == OB_MBALL) { - Object *ob_basis = NULL; + Object *ob_basis = nullptr; if (!BKE_mball_is_basis(ob_mball) && ((ob_basis = BKE_mball_basis_find(scene, ob_mball)) && (ob_basis->flag & OB_DONE))) { ED_object_base_free_and_unlink(bmain, scene, ob_mball); @@ -3159,7 +3188,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove curves and meshes converted to Grease Pencil object. */ if (gpencilConverted) { FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { - if (ELEM(ob_delete->type, OB_CURVE, OB_MESH)) { + if (ELEM(ob_delete->type, OB_CURVES_LEGACY, OB_MESH)) { if (ob_delete->flag & OB_DONE) { ED_object_base_free_and_unlink(bmain, scene, ob_delete); } @@ -3172,7 +3201,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */ if (gpencilCurveConverted) { FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { - if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) { + if (ELEM(ob_delete->type, OB_CURVES_LEGACY) && (ob_delete->flag & OB_DONE)) { ED_object_base_free_and_unlink(bmain, scene, ob_delete); } } @@ -3181,7 +3210,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) } // XXX ED_object_editmode_enter(C, 0); - // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */ + // XXX exit_editmode(C, EM_FREEDATA|); /* free data, but no undo */ if (basact) { /* active base was changed */ @@ -3208,15 +3237,15 @@ static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) uiLayoutSetPropSep(layout, true); - uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "target", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "keep_original", 0, nullptr, ICON_NONE); if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) { - uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "thickness", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "angle", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "seams", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "faces", 0, nullptr, ICON_NONE); } } @@ -3243,14 +3272,14 @@ void OBJECT_OT_convert(wmOperatorType *ot) ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); RNA_def_boolean(ot->srna, "keep_original", - 0, + false, "Keep Original", "Keep original objects instead of replacing them"); prop = RNA_def_float_rotation(ot->srna, "angle", 0, - NULL, + nullptr, DEG2RADF(0.0f), DEG2RADF(180.0f), "Threshold Angle", @@ -3260,8 +3289,8 @@ void OBJECT_OT_convert(wmOperatorType *ot) RNA_def_property_float_default(prop, DEG2RADF(70.0f)); RNA_def_int(ot->srna, "thickness", 5, 1, 100, "Thickness", "", 1, 100); - RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges"); - RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes"); + RNA_def_boolean(ot->srna, "seams", false, "Only Seam Edges", "Convert only seam edges"); + RNA_def_boolean(ot->srna, "faces", true, "Export Faces", "Export faces as filled strokes"); RNA_def_float_distance(ot->srna, "offset", 0.01f, @@ -3279,16 +3308,11 @@ void OBJECT_OT_convert(wmOperatorType *ot) /** \name Duplicate Object Operator * \{ */ -/* - * dupflag: a flag made from constants declared in DNA_userdef_types.h - * The flag tells adduplicate() whether to copy data linked to the object, - * or to reference the existing data. - * U.dupflag for default operations or you can construct a flag as python does - * if the dupflag is 0 then no data will be copied (linked duplicate). */ - -/* used below, assumes id.new is correct */ -/* leaves selection of base/object unaltered */ -/* Does set ID->newid pointers. */ +/** + * - Assumes `id.new` is correct. + * - Leaves selection of base/object unaltered. + * - Sets #ID.newid pointers. + */ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -3296,18 +3320,19 @@ static Base *object_add_duplicate_internal(Main *bmain, const eDupli_ID_Flags dupflag, const eLibIDDuplicateFlags duplicate_options) { - Base *base, *basen = NULL; + Base *base, *basen = nullptr; Object *obn; if (ob->mode & OB_MODE_POSE) { /* nothing? */ } else { - obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)); + obn = static_cast<Object *>( + ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options))); DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); - if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if ((base != nullptr) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { BKE_collection_object_add_from(bmain, scene, ob, obn); } else { @@ -3316,7 +3341,7 @@ static Base *object_add_duplicate_internal(Main *bmain, } basen = BKE_view_layer_base_find(view_layer, obn); - if (base != NULL) { + if (base != nullptr) { basen->local_view_bits = base->local_view_bits; } @@ -3325,8 +3350,7 @@ static Base *object_add_duplicate_internal(Main *bmain, */ /* XXX: is 2) really a good measure here? */ if (ob->rigidbody_object || ob->rigidbody_constraint) { - Collection *collection; - for (collection = bmain->collections.first; collection; collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob)) { BKE_collection_object_add(bmain, collection, obn); } @@ -3349,8 +3373,8 @@ Base *ED_object_add_duplicate( dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - if (basen == NULL) { - return NULL; + if (basen == nullptr) { + return nullptr; } ob = basen->object; @@ -3363,7 +3387,7 @@ Base *ED_object_add_duplicate( /* DAG_relations_tag_update(bmain); */ /* caller must do */ - if (ob->data != NULL) { + if (ob->data != nullptr) { DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } @@ -3379,7 +3403,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool linked = RNA_boolean_get(op->ptr, "linked"); - const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; + const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ @@ -3399,7 +3423,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) ED_object_base_select(base, BA_DESELECT); ED_object_base_select(basen, BA_SELECT); - if (basen == NULL) { + if (basen == nullptr) { continue; } @@ -3409,7 +3433,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) } if (basen->object->data) { - DEG_id_tag_update(basen->object->data, 0); + DEG_id_tag_update(static_cast<ID *>(basen->object->data), 0); } } CTX_DATA_END; @@ -3447,7 +3471,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) /* to give to transform */ prop = RNA_def_boolean(ot->srna, "linked", - 0, + false, "Linked", "Duplicate object but not object data, linking to the original data"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -3473,14 +3497,14 @@ static int object_add_named_exec(bContext *C, wmOperator *op) Base *basen; Object *ob; const bool linked = RNA_boolean_get(op->ptr, "linked"); - const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; + const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; /* find object, create fake base */ RNA_string_get(op->ptr, "name", name); ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); - if (ob == NULL) { + if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); return OPERATOR_CANCELLED; } @@ -3499,7 +3523,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) */ LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - if (basen == NULL) { + if (basen == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } @@ -3510,7 +3534,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ - ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); + ED_object_base_deselect_all(view_layer, nullptr, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); @@ -3566,11 +3590,11 @@ void OBJECT_OT_add_named(wmOperatorType *ot) "Linked", "Duplicate object but not object data, linking to the original data"); - RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add"); prop = RNA_def_float_matrix( - ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); object_add_drop_xy_props(ot); } @@ -3599,7 +3623,7 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) ob = OBACT(view_layer); } - if (ob == NULL) { + if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); return OPERATOR_CANCELLED; } @@ -3615,8 +3639,10 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); if (RNA_property_is_set(op->ptr, prop_matrix)) { + ObjectsInViewLayerParams params = {0}; uint objects_len; - Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0}); + Object **objects = BKE_view_layer_array_selected_objects_params( + view_layer, nullptr, &objects_len, ¶ms); float matrix[4][4]; RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); @@ -3674,14 +3700,14 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) PropertyRNA *prop; RNA_def_string(ot->srna, "name", - NULL, + nullptr, MAX_ID_NAME - 2, "Name", "Object name to place (when unset use the active object)"); prop = RNA_def_float_matrix( - ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); object_add_drop_xy_props(ot); } @@ -3696,12 +3722,12 @@ static bool object_join_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || + if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || ID_IS_OVERRIDE_LIBRARY(ob->data)) { return false; } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_ARMATURE, OB_GPENCIL)) { return ED_operator_screenactive(C); } return false; @@ -3740,7 +3766,7 @@ static int object_join_exec(bContext *C, wmOperator *op) if (ob->type == OB_MESH) { ret = ED_mesh_join_objects_exec(C, op); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ret = ED_curve_join_objects_exec(C, op); } else if (ob->type == OB_ARMATURE) { @@ -3802,7 +3828,7 @@ static bool join_shapes_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || + if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || ID_IS_OVERRIDE_LIBRARY(ob->data)) { return false; } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 30e3f2b0c69..3b40a10eb2a 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -301,6 +301,7 @@ static void bake_targets_refresh(BakeTargets *targets) Image *ima = targets->images[i].image; if (ima) { + BKE_image_partial_update_mark_full_update(ima); LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); @@ -605,7 +606,7 @@ static bool bake_objects_check(Main *bmain, continue; } - if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { + if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL) == false) { BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, " @@ -1038,19 +1039,18 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, * materials and UVs. */ pixel->seed = v; - /* Barycentric coordinates, nudged a bit to avoid precision issues that - * may happen when exactly at the vertex coordinate. */ + /* Barycentric coordinates. */ if (j == 0) { - pixel->uv[0] = 1.0f - FLT_EPSILON; - pixel->uv[1] = FLT_EPSILON / 2.0f; + pixel->uv[0] = 1.0f; + pixel->uv[1] = 0.0f; } else if (j == 1) { - pixel->uv[0] = FLT_EPSILON / 2.0f; - pixel->uv[1] = 1.0f - FLT_EPSILON; + pixel->uv[0] = 0.0f; + pixel->uv[1] = 1.0f; } else if (j == 2) { - pixel->uv[0] = FLT_EPSILON / 2.0f; - pixel->uv[1] = FLT_EPSILON / 2.0f; + pixel->uv[0] = 0.0f; + pixel->uv[1] = 0.0f; } } } diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 4ccd35b512b..3f4ed27a175 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -511,7 +511,7 @@ static void test_constraint( * * In other cases it should be impossible to have a type mismatch. */ - if (ct->tar->type != OB_CURVE) { + if (ct->tar->type != OB_CURVES_LEGACY) { con->flag |= CONSTRAINT_DISABLE; } else { @@ -1443,6 +1443,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Object *ob = ED_object_active_context(C); bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL); /* Store name temporarily for report. */ @@ -1510,6 +1515,11 @@ static int constraint_apply_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Object *ob = ED_object_active_context(C); bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); @@ -1602,6 +1612,11 @@ static int constraint_copy_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Object *ob = ED_object_active_context(C); bConstraint *con = edit_constraint_property_get(C, op, ob, 0); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); @@ -1682,6 +1697,11 @@ static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Object *obact = ED_object_active_context(C); bConstraint *con = edit_constraint_property_get(C, op, obact, 0); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ED_object_constraint_list_from_constraint(obact, con, &pchan); @@ -2275,7 +2295,8 @@ static bool get_new_constraint_target( break; } - if (((!only_curve) || (ob->type == OB_CURVE)) && ((!only_mesh) || (ob->type == OB_MESH))) { + if (((!only_curve) || (ob->type == OB_CURVES_LEGACY)) && + ((!only_mesh) || (ob->type == OB_MESH))) { /* set target */ *tar_ob = ob; found = true; diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index f74556e3639..63513eac965 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -382,7 +382,7 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; struct Key *key = cu->key; @@ -505,7 +505,7 @@ void ED_object_data_xform_destroy(struct XFormObjectData *xod_base) } break; } - case ID_CU: { + case ID_CU_LEGACY: { struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base; if (xod->key_data != NULL) { MEM_freeN(xod->key_data); @@ -565,7 +565,7 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float break; } - case ID_CU: { + case ID_CU_LEGACY: { BLI_assert(xod_base->is_edit_mode == false); /* Not used currently. */ Curve *cu = (Curve *)xod_base->id; @@ -670,7 +670,7 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base) break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)xod_base->id; struct Key *key = cu->key; @@ -745,7 +745,7 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) DEG_id_tag_update(<->id, ID_RECALC_GEOMETRY); break; } - case ID_CU: { + case ID_CU_LEGACY: { /* Generic update. */ Curve *cu = (Curve *)xod_base->id; DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index fd40ac7bc7c..82b14787d9b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -21,6 +21,7 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_collection_types.h" #include "DNA_curve_types.h" @@ -73,6 +74,7 @@ #include "ED_curve.h" #include "ED_gpencil.h" #include "ED_image.h" +#include "ED_keyframes_keylist.h" #include "ED_lattice.h" #include "ED_mball.h" #include "ED_mesh.h" @@ -89,7 +91,7 @@ #include "CLG_log.h" -/* For menu/popup icons etc etc. */ +/* For menu/popup icons etc. */ #include "UI_interface.h" #include "UI_resources.h" @@ -340,10 +342,10 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); int index = RNA_int_get(op->ptr, "collection_index"); - const bool extend = (win->eventstate->shift != 0); + const bool extend = (win->eventstate->modifier & KM_SHIFT) != 0; const bool toggle = RNA_boolean_get(op->ptr, "toggle"); - if (win->eventstate->alt != 0) { + if (win->eventstate->modifier & KM_ALT) { index += 10; } @@ -568,7 +570,7 @@ static bool ED_object_editmode_load_free_ex(Main *bmain, */ DEG_relations_tag_update(bmain); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { const Curve *cu = obedit->data; if (cu->editnurb == NULL) { return false; @@ -799,12 +801,16 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); } - else if (ELEM(ob->type, OB_SURF, OB_CURVE)) { + else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) { ok = true; ED_curve_editnurb_make(ob); WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } + else if (ob->type == OB_CURVES) { + ok = true; + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVES, scene); + } if (ok) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -1019,7 +1025,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object) if (!md) { if (pd && (pd->shape == PFIELD_SHAPE_SURFACE) && !ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) { - if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) { + if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVES_LEGACY)) { ED_object_modifier_add(NULL, bmain, scene, object, NULL, eModifierType_Surface); } } @@ -1205,30 +1211,32 @@ static int object_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEv /* set default settings from existing/stored settings */ { bAnimVizSettings *avs = &ob->avs; - - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); + RNA_enum_set(op->ptr, "display_type", avs->path_type); + RNA_enum_set(op->ptr, "range", avs->path_range); } /* show popup dialog to allow editing of range... */ /* FIXME: hard-coded dimensions here are just arbitrary. */ - return WM_operator_props_dialog_popup(C, op, 200); + return WM_operator_props_dialog_popup(C, op, 270); } /* Calculate/recalculate whole paths (avs.path_sf to avs.path_ef) */ static int object_calculate_paths_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - int start = RNA_int_get(op->ptr, "start_frame"); - int end = RNA_int_get(op->ptr, "end_frame"); + short path_type = RNA_enum_get(op->ptr, "display_type"); + short path_range = RNA_enum_get(op->ptr, "range"); - /* set up path data for bones being calculated */ + /* set up path data for objects being calculated */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - bAnimVizSettings *avs = &ob->avs; - - /* grab baking settings from operator settings */ - avs->path_sf = start; - avs->path_ef = end; + /* When operator is not invoked, dismiss the operator settings */ + if (op->flag & OP_IS_INVOKE) { + bAnimVizSettings *avs = &ob->avs; + /* grab baking settings from operator settings */ + avs->path_type = path_type; + avs->path_range = path_range; + } + animviz_motionpath_compute_range(ob, scene); /* verify that the selected object has the appropriate settings */ animviz_verify_motionpaths(op->reports, scene, ob, NULL); @@ -1247,9 +1255,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) void OBJECT_OT_paths_calculate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Calculate Object Paths"; + ot->name = "Calculate Object Motion Paths"; ot->idname = "OBJECT_OT_paths_calculate"; - ot->description = "Calculate motion paths for the selected objects"; + ot->description = "Generate motion paths for the selected objects"; /* api callbacks */ ot->invoke = object_calculate_paths_invoke; @@ -1260,24 +1268,18 @@ void OBJECT_OT_paths_calculate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_int(ot->srna, - "start_frame", - 1, - MINAFRAME, - MAXFRAME, - "Start", - "First frame to calculate object paths on", - MINFRAME, - MAXFRAME / 2.0); - RNA_def_int(ot->srna, - "end_frame", - 250, - MINAFRAME, - MAXFRAME, - "End", - "Last frame to calculate object paths on", - MINFRAME, - MAXFRAME / 2.0); + RNA_def_enum(ot->srna, + "display_type", + rna_enum_motionpath_display_type_items, + MOTIONPATH_TYPE_RANGE, + "Display type", + ""); + RNA_def_enum(ot->srna, + "range", + rna_enum_motionpath_range_items, + MOTIONPATH_RANGE_SCENE, + "Computation Range", + ""); } /** \} */ @@ -1296,13 +1298,19 @@ static bool object_update_paths_poll(bContext *C) return false; } -static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) +static int object_update_paths_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); if (scene == NULL) { return OPERATOR_CANCELLED; } + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + animviz_motionpath_compute_range(ob, scene); + /* verify that the selected object has the appropriate settings */ + animviz_verify_motionpaths(op->reports, scene, ob, NULL); + } + CTX_DATA_END; /* calculate the paths for objects that have them (and are tagged to get refreshed) */ ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); @@ -1423,7 +1431,7 @@ static int object_clear_paths_exec(bContext *C, wmOperator *op) /* operator callback/wrapper */ static int object_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return object_clear_paths_exec(C, op); @@ -1453,46 +1461,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Update Motion Paths Range from Scene Operator - * \{ */ - -static int object_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - - /* Loop over all editable objects in scene. */ - CTX_DATA_BEGIN (C, Object *, ob, editable_objects) { - /* use Preview Range or Full Frame Range - whichever is in use */ - ob->avs.path_sf = PSFRA; - ob->avs.path_ef = PEFRA; - - /* tag for updates */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - } - CTX_DATA_END; - - return OPERATOR_FINISHED; -} - -void OBJECT_OT_paths_range_update(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Update Range from Scene"; - ot->idname = "OBJECT_OT_paths_range_update"; - ot->description = "Update frame range for motion paths from the Scene's current frame range"; - - /* callbacks */ - ot->exec = object_update_paths_range_exec; - ot->poll = ED_operator_object_active_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Object Shade Smooth/Flat Operator * \{ */ @@ -1548,7 +1516,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } - else if (ELEM(ob->type, OB_SURF, OB_CURVE)) { + else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) { BKE_curve_smooth_flag_set(ob->data, use_smooth); changed = true; } diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 338307dc8ca..dffbb3bedd5 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -342,7 +342,7 @@ static bool object_hook_index_array(Main *bmain, } return true; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: ED_curve_editnurb_load(bmain, obedit); ED_curve_editnurb_make(obedit); @@ -447,7 +447,7 @@ static void object_hook_select(Object *ob, HookModifierData *hmd) else if (ob->type == OB_LATTICE) { select_editlattice_hook(ob, hmd); } - else if (ob->type == OB_CURVE) { + else if (ob->type == OB_CURVES_LEGACY) { select_editcurve_hook(ob, hmd); } else if (ob->type == OB_SURF) { diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 0aac5957c9d..135c76140c1 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -76,7 +76,6 @@ void OBJECT_OT_shade_flat(struct wmOperatorType *ot); void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); void OBJECT_OT_paths_update(struct wmOperatorType *ot); void OBJECT_OT_paths_clear(struct wmOperatorType *ot); -void OBJECT_OT_paths_range_update(struct wmOperatorType *ot); void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot); void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot); @@ -97,7 +96,7 @@ void OBJECT_OT_select_more(struct wmOperatorType *ot); void OBJECT_OT_select_less(struct wmOperatorType *ot); void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); -/* object_add.c */ +/* object_add.cc */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 509e496c39a..8e9e8558016 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -118,7 +118,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_MBALL: @@ -143,7 +143,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } break; case OB_CURVES: - if (mode & (OB_MODE_SCULPT_CURVES)) { + if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) { return true; } break; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 38a955dca0a..e0d25baec16 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -110,7 +110,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object * else if (ob->type == OB_MBALL) { BKE_displist_make_mball(depsgraph, scene_eval, ob_eval); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false); } else if (ob->type == OB_GPENCIL) { @@ -757,7 +757,7 @@ static bool modifier_apply_obdata( } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Curve *curve = ob->data; Curve *curve_eval = (Curve *)object_eval->data; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 35f5ede270d..45ee4daa441 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -45,7 +45,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_paths_calculate); WM_operatortype_append(OBJECT_OT_paths_update); WM_operatortype_append(OBJECT_OT_paths_clear); - WM_operatortype_append(OBJECT_OT_paths_range_update); WM_operatortype_append(OBJECT_OT_paths_update_visible); WM_operatortype_append(OBJECT_OT_forcefield_toggle); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 8834941d083..3ecf86d14ed 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -169,7 +169,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } } } - else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) { + else if (ELEM(obedit->type, OB_SURF, OB_CURVES_LEGACY)) { ListBase *editnurb = object_editcurve_get(obedit); for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) { @@ -341,7 +341,7 @@ EnumPropertyItem prop_clear_parent_types[] = { /* Helper for ED_object_parent_clear() - Remove deform-modifiers associated with parent */ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) { - if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVE)) { + if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVES_LEGACY)) { ModifierData *md, *mdn; /* assume that we only need to remove the first instance of matching deform modifier here */ @@ -363,7 +363,7 @@ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) free = true; } } - else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVE)) { + else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVES_LEGACY)) { CurveModifierData *cmd = (CurveModifierData *)md; if (cmd->object == par) { free = true; @@ -531,7 +531,7 @@ bool ED_object_parent_set(ReportList *reports, switch (partype) { case PAR_FOLLOW: case PAR_PATH_CONST: { - if (par->type != OB_CURVE) { + if (par->type != OB_CURVES_LEGACY) { return false; } Curve *cu = par->data; @@ -626,7 +626,7 @@ bool ED_object_parent_set(ReportList *reports, */ /* XXX currently this should only happen for meshes, curves, surfaces, * and lattices - this stuff isn't available for meta-balls yet. */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; switch (partype) { @@ -968,7 +968,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE_RELATIVE); } - else if (parent->type == OB_CURVE) { + else if (parent->type == OB_CURVES_LEGACY) { uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_CURVE); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_FOLLOW); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_PATH_CONST); @@ -1820,7 +1820,7 @@ static void single_obdata_users( ob->data, BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: ob->data = cu = ID_NEW_SET( @@ -2318,7 +2318,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id_root, &obact->id, NULL); + bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, NULL); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 44e6fb10528..2e495ac6147 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -406,7 +406,7 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve d = cd->slow_mval[0] - mval[0]; } - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* Linear mode, enables jumping to any voxel size. */ d = d * 0.0005f; } diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index f017fea7cdf..9e82abf4d07 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -613,7 +613,7 @@ static int apply_objects_internal(bContext *C, OB_ARMATURE, OB_LATTICE, OB_MBALL, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_GPENCIL)) { @@ -639,13 +639,13 @@ static int apply_objects_internal(bContext *C, } } - if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ID *obdata = ob->data; Curve *cu; cu = ob->data; - if (((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) { + if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) { BKE_reportf( reports, RPT_ERROR, @@ -811,7 +811,7 @@ static int apply_objects_internal(bContext *C, MetaBall *mb = ob->data; BKE_mball_transform(mb, mat, do_props); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; scale = mat3_to_scale(rsmat); BKE_curve_transform_ex(cu, mat, true, do_props, scale); @@ -1209,7 +1209,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) do_inverse_offset = true; } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; if (centermode == ORIGIN_TO_CURSOR) { @@ -1223,7 +1223,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } /* don't allow Z change if curve is 2D */ - if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { + if ((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) { cent[2] = 0.0; } @@ -1861,7 +1861,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const view3d_operator_needs_opengl(C); - const bool is_translate = (event->ctrl != 0); + const bool is_translate = event->modifier & KM_CTRL; const bool is_translate_init = is_translate && (xfd->is_translate != is_translate); if (event->type == MOUSEMOVE || is_translate_init) { diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 19940fbb0fc..cb9c8a92abe 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -64,7 +64,7 @@ bool ED_object_calc_active_center_for_editmode(Object *obedit, break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: { Curve *cu = obedit->data; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index af9496263e7..fc815ebe682 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4684,7 +4684,7 @@ static int brush_edit_init(bContext *C, wmOperator *op) bedit->ob = ob; bedit->edit = edit; - bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min, NULL); + bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min); /* cache view depths and settings for re-use */ PE_set_view3d_data(C, &bedit->data); @@ -4757,7 +4757,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) switch (pset->brushtype) { case PE_BRUSH_COMB: { - const float mval_f[2] = {dx, dy}; + const float xy_delta[2] = {dx, dy}; data.mval = mval; data.rad = pe_brush_size_get(scene, brush); @@ -4771,7 +4771,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) invert_m4_m4(ob->imat, ob->obmat); - ED_view3d_win_to_delta(region, mval_f, vec, bedit->zfac); + ED_view3d_win_to_delta(region, xy_delta, bedit->zfac, vec); data.dvec = vec; foreach_mouse_hit_key(&data, brush_comb, selected); @@ -4963,7 +4963,7 @@ static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *e RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->shift != false); /* XXX hardcoded */ + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_SHIFT); /* XXX hardcoded */ /* apply */ brush_edit_apply(C, op, &itemptr); diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index 5308d4611e7..03c0f3977b7 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -618,6 +618,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec static void current_scene_update(void *rjv, Scene *scene) { RenderJob *rj = static_cast<RenderJob *>(rjv); + + if (rj->current_scene != scene) { + /* Image must be updated when rendered scene changes. */ + BKE_image_partial_update_mark_full_update(rj->image); + } + rj->current_scene = scene; rj->iuser.scene = scene; } @@ -1188,5 +1194,6 @@ void RENDER_OT_shutter_curve_preset(wmOperatorType *ot) ot->exec = render_shutter_curve_preset_exec; prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 1ca2b01a2cb..be66e87f2e5 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -599,30 +599,30 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data) const ID_Type id_type = GS(id->name); switch (id_type) { /* Whitelist: */ - case ID_ME: /* Mesh */ - case ID_CU: /* Curve */ - case ID_MB: /* MetaBall */ - case ID_MA: /* Material */ - case ID_TE: /* Tex (Texture) */ - case ID_IM: /* Image */ - case ID_LT: /* Lattice */ - case ID_LA: /* Light */ - case ID_CA: /* Camera */ - case ID_KE: /* Key (shape key) */ - case ID_VF: /* VFont (Vector Font) */ - case ID_TXT: /* Text */ - case ID_SPK: /* Speaker */ - case ID_SO: /* Sound */ - case ID_AR: /* bArmature */ - case ID_NT: /* bNodeTree */ - case ID_PA: /* ParticleSettings */ - case ID_MC: /* MovieClip */ - case ID_MSK: /* Mask */ - case ID_LP: /* LightProbe */ - case ID_CV: /* Curves */ - case ID_PT: /* PointCloud */ - case ID_VO: /* Volume */ - case ID_SIM: /* Simulation */ + case ID_ME: /* Mesh */ + case ID_CU_LEGACY: /* Curve */ + case ID_MB: /* MetaBall */ + case ID_MA: /* Material */ + case ID_TE: /* Tex (Texture) */ + case ID_IM: /* Image */ + case ID_LT: /* Lattice */ + case ID_LA: /* Light */ + case ID_CA: /* Camera */ + case ID_KE: /* Key (shape key) */ + case ID_VF: /* VFont (Vector Font) */ + case ID_TXT: /* Text */ + case ID_SPK: /* Speaker */ + case ID_SO: /* Sound */ + case ID_AR: /* bArmature */ + case ID_NT: /* bNodeTree */ + case ID_PA: /* ParticleSettings */ + case ID_MC: /* MovieClip */ + case ID_MSK: /* Mask */ + case ID_LP: /* LightProbe */ + case ID_CV: /* Curves */ + case ID_PT: /* PointCloud */ + case ID_VO: /* Volume */ + case ID_SIM: /* Simulation */ break; /* Blacklist: */ diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 6aaf551a88a..eca30a6ac25 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -10,6 +10,7 @@ #include <cmath> #include <cstdlib> #include <cstring> +#include <list> #ifndef WIN32 # include <unistd.h> @@ -1370,89 +1371,73 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat ShaderPreview *sp = static_cast<ShaderPreview *>(customdata); if (sp->pr_method == PR_ICON_DEFERRED) { - PreviewImage *prv = static_cast<PreviewImage *>(sp->owner); - ImBuf *thumb; - char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(prv)); - ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); - char *path = &deferred_data[1]; - - // printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path); - - thumb = IMB_thumb_manage(path, THB_LARGE, source); - - if (thumb) { - /* PreviewImage assumes premultiplied alhpa... */ - IMB_premultiply_alpha(thumb); - - icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect); - IMB_freeImBuf(thumb); - } + BLI_assert_unreachable(); + return; } - else { - ID *id = sp->id; - short idtype = GS(id->name); - BLI_assert(id != nullptr); - - if (idtype == ID_IM) { - Image *ima = (Image *)id; - ImBuf *ibuf = nullptr; - ImageUser iuser; - BKE_imageuser_default(&iuser); + ID *id = sp->id; + short idtype = GS(id->name); - if (ima == nullptr) { - return; - } + BLI_assert(id != nullptr); - /* setup dummy image user */ - iuser.framenr = 1; - iuser.scene = sp->scene; - - /* NOTE(@elubie): this needs to be changed: here image is always loaded if not - * already there. Very expensive for large images. Need to find a way to - * only get existing `ibuf`. */ - ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); - if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(ima, ibuf, nullptr); - return; - } + if (idtype == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf = nullptr; + ImageUser iuser; + BKE_imageuser_default(&iuser); - icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); + if (ima == nullptr) { + return; + } - *do_update = true; + /* setup dummy image user */ + iuser.framenr = 1; + iuser.scene = sp->scene; + /* NOTE(@elubie): this needs to be changed: here image is always loaded if not + * already there. Very expensive for large images. Need to find a way to + * only get existing `ibuf`. */ + ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); + if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { BKE_image_release_ibuf(ima, ibuf, nullptr); + return; } - else if (idtype == ID_BR) { - Brush *br = (Brush *)id; - br->icon_imbuf = icon_preview_imbuf_from_brush(br); + icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); - memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); + *do_update = true; - if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { - return; - } + BKE_image_release_ibuf(ima, ibuf, nullptr); + } + else if (idtype == ID_BR) { + Brush *br = (Brush *)id; - icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + br->icon_imbuf = icon_preview_imbuf_from_brush(br); - *do_update = true; - } - else if (idtype == ID_SCR) { - bScreen *screen = (bScreen *)id; + memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); - ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); - *do_update = true; + if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { + return; } - else { - /* re-use shader job */ - shader_preview_startjob(customdata, stop, do_update); - /* world is rendered with alpha=0, so it wasn't displayed - * this could be render option for sky to, for later */ - if (idtype == ID_WO) { - set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); - } + icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + + *do_update = true; + } + else if (idtype == ID_SCR) { + bScreen *screen = (bScreen *)id; + + ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); + *do_update = true; + } + else { + /* re-use shader job */ + shader_preview_startjob(customdata, stop, do_update); + + /* world is rendered with alpha=0, so it wasn't displayed + * this could be render option for sky to, for later */ + if (idtype == ID_WO) { + set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); } } } @@ -1670,6 +1655,197 @@ static void icon_preview_endjob(void *customdata) } } +/** + * Background job to manage requests for deferred loading of previews from the hard drive. + * + * Launches a single job to manage all incoming preview requests. The job is kept running until all + * preview requests are done loading (or it's otherwise aborted, e.g. by closing Blender). + * + * Note that this will use the OS thumbnail cache, i.e. load a preview from there or add it if not + * there yet. These two cases may lead to different performance. + */ +class PreviewLoadJob { + struct RequestedPreview { + PreviewImage *preview; + /** Requested size. */ + eIconSizes icon_size; + }; + + /** The previews that are still to be loaded. */ + ThreadQueue *todo_queue_; /* RequestedPreview * */ + /** All unfinished preview requests, #update_fn() calls #finish_preview_request() on loaded + * previews and removes them from this list. Only access from the main thread! */ + std::list<struct RequestedPreview> requested_previews_; + + public: + PreviewLoadJob(); + ~PreviewLoadJob(); + + static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *); + static void load_jobless(PreviewImage *, eIconSizes); + + void push_load_request(PreviewImage *, eIconSizes); + + private: + static void run_fn(void *, short *, short *, float *); + static void update_fn(void *); + static void end_fn(void *); + static void free_fn(void *); + + /** Mark a single requested preview as being done, remove the request. */ + static void finish_request(RequestedPreview &); +}; + +PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init()) +{ +} + +PreviewLoadJob::~PreviewLoadJob() +{ + BLI_thread_queue_free(todo_queue_); +} + +PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win) +{ + wmJob *wm_job = WM_jobs_get(wm, win, nullptr, "Load Previews", 0, WM_JOB_TYPE_LOAD_PREVIEW); + + if (!WM_jobs_is_running(wm_job)) { + PreviewLoadJob *job_data = MEM_new<PreviewLoadJob>("PreviewLoadJobData"); + + WM_jobs_customdata_set(wm_job, job_data, free_fn); + WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); + WM_jobs_callbacks(wm_job, run_fn, nullptr, update_fn, end_fn); + + WM_jobs_start(wm, wm_job); + } + + return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job)); +} + +void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size) +{ + PreviewLoadJob job_data{}; + + job_data.push_load_request(preview, icon_size); + + short stop = 0, do_update = 0; + float progress = 0; + run_fn(&job_data, &stop, &do_update, &progress); + update_fn(&job_data); + end_fn(&job_data); +} + +void PreviewLoadJob::push_load_request(PreviewImage *preview, const eIconSizes icon_size) +{ + BLI_assert(preview->tag & PRV_TAG_DEFFERED); + RequestedPreview requested_preview{}; + requested_preview.preview = preview; + requested_preview.icon_size = icon_size; + + preview->flag[icon_size] |= PRV_RENDERING; + /* Warn main thread code that this preview is being rendered and cannot be freed. */ + preview->tag |= PRV_TAG_DEFFERED_RENDERING; + + requested_previews_.push_back(requested_preview); + BLI_thread_queue_push(todo_queue_, &requested_previews_.back()); +} + +void PreviewLoadJob::run_fn(void *customdata, + short *stop, + short *do_update, + float *UNUSED(progress)) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + IMB_thumb_locks_acquire(); + + while (RequestedPreview *request = reinterpret_cast<RequestedPreview *>( + BLI_thread_queue_pop_timeout(job_data->todo_queue_, 100))) { + if (*stop) { + break; + } + + PreviewImage *preview = request->preview; + + const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview)); + const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); + const char *path = &deferred_data[1]; + + // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path); + + IMB_thumb_path_lock(path); + ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source); + IMB_thumb_path_unlock(path); + + if (thumb) { + /* PreviewImage assumes premultiplied alpha... */ + IMB_premultiply_alpha(thumb); + + icon_copy_rect(thumb, + preview->w[request->icon_size], + preview->h[request->icon_size], + preview->rect[request->icon_size]); + IMB_freeImBuf(thumb); + } + + *do_update = true; + } + + IMB_thumb_locks_release(); +} + +/* Only execute on the main thread! */ +void PreviewLoadJob::finish_request(RequestedPreview &request) +{ + PreviewImage *preview = request.preview; + + preview->tag &= ~PRV_TAG_DEFFERED_RENDERING; + BKE_previewimg_finish(preview, request.icon_size); + + BLI_assert_msg(BLI_thread_is_main(), + "Deferred releasing of preview images should only run on the main thread"); + if (preview->tag & PRV_TAG_DEFFERED_DELETE) { + BLI_assert(preview->tag & PRV_TAG_DEFFERED); + BKE_previewimg_deferred_release(preview); + } +} + +void PreviewLoadJob::update_fn(void *customdata) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + for (auto request_it = job_data->requested_previews_.begin(); + request_it != job_data->requested_previews_.end();) { + RequestedPreview &requested = *request_it; + /* Skip items that are not done loading yet. */ + if (requested.preview->tag & PRV_TAG_DEFFERED_RENDERING) { + ++request_it; + continue; + } + finish_request(requested); + + /* Remove properly finished previews from the job data. */ + auto next_it = job_data->requested_previews_.erase(request_it); + request_it = next_it; + } +} + +void PreviewLoadJob::end_fn(void *customdata) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + /* Finish any possibly remaining queued previews. */ + for (RequestedPreview &request : job_data->requested_previews_) { + finish_request(request); + } + job_data->requested_previews_.clear(); +} + +void PreviewLoadJob::free_fn(void *customdata) +{ + MEM_delete(reinterpret_cast<PreviewLoadJob *>(customdata)); +} + static void icon_preview_free(void *customdata) { IconPreview *ip = (IconPreview *)customdata; @@ -1698,8 +1874,19 @@ bool ED_preview_id_is_supported(const ID *id) } void ED_preview_icon_render( - const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey) + const bContext *C, Scene *scene, PreviewImage *prv_img, ID *id, eIconSizes icon_size) { + /* Deferred loading of previews from the file system. */ + if (prv_img->tag & PRV_TAG_DEFFERED) { + if (prv_img->flag[icon_size] & PRV_RENDERING) { + /* Already in the queue, don't add it again. */ + return; + } + + PreviewLoadJob::load_jobless(prv_img, icon_size); + return; + } + IconPreview ip = {nullptr}; short stop = false, update = false; float progress = 0.0f; @@ -1716,7 +1903,10 @@ void ED_preview_icon_render( ip.id_copy = duplicate_ids(id, true); ip.active_object = CTX_data_active_object(C); - icon_preview_add_size(&ip, rect, sizex, sizey); + prv_img->flag[icon_size] |= PRV_RENDERING; + + icon_preview_add_size( + &ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]); icon_preview_startjob_all_sizes(&ip, &stop, &update, &progress); @@ -1729,20 +1919,31 @@ void ED_preview_icon_render( } void ED_preview_icon_job( - const bContext *C, void *owner, ID *id, uint *rect, int sizex, int sizey, const bool delay) + const bContext *C, PreviewImage *prv_img, ID *id, eIconSizes icon_size, const bool delay) { - wmJob *wm_job; + /* Deferred loading of previews from the file system. */ + if (prv_img->tag & PRV_TAG_DEFFERED) { + if (prv_img->flag[icon_size] & PRV_RENDERING) { + /* Already in the queue, don't add it again. */ + return; + } + PreviewLoadJob &load_job = PreviewLoadJob::ensure_job(CTX_wm_manager(C), CTX_wm_window(C)); + load_job.push_load_request(prv_img, icon_size); + + return; + } + IconPreview *ip, *old_ip; ED_preview_ensure_dbase(); /* suspended start means it starts after 1 timer step, see WM_jobs_timer below */ - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - owner, - "Icon Preview", - WM_JOB_EXCL_RENDER, - WM_JOB_TYPE_RENDER_PREVIEW); + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + prv_img, + "Icon Preview", + WM_JOB_EXCL_RENDER, + WM_JOB_TYPE_RENDER_PREVIEW); ip = MEM_cnew<IconPreview>("icon preview"); @@ -1757,20 +1958,14 @@ void ED_preview_icon_job( ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ip->scene = DEG_get_input_scene(ip->depsgraph); ip->active_object = CTX_data_active_object(C); - ip->owner = owner; + ip->owner = prv_img; ip->id = id; ip->id_copy = duplicate_ids(id, false); - icon_preview_add_size(ip, rect, sizex, sizey); + prv_img->flag[icon_size] |= PRV_RENDERING; - /* Special threading hack: - * warn main code that this preview is being rendered and cannot be freed... */ - { - PreviewImage *prv_img = static_cast<PreviewImage *>(owner); - if (prv_img->tag & PRV_TAG_DEFFERED) { - prv_img->tag |= PRV_TAG_DEFFERED_RENDERING; - } - } + icon_preview_add_size( + ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]); /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index bad4208cf19..cd395674177 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -310,7 +310,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Nurb *nu; ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data); @@ -411,7 +411,7 @@ static int material_slot_de_select(bContext *C, bool select) changed = EDBM_deselect_by_material(em, mat_nr_active, select); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data); Nurb *nu; BPoint *bp; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 95534d2a036..af84f6f99a9 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3438,6 +3438,37 @@ bool ED_area_is_global(const ScrArea *area) return area->global != NULL; } +ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int xy[2]) +{ + bScreen *screen = CTX_wm_screen(C); + wmWindow *win = CTX_wm_window(C); + + ScrArea *area = NULL; + + if (win->parent) { + /* If active window is a child, check itself first. */ + area = BKE_screen_find_area_xy(screen, spacetype, xy); + } + + if (!area) { + /* Check all windows except the active one. */ + int scr_pos[2]; + wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos); + if (r_win && r_win != win) { + win = r_win; + screen = WM_window_get_active_screen(win); + area = BKE_screen_find_area_xy(screen, spacetype, scr_pos); + } + } + + if (!area && !win->parent) { + /* If active window is a parent window, check itself last. */ + area = BKE_screen_find_area_xy(screen, spacetype, xy); + } + + return area; +} + ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen) { ScrArea *global_area = win->global_areas.areabase.first; diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index f43bc08151a..8a84f4cf079 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -77,8 +77,9 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, * filtering results. Mipmaps can be used to get better results (i.e. #GL_LINEAR_MIPMAP_LINEAR), * so always use mipmaps when filtering. */ const bool use_mipmap = use_filter && ((draw_width < img_w) || (draw_height < img_h)); + const int mips = use_mipmap ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, 1, gpu_format, NULL); + GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, mips, gpu_format, NULL); const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 7171aa7ac3b..ee3bc3cba76 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -585,7 +585,7 @@ bool ED_operator_uvmap(bContext *C) bool ED_operator_editsurfcurve(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { return NULL != ((Curve *)obedit->data)->editnurb; } return false; @@ -604,7 +604,7 @@ bool ED_operator_editsurfcurve_region_view3d(bContext *C) bool ED_operator_editcurve(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_CURVE) { + if (obedit && obedit->type == OB_CURVES_LEGACY) { return NULL != ((Curve *)obedit->data)->editnurb; } return false; @@ -613,7 +613,7 @@ bool ED_operator_editcurve(bContext *C) bool ED_operator_editcurve_3d(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_CURVE) { + if (obedit && obedit->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)obedit->data; return (cu->flag & CU_3D) && (NULL != cu->editnurb); @@ -1008,9 +1008,6 @@ static void actionzone_exit(wmOperator *op) static void actionzone_apply(bContext *C, wmOperator *op, int type) { wmWindow *win = CTX_wm_window(C); - sActionzoneData *sad = op->customdata; - - sad->modifier = RNA_int_get(op->ptr, "modifier"); wmEvent event; wm_event_init_from_window(win, &event); @@ -1026,7 +1023,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) } event.val = KM_NOTHING; - event.is_repeat = false; + event.flag = 0; event.customdata = op->customdata; event.customdata_free = true; op->customdata = NULL; @@ -1051,6 +1048,7 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) sad->az = az; sad->x = event->xy[0]; sad->y = event->xy[1]; + sad->modifier = RNA_int_get(op->ptr, "modifier"); /* region azone directly reacts on mouse clicks */ if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) { @@ -1114,7 +1112,17 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* What area are we now in? */ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy); - if (area == sad->sa1) { + if (sad->modifier == 1) { + /* Duplicate area into new window. */ + WM_cursor_set(win, WM_CURSOR_EDIT); + is_gesture = (delta_max > area_threshold); + } + else if (sad->modifier == 2) { + /* Swap areas. */ + WM_cursor_set(win, WM_CURSOR_SWAP_AREA); + is_gesture = true; + } + else if (area == sad->sa1) { /* Same area, so possible split. */ WM_cursor_set(win, SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT : @@ -1320,8 +1328,9 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: - /* second area, for join */ - sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy); + /* Second area to swap with. */ + sad->sa2 = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, event->xy); + WM_cursor_set(CTX_wm_window(C), (sad->sa2) ? WM_CURSOR_SWAP_AREA : WM_CURSOR_STOP); break; case LEFTMOUSE: /* release LMB */ if (event->val == KM_RELEASE) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 55099e2e750..59fbc3a64fb 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../bmesh ../../depsgraph ../../draw + ../../functions ../../gpu ../../imbuf ../../makesdna @@ -24,11 +25,13 @@ set(INC ) set(SRC + curves_sculpt_ops.cc paint_cursor.c paint_curve.c paint_curve_undo.c paint_hide.c - paint_image.c + paint_image.cc + paint_image_ops_paint.cc paint_image_2d.c paint_image_2d_curve_mask.cc paint_image_proj.c @@ -66,6 +69,7 @@ set(SRC sculpt_undo.c sculpt_uv.c + curves_sculpt_intern.h paint_intern.h sculpt_intern.h ) @@ -75,5 +79,16 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.h b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h new file mode 100644 index 00000000000..0d99e61192f --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct bContext; + +#ifdef __cplusplus +extern "C" { +#endif + +bool CURVES_SCULPT_mode_poll(struct bContext *C); +bool CURVES_SCULPT_mode_poll_view3d(struct bContext *C); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc new file mode 100644 index 00000000000..936226a03ed --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -0,0 +1,411 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_utildefines.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_paint.h" + +#include "WM_api.h" +#include "WM_toolsystem.h" + +#include "ED_curves_sculpt.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_screen_types.h" + +#include "RNA_access.h" + +#include "BLI_index_mask_ops.hh" +#include "BLI_math_vector.hh" + +#include "curves_sculpt_intern.h" +#include "paint_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Poll Functions + * \{ */ + +bool CURVES_SCULPT_mode_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + return ob && ob->mode & OB_MODE_SCULPT_CURVES; +} + +bool CURVES_SCULPT_mode_poll_view3d(bContext *C) +{ + if (!CURVES_SCULPT_mode_poll(C)) { + return false; + } + if (CTX_wm_region_view3d(C) == nullptr) { + return false; + } + return true; +} + +/** \} */ + +namespace blender::ed::sculpt_paint { + +using blender::bke::CurvesGeometry; + +/* -------------------------------------------------------------------- */ +/** \name * SCULPT_CURVES_OT_brush_stroke + * \{ */ + +struct StrokeExtension { + bool is_first; + float2 mouse_position; +}; + +/** + * Base class for stroke based operations in curves sculpt mode. + */ +class CurvesSculptStrokeOperation { + public: + virtual ~CurvesSculptStrokeOperation() = default; + virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; +}; + +class DeleteOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) + { + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position : + last_mouse_position_; + const float2 mouse_end = stroke_extension.mouse_position; + + /* Find indices of curves that have to be removed. */ + Vector<int64_t> indices; + const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 512, indices, [&](const int curve_i) { + const IndexRange point_range = curves.range_for_curve(curve_i); + for (const int segment_i : IndexRange(point_range.size() - 1)) { + const float3 pos1 = positions[point_range[segment_i]]; + const float3 pos2 = positions[point_range[segment_i + 1]]; + + float2 pos1_proj, pos2_proj; + ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values); + ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values); + + const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end); + if (dist <= brush_radius) { + return true; + } + } + return false; + }); + + /* Just reset positions instead of actually removing the curves. This is just a prototype. */ + threading::parallel_for(curves_to_remove.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curves_to_remove.slice(range)) { + for (const int point_i : curves.range_for_curve(curve_i)) { + positions[point_i] = {0.0f, 0.0f, 0.0f}; + } + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + + last_mouse_position_ = stroke_extension.mouse_position; + } +}; + +class MoveOperation : public CurvesSculptStrokeOperation { + private: + Vector<int64_t> points_to_move_indices_; + IndexMask points_to_move_; + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) + { + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + if (stroke_extension.is_first) { + /* Find point indices to move. */ + points_to_move_ = index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 512, points_to_move_indices_, [&](const int64_t point_i) { + const float3 position = positions[point_i]; + float2 screen_position; + ED_view3d_project_float_v2_m4(region, position, screen_position, projection.values); + const float distance = len_v2v2(screen_position, stroke_extension.mouse_position); + return distance <= brush_radius; + }); + } + else { + /* Move points based on mouse movement. */ + const float2 mouse_diff = stroke_extension.mouse_position - last_mouse_position_; + threading::parallel_for(points_to_move_.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : points_to_move_.slice(range)) { + const float3 old_position = positions[point_i]; + float2 old_position_screen; + ED_view3d_project_float_v2_m4( + region, old_position, old_position_screen, projection.values); + const float2 new_position_screen = old_position_screen + mouse_diff; + float3 new_position; + ED_view3d_win_to_3d(v3d, region, old_position, new_position_screen, new_position); + positions[point_i] = new_position; + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } + + last_mouse_position_ = stroke_extension.mouse_position; + } +}; + +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, + wmOperator *UNUSED(op)) +{ + Scene &scene = *CTX_data_scene(C); + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + switch (brush.curves_sculpt_tool) { + case CURVES_SCULPT_TOOL_TEST1: + return std::make_unique<MoveOperation>(); + case CURVES_SCULPT_TOOL_TEST2: + return std::make_unique<DeleteOperation>(); + } + BLI_assert_unreachable(); + return {}; +} + +struct SculptCurvesBrushStrokeData { + std::unique_ptr<CurvesSculptStrokeOperation> operation; + PaintStroke *stroke; +}; + +static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +{ + out[0] = mouse[0]; + out[1] = mouse[1]; + out[2] = 0; + UNUSED_VARS(C); + return true; +} + +static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) +{ + UNUSED_VARS(C, op, mouse); + return true; +} + +static void stroke_update_step(bContext *C, + wmOperator *op, + PaintStroke *UNUSED(stroke), + PointerRNA *stroke_element) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + + StrokeExtension stroke_extension; + RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); + + if (!op_data->operation) { + stroke_extension.is_first = true; + op_data->operation = start_brush_operation(C, op); + } + else { + stroke_extension.is_first = false; + } + + op_data->operation->on_stroke_extended(C, stroke_extension); +} + +static void stroke_done(const bContext *C, PaintStroke *stroke) +{ + UNUSED_VARS(C, stroke); +} + +static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__); + op_data->stroke = paint_stroke_new(C, + op, + stroke_get_location, + stroke_test_start, + stroke_update_step, + nullptr, + stroke_done, + event->type); + op->customdata = op_data; + + int return_value = op->type->modal(C, op, event); + if (return_value == OPERATOR_FINISHED) { + paint_stroke_free(C, op, op_data->stroke); + MEM_delete(op_data); + return OPERATOR_FINISHED; + } + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + int return_value = paint_stroke_modal(C, op, event, op_data->stroke); + if (ELEM(return_value, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { + MEM_delete(op_data); + } + return return_value; +} + +static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + paint_stroke_cancel(C, op, op_data->stroke); + MEM_delete(op_data); +} + +static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) +{ + ot->name = "Stroke Curves Sculpt"; + ot->idname = "SCULPT_CURVES_OT_brush_stroke"; + ot->description = "Sculpt curves using a brush"; + + ot->invoke = sculpt_curves_stroke_invoke; + ot->modal = sculpt_curves_stroke_modal; + ot->cancel = sculpt_curves_stroke_cancel; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + paint_stroke_operator_properties(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name * CURVES_OT_sculptmode_toggle + * \{ */ + +static bool curves_sculptmode_toggle_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob == nullptr) { + return false; + } + if (ob->type != OB_CURVES) { + return false; + } + return true; +} + +static void curves_sculptmode_enter(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; + + ob->mode = OB_MODE_SCULPT_CURVES; + + paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d); +} + +static void curves_sculptmode_exit(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + ob->mode = OB_MODE_OBJECT; +} + +static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES; + + if (is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + if (is_mode_set) { + curves_sculptmode_exit(C); + } + else { + curves_sculptmode_enter(C); + } + + WM_toolsystem_update_from_context_view3d(C); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); + return OPERATOR_CANCELLED; +} + +static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) +{ + ot->name = "Curve Sculpt Mode Toggle"; + ot->idname = "CURVES_OT_sculptmode_toggle"; + ot->description = "Enter/Exit sculpt mode for curves"; + + ot->exec = curves_sculptmode_toggle_exec; + ot->poll = curves_sculptmode_toggle_poll; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + +} // namespace blender::ed::sculpt_paint + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name * Registration + * \{ */ + +void ED_operatortypes_sculpt_curves() +{ + using namespace blender::ed::sculpt_paint; + WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke); + WM_operatortype_append(CURVES_OT_sculptmode_toggle); +} + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.cc index df9e2cf136f..0c73c2e1f43 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -6,10 +6,10 @@ * \brief Functions to paint images in 2D and 3D. */ -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <string.h> +#include <cfloat> +#include <cmath> +#include <cstdio> +#include <cstring> #include "MEM_guardedalloc.h" @@ -67,6 +67,8 @@ #include "paint_intern.h" +extern "C" { + /** * This is a static resource for non-global access. * Maybe it should be exposed as part of the paint operation, @@ -84,7 +86,7 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) imapaintpartial = *ippr; } -/* Imagepaint Partial Redraw & Dirty Region */ +/* Image paint Partial Redraw & Dirty Region. */ void ED_imapaint_clear_partial_redraw(void) { @@ -96,7 +98,7 @@ void imapaint_region_tiles( { int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS); *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS); @@ -107,11 +109,11 @@ void imapaint_region_tiles( void ED_imapaint_dirty_region( Image *ima, ImBuf *ibuf, ImageUser *iuser, int x, int y, int w, int h, bool find_old) { - ImBuf *tmpibuf = NULL; + ImBuf *tmpibuf = nullptr; int tilex, tiley, tilew, tileh, tx, ty; int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); if (w == 0 || h == 0) { return; @@ -128,7 +130,7 @@ void ED_imapaint_dirty_region( for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { ED_image_paint_tile_push( - undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, NULL, NULL, false, find_old); + undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, nullptr, nullptr, false, find_old); } } @@ -169,17 +171,19 @@ void imapaint_image_update( BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) { int i, j; - BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + BlurKernel *kernel = MEM_new<BlurKernel>("BlurKernel"); + float radius; int side; - eBlurKernelType type = br->blur_mode; + eBlurKernelType type = static_cast<eBlurKernelType>(br->blur_mode); if (proj) { radius = 0.5f; side = kernel->side = 2; kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->wdata = static_cast<float *>( + MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data")); kernel->pixel_len = radius; } else { @@ -191,7 +195,8 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) side = kernel->side = radius * 2 + 1; kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->wdata = static_cast<float *>( + MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data")); kernel->pixel_len = br->blur_kernel_radius; } @@ -224,9 +229,9 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) default: printf("unidentified kernel type, aborting\n"); - MEM_freeN(kernel->wdata); - MEM_freeN(kernel); - return NULL; + paint_delete_blur_kernel(kernel); + MEM_delete(kernel); + return nullptr; } return kernel; @@ -267,7 +272,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) SpaceImage *sima = CTX_wm_space_image(C); if (sima) { - if (sima->image != NULL && ID_IS_LINKED(sima->image)) { + if (sima->image != nullptr && ID_IS_LINKED(sima->image)) { return false; } ARegion *region = CTX_wm_region(C); @@ -281,7 +286,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) return false; } -static bool image_paint_poll(bContext *C) +bool image_paint_poll(bContext *C) { return image_paint_poll_ex(C, true); } @@ -307,24 +312,6 @@ static bool image_paint_2d_clone_poll(bContext *C) } /************************ paint operator ************************/ -typedef enum eTexPaintMode { - PAINT_MODE_2D, - PAINT_MODE_3D_PROJECT, -} eTexPaintMode; - -typedef struct PaintOperation { - eTexPaintMode mode; - - void *custom_paint; - - float prevmouse[2]; - float startmouse[2]; - double starttime; - - void *cursor; - ViewContext vc; -} PaintOperation; - bool paint_use_opacity_masking(Brush *brush) { return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) || @@ -369,7 +356,7 @@ void paint_brush_color_get(struct Scene *scene, break; } } - /* Gradient / Colorband colors are not considered PROP_COLOR_GAMMA. + /* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA. * Brush colors are expected to be in sRGB though. */ IMB_colormanagement_scene_linear_to_srgb_v3(color_gr); @@ -414,340 +401,11 @@ void paint_brush_exit_tex(Brush *brush) } } -static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) -{ - PaintOperation *pop = (PaintOperation *)customdata; - - if (pop) { - GPU_line_smooth(true); - GPU_blend(GPU_BLEND_ALPHA); - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - - ARegion *region = pop->vc.region; - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_width(4.0); - immUniformColor4ub(0, 0, 0, 255); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i( - pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); - immEnd(); - - GPU_line_width(2.0); - immUniformColor4ub(255, 255, 255, 255); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i( - pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); - immEnd(); - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); - GPU_line_smooth(false); - } -} - -static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ - Brush *brush = BKE_paint_brush(&settings->imapaint.paint); - int mode = RNA_enum_get(op->ptr, "mode"); - ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); - - copy_v2_v2(pop->prevmouse, mouse); - copy_v2_v2(pop->startmouse, mouse); - - /* initialize from context */ - if (CTX_wm_region_view3d(C)) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - bool uvs, mat, tex, stencil; - if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { - ED_paint_data_warning(op->reports, uvs, mat, tex, stencil); - MEM_freeN(pop); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - return NULL; - } - pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); - } - else { - pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op, mode); - } - - if (!pop->custom_paint) { - MEM_freeN(pop); - return NULL; - } - - if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { - pop->cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop); - } - - settings->imapaint.flag |= IMAGEPAINT_DRAWING; - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - - return pop; -} - -static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) -{ - PaintOperation *pop = paint_stroke_mode_data(stroke); - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = CTX_data_tool_settings(C); - UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; - - /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startalpha = BKE_brush_alpha_get(scene, brush); - - float mouse[2]; - float pressure; - float size; - float distance = paint_stroke_distance_get(stroke); - int eraser; - - RNA_float_get_array(itemptr, "mouse", mouse); - pressure = RNA_float_get(itemptr, "pressure"); - eraser = RNA_boolean_get(itemptr, "pen_flip"); - size = RNA_float_get(itemptr, "size"); - - /* stroking with fill tool only acts on stroke end */ - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - copy_v2_v2(pop->prevmouse, mouse); - return; - } - - if (BKE_brush_use_alpha_pressure(brush)) { - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); - } - else { - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); - } - - if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { - UndoStack *ustack = CTX_wm_manager(C)->undo_stack; - ED_image_undo_restore(ustack->step_init); - } - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke( - C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - - copy_v2_v2(pop->prevmouse, mouse); - - /* restore brush values */ - BKE_brush_alpha_set(scene, brush, startalpha); -} - -static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) -{ - PaintOperation *pop = paint_stroke_mode_data(stroke); - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_redraw(C, pop->custom_paint, final); - } - else { - paint_2d_redraw(C, pop->custom_paint, final); - } -} - -static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) -{ - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = scene->toolsettings; - PaintOperation *pop = paint_stroke_mode_data(stroke); - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - if (brush->flag & BRUSH_USE_GRADIENT) { - if (pop->mode == PAINT_MODE_2D) { - paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke(C, - pop->custom_paint, - pop->startmouse, - pop->prevmouse, - paint_stroke_flipped(stroke), - 1.0, - 0.0, - BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - else { - if (pop->mode == PAINT_MODE_2D) { - float color[3]; - if (paint_stroke_inverted(stroke)) { - srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); - } - else { - srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); - } - paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke(C, - pop->custom_paint, - pop->startmouse, - pop->prevmouse, - paint_stroke_flipped(stroke), - 1.0, - 0.0, - BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - } - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke_done(pop->custom_paint); - } - else { - paint_2d_stroke_done(pop->custom_paint); - } - - if (pop->cursor) { - WM_paint_cursor_end(pop->cursor); - } - - ED_image_undo_push_end(); - - /* duplicate warning, see texpaint_init */ -#if 0 - if (pop->s.warnmultifile) { - BKE_reportf(op->reports, - RPT_WARNING, - "Image requires 4 color channels to paint: %s", - pop->s.warnmultifile); - } - if (pop->s.warnpackedfile) { - BKE_reportf(op->reports, - RPT_WARNING, - "Packed MultiLayer files cannot be painted: %s", - pop->s.warnpackedfile); - } -#endif - MEM_freeN(pop); -} - -static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) -{ - PaintOperation *pop; - - /* TODO: Should avoid putting this here. Instead, last position should be requested - * from stroke system. */ - - if (!(pop = texture_paint_init(C, op, mouse))) { - return false; - } - - paint_stroke_set_mode_data(op->customdata, pop); - - return true; -} - -static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - int retval; - - op->customdata = paint_stroke_new(C, - op, - NULL, - paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, - event->type); - - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); - - return OPERATOR_RUNNING_MODAL; -} - -static int paint_exec(bContext *C, wmOperator *op) -{ - PropertyRNA *strokeprop; - PointerRNA firstpoint; - float mouse[2]; - - strokeprop = RNA_struct_find_property(op->ptr, "stroke"); - - if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { - return OPERATOR_CANCELLED; - } - - RNA_float_get_array(&firstpoint, "mouse", mouse); - - op->customdata = paint_stroke_new(C, - op, - NULL, - paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, - 0); - /* frees op->customdata */ - return paint_stroke_exec(C, op); -} - -void PAINT_OT_image_paint(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Image Paint"; - ot->idname = "PAINT_OT_image_paint"; - ot->description = "Paint a stroke into the image"; - - /* api callbacks */ - ot->invoke = paint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = paint_exec; - ot->poll = image_paint_poll; - ot->cancel = paint_stroke_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; - - paint_stroke_operator_properties(ot); -} - bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; + SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first); if (sima->mode == SI_MODE_PAINT) { ARegion *region = CTX_wm_region(C); ED_space_image_get_zoom(sima, region, zoomx, zoomy); @@ -768,8 +426,8 @@ static void toggle_paint_cursor(Scene *scene, bool enable) Paint *p = &settings->imapaint.paint; if (p->paint_cursor && !enable) { - WM_paint_cursor_end(p->paint_cursor); - p->paint_cursor = NULL; + WM_paint_cursor_end(static_cast<wmPaintCursor *>(p->paint_cursor)); + p->paint_cursor = nullptr; paint_cursor_delete_textures(); } else if (enable) { @@ -807,10 +465,10 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene) /************************ grab clone operator ************************/ -typedef struct GrabClone { +struct GrabClone { float startoffset[2]; int startx, starty; -} GrabClone; +}; static void grab_clone_apply(bContext *C, wmOperator *op) { @@ -834,7 +492,7 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event) Brush *brush = image_paint_brush(C); GrabClone *cmv; - cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); + cmv = MEM_new<GrabClone>("GrabClone"); copy_v2_v2(cmv->startoffset, brush->clone.offset); cmv->startx = event->xy[0]; cmv->starty = event->xy[1]; @@ -849,7 +507,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) { Brush *brush = image_paint_brush(C); ARegion *region = CTX_wm_region(C); - GrabClone *cmv = op->customdata; + GrabClone *cmv = static_cast<GrabClone *>(op->customdata); float startfx, startfy, fx, fy, delta[2]; int xmin = region->winrct.xmin, ymin = region->winrct.ymin; @@ -880,7 +538,8 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op) { - MEM_freeN(op->customdata); + GrabClone *cmv = static_cast<GrabClone *>(op->customdata); + MEM_delete(cmv); } void PAINT_OT_grab_clone(wmOperatorType *ot) @@ -904,7 +563,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) RNA_def_float_vector(ot->srna, "delta", 2, - NULL, + nullptr, -FLT_MAX, FLT_MAX, "Delta", @@ -914,12 +573,12 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) } /******************** sample color operator ********************/ -typedef struct { +struct SampleColorData { bool show_cursor; short launch_event; float initcolor[3]; bool sample_palette; -} SampleColorData; +}; static void sample_color_update_header(SampleColorData *data, bContext *C) { @@ -973,7 +632,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); + SampleColorData *data = MEM_new<SampleColorData>("sample color custom data"); ARegion *region = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); @@ -1009,7 +668,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); - SampleColorData *data = op->customdata; + SampleColorData *data = static_cast<SampleColorData *>(op->customdata); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); @@ -1023,8 +682,8 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) RNA_boolean_set(op->ptr, "palette", true); } WM_cursor_modal_restore(CTX_wm_window(C)); - MEM_freeN(data); - ED_workspace_status_text(C, NULL); + MEM_delete(data); + ED_workspace_status_text(C, nullptr); return OPERATOR_FINISHED; } @@ -1083,25 +742,26 @@ void PAINT_OT_sample_color(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_int_vector( + ot->srna, "location", 2, nullptr, 0, INT_MAX, "Location", "", 0, 16384); + RNA_def_property_flag(prop, static_cast<PropertyFlag>(PROP_SKIP_SAVE | PROP_HIDDEN)); - RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color"); - RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", ""); + RNA_def_boolean(ot->srna, "merged", false, "Sample Merged", "Sample the output display color"); + RNA_def_boolean(ot->srna, "palette", false, "Add to Palette", ""); } /******************** texture paint toggle operator ********************/ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob) { - Image *ima = NULL; + Image *ima = nullptr; ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; /* This has to stay here to regenerate the texture paint * cache in case we are loading a file */ BKE_texpaint_slots_refresh_object(scene, ob); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); /* entering paint mode also sets image to editors */ if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { @@ -1117,11 +777,11 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob } if (ima) { - wmWindowManager *wm = bmain->wm.first; - for (wmWindow *win = wm->windows.first; win; win = win->next) { + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { const bScreen *screen = WM_window_get_active_screen(win); - for (ScrArea *area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl = area->spacedata.first; + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); if (sl->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)sl; @@ -1142,12 +802,12 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, 0); + BKE_image_paint_set_mipmap(bmain, false); toggle_paint_cursor(scene, true); Mesh *me = BKE_mesh_from_object(ob); - BLI_assert(me != NULL); + BLI_assert(me != nullptr); DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_SCENE | ND_MODE, scene); } @@ -1167,11 +827,11 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob) if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, 1); + BKE_image_paint_set_mipmap(bmain, true); toggle_paint_cursor(scene, false); Mesh *me = BKE_mesh_from_object(ob); - BLI_assert(me != NULL); + BLI_assert(me != nullptr); DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_SCENE | ND_MODE, scene); } @@ -1187,7 +847,7 @@ void ED_object_texture_paint_mode_exit(bContext *C) static bool texture_paint_toggle_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) { + if (ob == nullptr || ob->type != OB_MESH) { return false; } if (!ob->data || ID_IS_LINKED(ob->data)) { @@ -1207,7 +867,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) const bool is_mode_set = (ob->mode & mode_flag) != 0; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, ob, static_cast<eObjectMode>(mode_flag), op->reports)) { return OPERATOR_CANCELLED; } } @@ -1274,7 +934,7 @@ static bool brush_colors_flip_poll(bContext *C) } else { Object *ob = CTX_data_active_object(C); - if (ob != NULL) { + if (ob != nullptr) { if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) { return true; } @@ -1310,8 +970,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - const float mouse_init[2] = {mouse[0], mouse[1]}; - paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL); + const float mouse_init[2] = {static_cast<float>(mouse[0]), static_cast<float>(mouse[1])}; + paint_2d_bucket_fill(C, color, nullptr, mouse_init, nullptr, nullptr); ED_image_undo_push_end(); @@ -1349,3 +1009,4 @@ bool mask_paint_poll(bContext *C) { return BKE_paint_select_elem_test(CTX_data_active_object(C)); } +} diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc index 64115f7c6eb..f5657b004e2 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc @@ -53,6 +53,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, { BLI_assert(curve_mask_cache->curve_mask != nullptr); int offset = (int)floorf(diameter / 2.0f); + int clamped_radius = max_ff(radius, 1.0); unsigned short *m = curve_mask_cache->curve_mask; @@ -76,7 +77,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, pixel_xy[1] = static_cast<float>(y) + aa_offset; for (int j = 0; j < aa_samples; j++) { const float len = len_v2v2(pixel_xy, bpos); - const int sample_index = min_ii((len / radius) * CurveSamplesBaseLen, + const int sample_index = min_ii((len / clamped_radius) * CurveSamplesBaseLen, CurveSamplesLen - 1); const float sample_weight = curve_mask_cache->sampled_curve[sample_index]; diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc new file mode 100644 index 00000000000..786fcc47526 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup edsculpt + * \brief Painting operator to paint in 2D and 3D. + */ + +#include "DNA_brush_types.h" +#include "DNA_color_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_paint.h" +#include "BKE_undo_system.h" + +#include "DEG_depsgraph.h" + +#include "ED_paint.h" +#include "ED_view3d.h" + +#include "GPU_immediate.h" +#include "GPU_state.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "paint_intern.h" + +namespace blender::ed::sculpt_paint::image::ops::paint { + +/** + * Interface to use the same painting operator for 3D and 2D painting. Interface removes the + * differences between the actual calls that are being performed. + */ +class AbstractPaintMode { + public: + virtual ~AbstractPaintMode() = default; + virtual void *paint_new_stroke( + bContext *C, wmOperator *op, Object *ob, const float mouse[2], int mode) = 0; + virtual void paint_stroke(bContext *C, + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) = 0; + + virtual void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) = 0; + virtual void paint_stroke_done(void *stroke_handle) = 0; + virtual void paint_gradient_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) = 0; + virtual void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) = 0; +}; + +class ImagePaintMode : public AbstractPaintMode { + public: + void *paint_new_stroke(bContext *C, + wmOperator *op, + Object *UNUSED(ob), + const float UNUSED(mouse[2]), + int mode) override + { + return paint_2d_new_stroke(C, op, mode); + } + + void paint_stroke(bContext *UNUSED(C), + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) override + { + paint_2d_stroke(stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size); + } + + void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override + { + paint_2d_redraw(C, stroke_handle, final); + } + + void paint_stroke_done(void *stroke_handle) override + { + paint_2d_stroke_done(stroke_handle); + } + + void paint_gradient_fill(const bContext *C, + const Scene *UNUSED(scene), + Brush *brush, + struct PaintStroke *UNUSED(stroke), + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_2d_gradient_fill(C, brush, mouse_start, mouse_end, stroke_handle); + } + + void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + float color[3]; + if (paint_stroke_inverted(stroke)) { + srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); + } + else { + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + } + paint_2d_bucket_fill(C, color, brush, mouse_start, mouse_end, stroke_handle); + } +}; + +class ProjectionPaintMode : public AbstractPaintMode { + public: + void *paint_new_stroke( + bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override + { + return paint_proj_new_stroke(C, ob, mouse, mode); + } + + void paint_stroke(bContext *C, + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) override + { + paint_proj_stroke(C, stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size); + }; + + void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override + { + paint_proj_redraw(C, stroke_handle, final); + } + + void paint_stroke_done(void *stroke_handle) override + { + paint_proj_stroke_done(stroke_handle); + } + + void paint_gradient_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end); + } + + void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end); + } + + private: + void paint_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) + { + paint_proj_stroke(C, + stroke_handle, + mouse_start, + mouse_end, + paint_stroke_flipped(stroke), + 1.0, + 0.0, + BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, stroke_handle, false); + paint_proj_redraw(C, stroke_handle, true); + } +}; + +struct PaintOperation { + AbstractPaintMode *mode = nullptr; + + void *stroke_handle = nullptr; + + float prevmouse[2] = {0.0f, 0.0f}; + float startmouse[2] = {0.0f, 0.0f}; + double starttime = 0.0; + + wmPaintCursor *cursor = nullptr; + ViewContext vc = {nullptr}; + + PaintOperation() = default; + ~PaintOperation() + { + MEM_delete(mode); + mode = nullptr; + + if (cursor) { + WM_paint_cursor_end(cursor); + cursor = nullptr; + } + } +}; + +static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +{ + PaintOperation *pop = (PaintOperation *)customdata; + + if (pop) { + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + + ARegion *region = pop->vc.region; + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_line_width(4.0); + immUniformColor4ub(0, 0, 0, 255); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i( + pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); + immEnd(); + + GPU_line_width(2.0); + immUniformColor4ub(255, 255, 255, 255); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i( + pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); + immEnd(); + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); + } +} + +static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + PaintOperation *pop = MEM_new<PaintOperation>("PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); + int mode = RNA_enum_get(op->ptr, "mode"); + ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); + + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); + + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + + /* initialize from context */ + if (CTX_wm_region_view3d(C)) { + bool uvs, mat, tex, stencil; + if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { + ED_paint_data_warning(op->reports, uvs, mat, tex, stencil); + MEM_delete(pop); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); + return nullptr; + } + pop->mode = MEM_new<ProjectionPaintMode>("ProjectionPaintMode"); + } + else { + pop->mode = MEM_new<ImagePaintMode>("ImagePaintMode"); + } + + pop->stroke_handle = pop->mode->paint_new_stroke(C, op, ob, mouse, mode); + if (!pop->stroke_handle) { + MEM_delete(pop); + return nullptr; + } + + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate( + SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop); + } + + settings->imapaint.flag |= IMAGEPAINT_DRAWING; + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); + + return pop; +} + +static void paint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) +{ + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; + + /* initial brush values. Maybe it should be considered moving these to stroke system */ + float startalpha = BKE_brush_alpha_get(scene, brush); + + float mouse[2]; + float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); + int eraser; + + RNA_float_get_array(itemptr, "mouse", mouse); + pressure = RNA_float_get(itemptr, "pressure"); + eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = RNA_float_get(itemptr, "size"); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } + + if (BKE_brush_use_alpha_pressure(brush)) { + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + } + else { + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + } + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + UndoStack *ustack = CTX_wm_manager(C)->undo_stack; + ED_image_undo_restore(ustack->step_init); + } + + pop->mode->paint_stroke( + C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size); + + copy_v2_v2(pop->prevmouse, mouse); + + /* restore brush values */ + BKE_brush_alpha_set(scene, brush, startalpha); +} + +static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) +{ + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + pop->mode->paint_stroke_redraw(C, pop->stroke_handle, final); +} + +static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = scene->toolsettings; + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + pop->mode->paint_gradient_fill( + C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse); + } + else { + pop->mode->paint_bucket_fill( + C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse); + } + } + pop->mode->paint_stroke_done(pop->stroke_handle); + pop->stroke_handle = nullptr; + + ED_image_undo_push_end(); + +/* duplicate warning, see texpaint_init */ +#if 0 + if (pop->s.warnmultifile) { + BKE_reportf(op->reports, + RPT_WARNING, + "Image requires 4 color channels to paint: %s", + pop->s.warnmultifile); + } + if (pop->s.warnpackedfile) { + BKE_reportf(op->reports, + RPT_WARNING, + "Packed MultiLayer files cannot be painted: %s", + pop->s.warnpackedfile); + } +#endif + MEM_delete(pop); +} + +static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) +{ + PaintOperation *pop; + + /* TODO: Should avoid putting this here. Instead, last position should be requested + * from stroke system. */ + + if (!(pop = texture_paint_init(C, op, mouse))) { + return false; + } + + paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop); + + return true; +} + +static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + + op->customdata = paint_stroke_new(C, + op, + nullptr, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + event->type); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata)); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); + + return OPERATOR_RUNNING_MODAL; +} + +static int paint_exec(bContext *C, wmOperator *op) +{ + PropertyRNA *strokeprop; + PointerRNA firstpoint; + float mouse[2]; + + strokeprop = RNA_struct_find_property(op->ptr, "stroke"); + + if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { + return OPERATOR_CANCELLED; + } + + RNA_float_get_array(&firstpoint, "mouse", mouse); + + op->customdata = paint_stroke_new(C, + op, + nullptr, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + 0); + /* frees op->customdata */ + return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata)); +} + +static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata)); +} + +static void paint_cancel(bContext *C, wmOperator *op) +{ + paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata)); +} +} // namespace blender::ed::sculpt_paint::image::ops::paint + +extern "C" { +void PAINT_OT_image_paint(wmOperatorType *ot) +{ + using namespace blender::ed::sculpt_paint::image::ops::paint; + + /* identifiers */ + ot->name = "Image Paint"; + ot->idname = "PAINT_OT_image_paint"; + ot->description = "Paint a stroke into the image"; + + /* api callbacks */ + ot->invoke = paint_invoke; + ot->modal = paint_modal; + ot->exec = paint_exec; + ot->poll = image_paint_poll; + ot->cancel = paint_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; + + paint_stroke_operator_properties(ot); +} +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f1e79b98b83..82fdf49c28e 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -49,6 +49,7 @@ typedef struct CoNo { typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, + struct wmOperator *op, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); @@ -62,7 +63,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C, StrokeRedraw redraw, StrokeDone done, int event_type); -void paint_stroke_free(struct bContext *C, struct wmOperator *op); +void paint_stroke_free(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); /** * Returns zero if the stroke dots should not be spaced, non-zero otherwise. @@ -84,9 +85,12 @@ bool paint_supports_jitter(enum ePaintMode mode); * Called in paint_ops.c, on each regeneration of key-maps. */ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf); -int paint_stroke_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); -int paint_stroke_exec(struct bContext *C, struct wmOperator *op); -void paint_stroke_cancel(struct bContext *C, struct wmOperator *op); +int paint_stroke_modal(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *event, + struct PaintStroke *stroke); +int paint_stroke_exec(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); +void paint_stroke_cancel(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); bool paint_stroke_flipped(struct PaintStroke *stroke); bool paint_stroke_inverted(struct PaintStroke *stroke); struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); @@ -265,6 +269,7 @@ void paint_brush_color_get(struct Scene *scene, bool paint_use_opacity_masking(struct Brush *brush); void paint_brush_init_tex(struct Brush *brush); void paint_brush_exit_tex(struct Brush *brush); +bool image_paint_poll(struct bContext *C); void PAINT_OT_grab_clone(struct wmOperatorType *ot); void PAINT_OT_sample_color(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 1b876235ad0..926a564184a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -39,6 +39,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "curves_sculpt_intern.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -758,6 +759,7 @@ static const ePaintMode brush_select_paint_modes[] = { PAINT_MODE_VERTEX_GPENCIL, PAINT_MODE_SCULPT_GPENCIL, PAINT_MODE_WEIGHT_GPENCIL, + PAINT_MODE_SCULPT_CURVES, }; static int brush_select_exec(bContext *C, wmOperator *op) @@ -1388,6 +1390,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap = paint_stroke_modal_keymap(keyconf); WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke"); + /* Curves Sculpt mode. */ + keymap = WM_keymap_ensure(keyconf, "Sculpt Curves", 0, 0); + keymap->poll = CURVES_SCULPT_mode_poll; + /* sculpt expand. */ sculpt_expand_modal_keymap(keyconf); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 9bbfb81e08e..ae7570d21a1 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -505,16 +505,13 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert) } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, - wmOperator *op, - const float mouse_in[2], - float pressure) +static void paint_brush_stroke_add_step( + bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; float mouse_out[2]; PointerRNA itemptr; @@ -614,7 +611,7 @@ static void paint_brush_stroke_add_step(bContext *C, RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt); - stroke->update_step(C, stroke, &itemptr); + stroke->update_step(C, op, stroke, &itemptr); /* don't record this for now, it takes up a lot of memory when doing long * strokes with small brush size, and operators have register disabled */ @@ -785,12 +782,12 @@ static float paint_space_stroke_spacing_variable(bContext *C, * towards the final mouse location. */ static int paint_space_stroke(bContext *C, wmOperator *op, + PaintStroke *stroke, const float final_mouse[2], float final_pressure) { const Scene *scene = CTX_data_scene(C); ARegion *region = CTX_wm_region(C); - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; Paint *paint = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); @@ -852,7 +849,7 @@ static int paint_space_stroke(bContext *C, spacing / no_pressure_spacing); stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, pressure); + paint_brush_stroke_add_step(C, op, stroke, mouse, pressure); length -= spacing; pressure = stroke->last_pressure; @@ -929,7 +926,7 @@ PaintStroke *paint_stroke_new(bContext *C, return stroke; } -void paint_stroke_free(bContext *C, wmOperator *op) +void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke) { RegionView3D *rv3d = CTX_wm_region_view3d(C); if (rv3d) { @@ -938,7 +935,6 @@ void paint_stroke_free(bContext *C, wmOperator *op) BKE_paint_set_overlay_override(0); - PaintStroke *stroke = op->customdata; if (stroke == NULL) { return; } @@ -961,12 +957,11 @@ void paint_stroke_free(bContext *C, wmOperator *op) BLI_freelistN(&stroke->line); - MEM_SAFE_FREE(op->customdata); + MEM_SAFE_FREE(stroke); } -static void stroke_done(bContext *C, wmOperator *op) +static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) { - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; /* reset rotation here to avoid doing so in cursor display */ @@ -988,7 +983,7 @@ static void stroke_done(bContext *C, wmOperator *op) } } - paint_stroke_free(C, op); + paint_stroke_free(C, op, stroke); } bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) @@ -1230,7 +1225,7 @@ static void paint_line_strokes_spacing(bContext *C, ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, 1.0); + paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0); length -= spacing; spacing_final = spacing; @@ -1252,8 +1247,8 @@ static void paint_stroke_line_end(bContext *C, if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); - paint_space_stroke(C, op, mouse, 1.0); + paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0); + paint_space_stroke(C, op, stroke, mouse, 1.0); } } @@ -1331,7 +1326,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); if (stroke->stroke_started) { - paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); + paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0); paint_line_strokes_spacing( C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); } @@ -1343,7 +1338,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str } } - stroke_done(C, op); + stroke_done(C, op, stroke); #ifdef DEBUG_TIME TIMEIT_END_AVERAGED(whole_stroke); @@ -1384,11 +1379,10 @@ static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) } } -int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke *stroke) { Paint *p = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); - PaintStroke *stroke = op->customdata; Brush *br = stroke->brush = BKE_paint_brush(p); PaintSample sample_average; float mouse[2]; @@ -1482,7 +1476,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) op->type->cancel(C, op); } else { - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, stroke); } return OPERATOR_CANCELLED; } @@ -1492,17 +1486,17 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); paint_stroke_line_end(C, op, stroke, mouse); - stroke_done(C, op); + stroke_done(C, op, stroke); return OPERATOR_FINISHED; } } else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) { paint_stroke_line_end(C, op, stroke, sample_average.mouse); - stroke_done(C, op); + stroke_done(C, op, stroke); return OPERATOR_FINISHED; } else if (br->flag & BRUSH_LINE) { - if (event->alt) { + if (event->modifier & KM_ALT) { stroke->constrain_line = true; } else { @@ -1530,7 +1524,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) { if (stroke->stroke_started) { if (paint_space_stroke_enabled(br, mode)) { - if (paint_space_stroke(C, op, mouse, pressure)) { + if (paint_space_stroke(C, op, stroke, mouse, pressure)) { redraw = true; } } @@ -1538,7 +1532,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) float dmouse[2]; sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); stroke->stroke_distance += len_v2(dmouse); - paint_brush_stroke_add_step(C, op, mouse, pressure); + paint_brush_stroke_add_step(C, op, stroke, mouse, pressure); redraw = true; } } @@ -1549,7 +1543,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) * instead of waiting till we have moved the space distance */ if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) { stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); + paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure); redraw = true; } @@ -1572,10 +1566,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -int paint_stroke_exec(bContext *C, wmOperator *op) +int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke) { - PaintStroke *stroke = op->customdata; - /* only when executed for the first time */ if (stroke->stroke_started == 0) { PropertyRNA *strokeprop; @@ -1592,21 +1584,21 @@ int paint_stroke_exec(bContext *C, wmOperator *op) if (stroke->stroke_started) { RNA_BEGIN (op->ptr, itemptr, "stroke") { - stroke->update_step(C, stroke, &itemptr); + stroke->update_step(C, op, stroke, &itemptr); } RNA_END; } bool ok = (stroke->stroke_started != 0); - stroke_done(C, op); + stroke_done(C, op, stroke); return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -void paint_stroke_cancel(bContext *C, wmOperator *op) +void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke) { - stroke_done(C, op); + stroke_done(C, op, stroke); } ViewContext *paint_stroke_view_context(PaintStroke *stroke) diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index f54b012b8dd..31b965c6a92 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -130,13 +130,12 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3], flo { Object *ob = vc->obact; float delta[3], scale, loc[3]; - const float mval_f[2] = {pixel_radius, 0.0f}; - float zfac; + const float xy_delta[2] = {pixel_radius, 0.0f}; mul_v3_m4v3(loc, ob->obmat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); - ED_view3d_win_to_delta(vc->region, mval_f, delta, zfac); + const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); + ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta); scale = fabsf(mat4_to_scale(ob->obmat)); scale = (scale == 0.0f) ? 1.0f : scale; @@ -585,7 +584,8 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) ot->poll = brush_curve_preset_poll; prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } /* face-select ops */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index a82636023f8..e2f8d81fe13 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2370,7 +2370,10 @@ static void wpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) +static void wpaint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); @@ -2551,7 +2554,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } /* add modal handler */ @@ -2575,7 +2578,7 @@ static int wpaint_exec(bContext *C, wmOperator *op) 0); /* frees op->customdata */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -2588,7 +2591,12 @@ static void wpaint_cancel(bContext *C, wmOperator *op) ob->sculpt->cache = NULL; } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); +} + +static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); } void PAINT_OT_weight_paint(wmOperatorType *ot) @@ -2600,7 +2608,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) /* api callbacks */ ot->invoke = wpaint_invoke; - ot->modal = paint_stroke_modal; + ot->modal = wpaint_modal; ot->exec = wpaint_exec; ot->poll = weight_paint_poll; ot->cancel = wpaint_cancel; @@ -3395,7 +3403,10 @@ static void vpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) +static void vpaint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); @@ -3497,7 +3508,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -3522,7 +3533,7 @@ static int vpaint_exec(bContext *C, wmOperator *op) 0); /* frees op->customdata */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -3535,7 +3546,12 @@ static void vpaint_cancel(bContext *C, wmOperator *op) ob->sculpt->cache = NULL; } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); +} + +static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); } void PAINT_OT_vertex_paint(wmOperatorType *ot) @@ -3547,7 +3563,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* api callbacks */ ot->invoke = vpaint_invoke; - ot->modal = paint_stroke_modal; + ot->modal = vpaint_modal; ot->exec = vpaint_exec; ot->poll = vertex_paint_poll; ot->cancel = vpaint_cancel; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 68edf9cd54a..70ff7596d6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2648,13 +2648,13 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int t static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) { Object *ob = vc->obact; - float loc[3], mval_f[2] = {0.0f, 1.0f}; - float zfac; + float loc[3]; + const float xy_delta[2] = {0.0f, 1.0f}; mul_v3_m4v3(loc, ob->imat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); + const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); - ED_view3d_win_to_delta(vc->region, mval_f, y, zfac); + ED_view3d_win_to_delta(vc->region, xy_delta, zfac, y); normalize_v3(y); add_v3_v3(y, ob->loc); @@ -5186,6 +5186,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f } static void sculpt_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), struct PaintStroke *UNUSED(stroke), PointerRNA *itemptr) { @@ -5333,12 +5334,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_PASS_THROUGH; } if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } /* Add modal handler. */ @@ -5364,7 +5365,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) 0); /* Frees op->customdata. */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -5382,7 +5383,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) paint_mesh_restore_co(sd, ob); } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); if (ss->cache) { SCULPT_cache_free(ss->cache); @@ -5392,6 +5393,11 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) sculpt_brush_exit_tex(sd); } +static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); +} + void SCULPT_OT_brush_stroke(wmOperatorType *ot) { /* Identifiers. */ @@ -5401,7 +5407,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) /* API callbacks. */ ot->invoke = sculpt_brush_stroke_invoke; - ot->modal = paint_stroke_modal; + ot->modal = sculpt_brush_stroke_modal; ot->exec = sculpt_brush_stroke_exec; ot->poll = SCULPT_poll; ot->cancel = sculpt_brush_stroke_cancel; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index a6b412b2b7e..6f9df4d8252 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1105,10 +1105,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v); bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v); /** - * Initialize a point-in-brush test with a given falloff shape + * Initialize a point-in-brush test with a given falloff shape. * - * \param falloff_shape PAINT_FALLOFF_SHAPE_SPHERE or PAINT_FALLOFF_SHAPE_TUBE - * \return The brush falloff function + * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE. + * \return The brush falloff function. */ SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, SculptBrushTest *test, diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 72640893dea..8fc10061f83 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -245,7 +245,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * /* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill * mask and face sets by connectivity directly. */ - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1; } diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index e383ada1331..8e8902c2ea7 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -659,7 +659,7 @@ static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *even { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c). */ - RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT); return action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 897091731a4..d53fe2efb03 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -29,6 +29,7 @@ #include "ED_clip.h" #include "ED_curve.h" #include "ED_curves.h" +#include "ED_curves_sculpt.h" #include "ED_fileselect.h" #include "ED_geometry.h" #include "ED_gizmo_library.h" @@ -96,6 +97,7 @@ void ED_spacetypes_init(void) ED_operatortypes_mesh(); ED_operatortypes_geometry(); ED_operatortypes_sculpt(); + ED_operatortypes_sculpt_curves(); ED_operatortypes_uvedit(); ED_operatortypes_paint(); ED_operatortypes_physics(); diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 287aef178ae..4e80e7ea5c2 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -230,7 +230,7 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && - (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { + (type == -1 || ELEM(type, OB_CURVES_LEGACY, OB_SURF, OB_FONT))) { return true; } if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (ELEM(type, -1, OB_ARMATURE))) { @@ -293,7 +293,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path) if (ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE, diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index cad6d34af24..e215b7c7992 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -282,11 +282,11 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Useful yet irritating feature, Shift+Click to open the file * Alt+Click to browse a folder in the OS's browser. */ - if (event->shift || event->alt) { + if (event->modifier & (KM_SHIFT | KM_ALT)) { wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true); PointerRNA props_ptr; - if (event->alt) { + if (event->modifier & KM_ALT) { char *lslash = (char *)BLI_path_slash_rfind(str); if (lslash) { *lslash = '\0'; diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index ba0b3a59e24..a24eae6a0ce 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -408,7 +408,7 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve * (when input method are used for utf8 inputs, the user may assign key event * including alt/ctrl/super like ctrl+m to commit utf8 string. in such case, * the modifiers in the utf8 character event make no sense.) */ - if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) { + if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) { return OPERATOR_PASS_THROUGH; } @@ -456,7 +456,18 @@ void CONSOLE_OT_insert(wmOperatorType *ot) static int console_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(op)) { ConsoleLine *ci = console_history_verify(C); - bool text_before_cursor = ci->cursor != 0 && !ELEM(ci->line[ci->cursor - 1], ' ', '\t'); + bool text_before_cursor = false; + + /* Check any text before cursor (not just the previous character) as is done for + * #TEXT_OT_indent_or_autocomplete because Python auto-complete operates on import + * statements such as completing possible sub-modules: `from bpy import `. */ + for (int i = 0; i < ci->cursor; i += BLI_str_utf8_size_safe(&ci->line[i])) { + if (!ELEM(ci->line[i], ' ', '\t')) { + text_before_cursor = true; + break; + } + } + if (text_before_cursor) { WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL); } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 3b3d968aed6..daa4b53803f 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2245,7 +2245,7 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const cache->misc_entries_indices[cache->misc_cursor] = index; cache->misc_cursor = (cache->misc_cursor + 1) % cache_size; -#if 0 /* Actually no, only block cached entries should have preview imho. */ +#if 0 /* Actually no, only block cached entries should have preview IMHO. */ if (cache->previews_pool) { filelist_cache_previews_push(filelist, ret, index); } diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 1006ba4b3f4..1a3355b0139 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -163,6 +163,18 @@ static void reset_bezts(tGraphSliderOp *gso) ANIM_animdata_freelist(&anim_data); } +/** + * Get factor value and store it in RNA property. + * Custom data of #wmOperator needs to contain #tGraphSliderOp. + */ +static float slider_factor_get_and_remember(wmOperator *op) +{ + tGraphSliderOp *gso = op->customdata; + const float factor = ED_slider_factor_get(gso->slider); + RNA_property_float_set(op->ptr, gso->factor_prop, factor); + return factor; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -378,8 +390,7 @@ static void decimate_modal_update(bContext *C, wmOperator *op) reset_bezts(gso); /* Apply... */ - float factor = ED_slider_factor_get(gso->slider); - RNA_property_float_set(op->ptr, gso->factor_prop, factor); + const float factor = slider_factor_get_and_remember(op); /* We don't want to limit the decimation to a certain error margin. */ const float error_sq_max = FLT_MAX; decimate_graph_keys(&gso->ac, factor, error_sq_max); @@ -598,8 +609,7 @@ static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op) /* Reset keyframe data to the state at invoke. */ reset_bezts(gso); - const float factor = ED_slider_factor_get(gso->slider); - RNA_property_float_set(op->ptr, gso->factor_prop, factor); + const float factor = slider_factor_get_and_remember(op); blend_to_neighbor_graph_keys(&gso->ac, factor); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -725,7 +735,8 @@ static void breakdown_modal_update(bContext *C, wmOperator *op) /* Reset keyframe data to the state at invoke. */ reset_bezts(gso); - breakdown_graph_keys(&gso->ac, ED_slider_factor_get(gso->slider)); + const float factor = slider_factor_get_and_remember(op); + breakdown_graph_keys(&gso->ac, factor); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } @@ -739,6 +750,7 @@ static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) tGraphSliderOp *gso = op->customdata; gso->modal_update = breakdown_modal_update; + gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); breakdown_draw_status_header(C, gso); return invoke_result; diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c index 4351186dc6f..9f934e47ebb 100644 --- a/source/blender/editors/space_graph/graph_utils.c +++ b/source/blender/editors/space_graph/graph_utils.c @@ -185,6 +185,7 @@ bool graphop_editable_keyframes_poll(bContext *C) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { + CTX_wm_operator_poll_msg_set(C, "There is no animation data to operate on"); return found; } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 27ac34ba346..22427675ff3 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -242,7 +242,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) GPU_blend(GPU_BLEND_NONE); - /* Vertical component of of the cursor. */ + /* Vertical component of the cursor. */ if (sipo->mode == SIPO_MODE_DRIVERS) { /* cursor x-value */ float x = sipo->cursorTime; diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index da899ef4c9a..6f0d5c2dbe9 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -152,7 +152,7 @@ static void stats_object(Object *ob, } break; case OB_SURF: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); if ((me_eval != nullptr) && !BLI_gset_add(objects_gset, (void *)me_eval)) { @@ -260,7 +260,7 @@ static void stats_object_edit(Object *obedit, SceneStats *stats) stats->totvert += 2; } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { /* OB_FONT has no cu->editnurb */ + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { /* OB_FONT has no cu->editnurb */ /* Curve Edit */ Curve *cu = static_cast<Curve *>(obedit->data); BezTriple *bezt; diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 4a507aa3bf2..8b059b33a9a 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -583,7 +583,7 @@ static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent * { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c) */ - RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT); return nla_action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index eda9f89b51c..d8a0fde6d07 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -110,7 +110,7 @@ static void nla_action_draw_keyframes( */ Range2f frame_range; - ED_keylist_frame_range(keylist, &frame_range); + ED_keylist_all_keys_frame_range(keylist, &frame_range); immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2); immUnbindProgram(); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 28ac7a34fa8..afb205f9f9e 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -147,7 +147,7 @@ static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerR } // namespace blender::ed::space_node #define SAMPLE_FLT_ISNONE FLT_MAX -/* Bad bad, 2.5 will do better? ... no it won't! */ +/* Bad! 2.5 will do better? ... no it won't! */ static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; void ED_node_sample_set(const float col[4]) { @@ -1967,9 +1967,10 @@ void node_draw_link_bezier(const bContext &C, const bNodeLink &link, const int th_col1, const int th_col2, - const int th_col3) + const int th_col3, + const bool selected) { - const float dim_factor = node_link_dim_factor(v2d, link); + const float dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link); float thickness = 1.5f; float dash_factor = 1.0f; @@ -2025,19 +2026,17 @@ void node_draw_link_bezier(const bContext &C, } /* Highlight links connected to selected nodes. */ - const bool is_fromnode_selected = link.fromnode && link.fromnode->flag & SELECT; - const bool is_tonode_selected = link.tonode && link.tonode->flag & SELECT; - if (is_fromnode_selected || is_tonode_selected) { + if (selected) { float color_selected[4]; UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected); const float alpha = color_selected[3]; /* Interpolate color if highlight color is not fully transparent. */ if (alpha != 0.0) { - if (is_fromnode_selected) { + if (link.fromsock) { interp_v3_v3v3(colors[1], colors[1], color_selected, alpha); } - if (is_tonode_selected) { + if (link.tosock) { interp_v3_v3v3(colors[2], colors[2], color_selected, alpha); } } @@ -2102,7 +2101,8 @@ void node_draw_link_bezier(const bContext &C, void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, - const bNodeLink &link) + const bNodeLink &link, + const bool selected) { int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; @@ -2146,7 +2146,7 @@ void node_draw_link(const bContext &C, } } - node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3); + node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected); } } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index a76988a60d0..349fa92d06d 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -47,7 +47,7 @@ static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Obje Light *light = (Light *)object.data; ui::context_path_add_generic(path, RNA_Light, light); } - if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) { + if (ELEM(object.type, OB_CURVES_LEGACY, OB_FONT, OB_SURF) && object.data) { Curve *curve = (Curve *)object.data; ui::context_path_add_generic(path, RNA_Curve, curve); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 455eceed293..1286f6a818c 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -660,7 +660,7 @@ static void node_draw_mute_line(const bContext &C, GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node.internal_links) { - node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); + node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false); } GPU_blend(GPU_BLEND_NONE); @@ -718,7 +718,12 @@ static void node_socket_draw_multi_input(const float color[4], const int locx, const int locy) { - const float outline_width = 1.0f; + /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness + * that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac` + * has the the same effect here. It scales the outline correctly across different screen DPIs + * and UI scales without being affected by the 'line-width'. */ + const float outline_width = NODE_SOCK_OUTLINE_SCALE * U.dpi_fac; + /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */ const rctf rect = { locx - width + outline_width * 0.5f, @@ -1049,7 +1054,7 @@ static void node_socket_draw_nested(const bContext &C, }, data, MEM_freeN); - /* Disable the button so that clicks on it are ignored the the link operator still works. */ + /* Disable the button so that clicks on it are ignored the link operator still works. */ UI_but_flag_enable(but, UI_BUT_DISABLED); UI_block_emboss_set(&block, old_emboss); } @@ -1060,7 +1065,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ { using namespace blender::ed::space_node; - const float size = 2.25f * NODE_SOCKSIZE * scale; + const float size = NODE_SOCKSIZE_DRAW_MULIPLIER * NODE_SOCKSIZE * scale; rcti draw_rect = *rect; float outline_color[4] = {0}; @@ -1081,7 +1086,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 1.0f); + immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Single point. */ @@ -1232,13 +1237,14 @@ static void node_draw_sockets(const View2D &v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 1.0f); + immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Set handle size. */ + const float socket_draw_size = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER; float scale; UI_view2d_scale_get(&v2d, &scale, nullptr); - scale *= 2.25f * NODE_SOCKSIZE; + scale *= socket_draw_size; if (!select_all) { immBeginAtMost(GPU_PRIM_POINTS, total_input_len + total_output_len); @@ -1251,7 +1257,10 @@ static void node_draw_sockets(const View2D &v2d, continue; } if (select_all || (sock->flag & SELECT)) { - selected_input_len++; + if (!(sock->flag & SOCK_MULTI_INPUT)) { + /* Don't add multi-input sockets here since they are drawn in a different batch. */ + selected_input_len++; + } continue; } /* Don't draw multi-input sockets here since they are drawn in a different batch. */ @@ -1318,6 +1327,10 @@ static void node_draw_sockets(const View2D &v2d, if (nodeSocketIsHidden(sock)) { continue; } + /* Don't draw multi-input sockets here since they are drawn in a different batch. */ + if (sock->flag & SOCK_MULTI_INPUT) { + continue; + } if (select_all || (sock->flag & SELECT)) { node_socket_draw_nested(C, ntree, @@ -1383,13 +1396,13 @@ static void node_draw_sockets(const View2D &v2d, } const bool is_node_hidden = (node.flag & NODE_HIDDEN); - const float width = NODE_SOCKSIZE; + const float width = 0.5f * socket_draw_size; float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width; float color[4]; float outline_color[4]; node_socket_color_get(C, ntree, node_ptr, *socket, color); - node_socket_outline_color_get(selected, socket->type, outline_color); + node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color); node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); } @@ -2650,10 +2663,18 @@ static void node_draw_nodetree(const bContext &C, nodelink_batch_start(snode); LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (!nodeLinkIsHidden(link)) { - node_draw_link(C, region.v2d, snode, *link); + if (!nodeLinkIsHidden(link) && !nodeLinkIsSelected(link)) { + node_draw_link(C, region.v2d, snode, *link, false); } } + + /* Draw selected node links after the unselected ones, so they are shown on top. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (!nodeLinkIsHidden(link) && nodeLinkIsSelected(link)) { + node_draw_link(C, region.v2d, snode, *link, true); + } + } + nodelink_batch_end(snode); GPU_blend(GPU_BLEND_NONE); @@ -2838,7 +2859,7 @@ void node_draw_space(const bContext &C, ARegion ®ion) GPU_line_smooth(true); if (snode.runtime->linkdrag) { for (const bNodeLink *link : snode.runtime->linkdrag->links) { - node_draw_link(C, v2d, snode, *link); + node_draw_link(C, v2d, snode, *link, true); } } GPU_line_smooth(false); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 7b7aaef518b..b30be6ae0af 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -89,7 +89,7 @@ struct CompoJob { float node_socket_calculate_height(const bNodeSocket &socket) { - float sock_height = NODE_SOCKSIZE * 2.0f; + float sock_height = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER; if (socket.flag & SOCK_MULTI_INPUT) { sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket.total_inputs, NODE_SOCKSIZE); } @@ -1160,12 +1160,16 @@ bool node_find_indicated_socket(SpaceNode &snode, { rctf rect; + const float size_sock_padded = NODE_SOCKSIZE + 4; + *nodep = nullptr; *sockp = nullptr; /* check if we click in a socket */ LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) { - BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4); + BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded); + rctf node_visible; + BLI_rctf_init_pt_radius(&node_visible, cursor, size_sock_padded); if (!(node->flag & NODE_HIDDEN)) { /* extra padding inside and out - allow dragging on the text areas too */ @@ -1184,7 +1188,7 @@ bool node_find_indicated_socket(SpaceNode &snode, if (!nodeSocketIsHidden(sock)) { if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) { if (cursor_isect_multi_input_socket(cursor, *sock)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; @@ -1192,7 +1196,7 @@ bool node_find_indicated_socket(SpaceNode &snode, } } else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; @@ -1205,7 +1209,7 @@ bool node_find_indicated_socket(SpaceNode &snode, LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (!nodeSocketIsHidden(sock)) { if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 592db3f7877..319f97e57f5 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -104,6 +104,8 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); #define NODE_HEIGHT(node) (node.height * UI_DPI_FAC) #define NODE_MARGIN_X (1.2f * U.widget_unit) #define NODE_SOCKSIZE (0.25f * U.widget_unit) +#define NODE_SOCKSIZE_DRAW_MULIPLIER 2.25f +#define NODE_SOCK_OUTLINE_SCALE 1.0f #define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) #define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) #define NODE_LINK_RESOL 12 @@ -196,7 +198,8 @@ void nodelink_batch_end(SpaceNode &snode); void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, - const bNodeLink &link); + const bNodeLink &link, + bool selected); /** * Don't do shadows if th_col3 is -1. */ @@ -206,7 +209,8 @@ void node_draw_link_bezier(const bContext &C, const bNodeLink &link, int th_col1, int th_col2, - int th_col3); + int th_col3, + bool selected); /** If v2d not nullptr, it clips and returns 0 if not visible. */ bool node_link_bezier_points(const View2D *v2d, const SpaceNode *snode, diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 1cfa932356b..299e7e5658c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2436,16 +2436,18 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); - /* Ignore main sockets when the types don't match. */ - if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), - static_cast<eNodeSocketDatatype>(best_input->type))) { - best_input = nullptr; - } - if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), - static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { - best_output = nullptr; + if (node_to_insert->type != NODE_REROUTE) { + /* Ignore main sockets when the types don't match. */ + if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), + static_cast<eNodeSocketDatatype>(best_input->type))) { + best_input = nullptr; + } + if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), + static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { + best_output = nullptr; + } } bNode *from_node = old_link->fromnode; diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 765661aa9d5..f38f6c2855d 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -1071,7 +1071,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) static int collection_isolate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "extend"); - if (!RNA_property_is_set(op->ptr, prop) && (event->shift)) { + if (!RNA_property_is_set(op->ptr, prop) && (event->modifier & KM_SHIFT)) { RNA_property_boolean_set(op->ptr, prop, true); } return collection_isolate_exec(C, op); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 0d8ee76d2f0..edd2e5f304f 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -319,7 +319,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) } if (!allow_parenting_without_modifier_key(space_outliner)) { - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { return false; } } @@ -417,8 +417,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); - parent_drop_set_parents( - C, op->reports, reinterpret_cast<wmDragID *>(drag->ids.first), par, PAR_OBJECT, event->alt); + parent_drop_set_parents(C, + op->reports, + reinterpret_cast<wmDragID *>(drag->ids.first), + par, + PAR_OBJECT, + event->modifier & KM_ALT); return OPERATOR_FINISHED; } @@ -446,7 +450,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); if (!allow_parenting_without_modifier_key(space_outliner)) { - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { return false; } } @@ -471,7 +475,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) case ID_OB: return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE); case ID_GR: - return event->shift || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE); + return (event->modifier & KM_SHIFT) || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE); default: return true; } @@ -496,7 +500,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven if (GS(drag_id->id->name) == ID_OB) { Object *object = (Object *)drag_id->id; - ED_object_parent_clear(object, event->alt ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL); + ED_object_parent_clear( + object, (event->modifier & KM_ALT) ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL); } } @@ -1166,10 +1171,11 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { + if (((event->modifier & KM_SHIFT) == 0) && + collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { TreeElement *te = data.te; TreeStoreElem *tselem = TREESTORE(te); - if (!data.from || event->ctrl) { + if (!data.from || event->modifier & KM_CTRL) { tselem->flag |= TSE_DRAG_INTO; changed = true; } @@ -1210,9 +1216,10 @@ static char *collection_drop_tooltip(bContext *C, const wmEvent *event = win ? win->eventstate : nullptr; CollectionDrop data; - if (event && !event->shift && collection_drop_init(C, drag, xy, event->ctrl, &data)) { + if (event && ((event->modifier & KM_SHIFT) == 0) && + collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) { TreeElement *te = data.te; - if (!data.from || event->ctrl) { + if (!data.from || event->modifier & KM_CTRL) { return BLI_strdup(TIP_("Link inside Collection")); } switch (data.insert_type) { @@ -1263,7 +1270,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); CollectionDrop data; - if (!collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { + if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { return OPERATOR_CANCELLED; } @@ -1291,7 +1298,9 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { /* Ctrl enables linking, so we don't need a from collection then. */ - Collection *from = (event->ctrl) ? nullptr : collection_parent_from_ID(drag_id->from_parent); + Collection *from = (event->modifier & KM_CTRL) ? + nullptr : + collection_parent_from_ID(drag_id->from_parent); if (GS(drag_id->id->name) == ID_OB) { /* Move/link object into collection. */ diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index dd7ca128282..2da416c8671 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -166,7 +166,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU { Bone *bone = (Bone *)poin; - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0); } } @@ -178,7 +178,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0); } @@ -194,7 +194,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *poin, void *poin2) ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_ebone( arm, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0); } @@ -210,7 +210,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *poin, void *po ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_ebone(arm, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0); } @@ -250,7 +250,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); PointerRNA ptr; - bool extend = (win->eventstate->shift != 0); + bool extend = (win->eventstate->modifier & KM_SHIFT); if (!extend) { return; @@ -571,8 +571,8 @@ static void outliner_collection_set_flag_recursive_fn(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); PointerRNA ptr; - bool do_isolate = (win->eventstate->ctrl != 0); - bool extend = (win->eventstate->shift != 0); + bool do_isolate = (win->eventstate->modifier & KM_CTRL); + bool extend = (win->eventstate->modifier & KM_SHIFT); if (!ELEM(true, do_isolate, extend)) { return; @@ -2043,7 +2043,7 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED const bool object_data_shared = (ob->data == tvc.obact->data); wmWindow *win = CTX_wm_window(C); - const bool do_extend = win->eventstate->ctrl != 0 && !object_data_shared; + const bool do_extend = (win->eventstate->modifier & KM_CTRL) && !object_data_shared; outliner_item_mode_toggle(C, &tvc, te, do_extend); } @@ -2592,7 +2592,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case OB_CAMERA: data.icon = ICON_OUTLINER_OB_CAMERA; break; - case OB_CURVE: + case OB_CURVES_LEGACY: data.icon = ICON_OUTLINER_OB_CURVE; break; case OB_MBALL: @@ -2655,7 +2655,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case ID_ME: data.icon = ICON_OUTLINER_DATA_MESH; break; - case ID_CU: + case ID_CU_LEGACY: data.icon = ICON_OUTLINER_DATA_CURVE; break; case ID_MB: diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index a6ac2a5a1f3..6916f5fe502 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -240,7 +240,7 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); /* Only toggle once for single click toggling */ - if (event->type == LEFTMOUSE) { + if ((event->type == LEFTMOUSE) && (event->val != KM_CLICK_DRAG)) { return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index bba4bac0e37..0516758e887 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -108,7 +108,7 @@ typedef struct TreeElementIcon { ID_LI, \ ID_OB, \ ID_ME, \ - ID_CU, \ + ID_CU_LEGACY, \ ID_MB, \ ID_NT, \ ID_MA, \ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index df10ce002c3..c6b9d9577b5 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -1171,7 +1171,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE context = BCONTEXT_OBJECT; break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_IM: case ID_LT: diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 18c37e08eff..8fcf967bce8 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -117,7 +117,7 @@ static void get_element_operation_type( break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_LT: case ID_LA: @@ -236,7 +236,7 @@ static void unlink_material_fn(bContext *UNUSED(C), totcol = me->totcol; matar = me->mat; } - else if (GS(tsep->id->name) == ID_CU) { + else if (GS(tsep->id->name) == ID_CU_LEGACY) { Curve *cu = (Curve *)tsep->id; totcol = cu->totcol; matar = cu->mat; @@ -766,29 +766,37 @@ static void id_override_library_create_fn(bContext *C, void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id_root = tselem->id; + + /* We can only safely apply this operation on one item at a time, so only do it on the active + * one. */ + if ((tselem->flag & TSE_ACTIVE) == 0) { + return; + } + + ID *id_root_reference = tselem->id; OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; bool success = false; - ID *id_reference = nullptr; + ID *id_instance_hint = nullptr; bool is_override_instancing_object = false; if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr && GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id)) { Object *ob = (Object *)tsep->id; - if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root) { - BLI_assert(GS(id_root->name) == ID_GR); + if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) { + BLI_assert(GS(id_root_reference->name) == ID_GR); /* Empty instantiating the collection we override, we need to pass it to BKE overriding code * for proper handling. */ - id_reference = tsep->id; + id_instance_hint = tsep->id; is_override_instancing_object = true; } } - if (ID_IS_OVERRIDABLE_LIBRARY(id_root) || (ID_IS_LINKED(id_root) && do_hierarchy)) { + if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) || + (ID_IS_LINKED(id_root_reference) && do_hierarchy)) { Main *bmain = CTX_data_main(C); - id_root->tag |= LIB_TAG_DOIT; + id_root_reference->tag |= LIB_TAG_DOIT; /* For now, remap all local usages of linked ID to local override one here. */ ID *id_iter; @@ -804,38 +812,100 @@ static void id_override_library_create_fn(bContext *C, if (do_hierarchy) { /* Tag all linked parents in tree hierarchy to be also overridden. */ + ID *id_hierarchy_root_reference = id_root_reference; while ((te = te->parent) != nullptr) { if (!TSE_IS_REAL_ID(te->store_elem)) { continue; } - if (!ID_IS_LINKED(te->store_elem->id)) { + + /* Tentative hierarchy root. */ + ID *id_current_hierarchy_root = te->store_elem->id; + + /* If the parent ID is from a different library than the reference root one, we are done + * with upwards tree processing in any case. */ + if (id_current_hierarchy_root->lib != id_root_reference->lib) { + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { + /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to + * get an actual real override. */ + continue; + } + + /* If the parent ID is already an override, and is valid (i.e. local override), we can + * access its hierarchy root directly. */ + if (!ID_IS_LINKED(id_current_hierarchy_root) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && + id_current_hierarchy_root->override_library->reference->lib == + id_root_reference->lib) { + id_hierarchy_root_reference = + id_current_hierarchy_root->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + break; + } + + if (ID_IS_LINKED(id_current_hierarchy_root)) { + /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this + * would most likely generate invisible/confusing/hard to use and manage overrides. */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid anchor ('%s') found, needed to create library override from " + "data-block '%s'", + id_current_hierarchy_root->name, + id_root_reference->name); + return; + } + + /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so + * current `id_hierarchy_root_reference` is our best candidate. */ + break; } + /* If some element in the tree needs to be overridden, but its ID is not overridable, * abort. */ - if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(te->store_elem->id)) { + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); BKE_reportf(reports, RPT_WARNING, "Could not create library override from data-block '%s', one of its parents " "is not overridable ('%s')", - id_root->name, - te->store_elem->id->name); + id_root_reference->name, + id_current_hierarchy_root->name); return; } - te->store_elem->id->tag |= LIB_TAG_DOIT; + id_current_hierarchy_root->tag |= LIB_TAG_DOIT; + id_hierarchy_root_reference = id_current_hierarchy_root; + } + + /* That case can happen when linked data is a complex mix involving several libraries and/or + * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data + * from another library. Do not try to support such cases for now. */ + if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || + (!ID_IS_LINKED(id_hierarchy_root_reference) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && + id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib))) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid hierarchy root ('%s') found, needed to create library override from " + "data-block '%s'", + id_hierarchy_root_reference->name, + id_root_reference->name); + return; } success = BKE_lib_override_library_create(bmain, CTX_data_scene(C), CTX_data_view_layer(C), nullptr, - id_root, - id_reference, + id_root_reference, + id_hierarchy_root_reference, + id_instance_hint, nullptr); } - else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { - success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != nullptr; + else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { + success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr; /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -845,14 +915,14 @@ static void id_override_library_create_fn(bContext *C, /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, (Object *)id_reference); + ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint); } } if (!success) { BKE_reportf(reports, RPT_WARNING, "Could not create library override from data-block '%s'", - id_root->name); + id_root_reference->name); } } @@ -1770,13 +1840,15 @@ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", 0, - "Make Library Override", - "Make a local override of this linked data-block"}, + "Make Library Override Single", + "Make a single, out-of-hierarchy local override of this linked data-block - only applies to " + "active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, "OVERRIDE_LIBRARY_CREATE_HIERARCHY", 0, "Make Library Override Hierarchy", - "Make a local override of this linked data-block, and its hierarchy of dependencies"}, + "Make a local override of this linked data-block, and its hierarchy of dependencies - only " + "applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, "OVERRIDE_LIBRARY_RESET", 0, diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 1605d5874ae..06a5918f25c 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -579,7 +579,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, * would require going over all tfaces, sort images in use. etc... */ break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; if (outliner_animdata_test(cu->adt)) { diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 6b68f1ee4a4..f9141dffd6a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -7,6 +7,8 @@ #include "DNA_listBase.h" #include "DNA_space_types.h" +#include "BLI_utildefines.h" + #include "tree_display.hh" using namespace blender::ed::outliner; @@ -30,11 +32,11 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode( case SO_OVERRIDES_LIBRARY: return std::make_unique<TreeDisplayOverrideLibrary>(space_outliner); case SO_VIEW_LAYER: - /* FIXME(Julian): this should not be the default! Return nullptr and handle that as valid - * case. */ - default: return std::make_unique<TreeDisplayViewLayer>(space_outliner); } + + BLI_assert_unreachable(); + return nullptr; } bool AbstractTreeDisplay::hasWarnings() const diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index 43d67ee106d..f94727ba356 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -23,11 +23,6 @@ namespace blender::ed::outliner { /* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ template<typename T> using List = ListBaseWrapper<T>; TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner) diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 996f51eee82..2fbc86705b9 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -89,8 +89,8 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner /** * Get actual warning data of a tree element, if any. * - * \param r_icon The icon to display as warning. - * \param r_message The message to display as warning. + * \param r_icon: The icon to display as warning. + * \param r_message: The message to display as warning. * \return true if there is a warning, false otherwise. */ bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message); diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index e126b024d52..64c73f57107 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -34,7 +34,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id); case ID_OB: case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_MA: case ID_TE: diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 8c12193fb88..5ac4363e63d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1636,7 +1636,9 @@ static void sequencer_draw_gpencil_overlay(const bContext *C) ED_annotation_draw_view2d(C, 0); } -/* Draw content and safety borders borders. */ +/** + * Draw content and safety borders. + */ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene) @@ -1926,7 +1928,6 @@ static void sequencer_draw_display_buffer(const bContext *C, if (!glsl_used) { immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); immUniformColor3f(1.0f, 1.0f, 1.0f); - immUniform1i("image", 0); } immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 3be890bfcc5..fbdc451cf06 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -264,7 +264,7 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, return nullptr; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE, OB_FONT)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVES_LEGACY, OB_FONT)) { return nullptr; } diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c index 496f500ef04..55873740491 100644 --- a/source/blender/editors/space_text/text_autocomplete.c +++ b/source/blender/editors/space_text/text_autocomplete.c @@ -408,7 +408,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_BACKSPACEKEY: if (event->val == KM_PRESS) { if (tools & TOOL_SUGG_LIST) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { texttool_suggest_clear(); retval = OPERATOR_CANCELLED; draw = 1; @@ -445,7 +445,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_RIGHTARROWKEY: if (event->val == KM_PRESS) { if (tools & TOOL_SUGG_LIST) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { texttool_suggest_clear(); retval = OPERATOR_CANCELLED; draw = 1; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index ddba6803f61..3c29b32c2fa 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3495,7 +3495,7 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) * (when input method are used for utf8 inputs, the user may assign key event * including alt/ctrl/super like ctrl+m to commit utf8 string. in such case, * the modifiers in the utf8 character event make no sense.) */ - if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) { + if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) { return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 7addda0f8c3..4656540c19b 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -413,6 +413,9 @@ static void view3d_main_region_init(wmWindowManager *wm, ARegion *region) keymap = WM_keymap_ensure(wm->defaultconf, "Particle", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); + keymap = WM_keymap_ensure(wm->defaultconf, "Sculpt Curves", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + /* editfont keymap swallows all... */ keymap = WM_keymap_ensure(wm->defaultconf, "Font", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); @@ -1475,6 +1478,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_EDIT_CURVE: ARRAY_SET_ITEMS(contexts, ".curve_edit"); break; + case CTX_MODE_EDIT_CURVES: + ARRAY_SET_ITEMS(contexts, ".curves_edit"); + break; case CTX_MODE_EDIT_SURFACE: ARRAY_SET_ITEMS(contexts, ".curve_edit"); break; @@ -1523,6 +1529,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_VERTEX_GPENCIL: ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); break; + case CTX_MODE_SCULPT_CURVES: + ARRAY_SET_ITEMS(contexts, ".curves_sculpt"); + break; default: break; } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index b77994e28cb..cf52134f5ab 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -355,7 +355,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float has_meshdata = (tot || totedgedata); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { TransformMedian_Curve *median = &median_basis.curve; Curve *cu = ob->data; BPoint *bp; @@ -1089,7 +1089,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF) && + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF) && (apply_vcos || median_basis.curve.b_weight || median_basis.curve.weight || median_basis.curve.radius || median_basis.curve.tilt)) { const TransformMedian_Curve *median = &median_basis.curve, diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 785c5ab28c8..53f7b3d5871 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -63,7 +63,7 @@ typedef struct SnapCursorDataIntern { int x; int y; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - short shift, ctrl, alt, oskey; + uint8_t modifier; #endif } last_eventstate; @@ -478,10 +478,7 @@ static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *data_intern, } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK if (!(state && (state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { - if ((event->ctrl != data_intern->last_eventstate.ctrl) || - (event->shift != data_intern->last_eventstate.shift) || - (event->alt != data_intern->last_eventstate.alt) || - (event->oskey != data_intern->last_eventstate.oskey)) { + if (event->modifier != data_intern->last_eventstate.modifier) { return true; } } @@ -507,19 +504,13 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w } const wmEvent *event = wm->winactive->eventstate; - if ((event->ctrl == data_intern->last_eventstate.ctrl) && - (event->shift == data_intern->last_eventstate.shift) && - (event->alt == data_intern->last_eventstate.alt) && - (event->oskey == data_intern->last_eventstate.oskey)) { + if (event->modifier == data_intern->last_eventstate.modifier) { /* Nothing has changed. */ return data_intern->snap_data.is_snap_invert; } /* Save new eventstate. */ - data_intern->last_eventstate.ctrl = event->ctrl; - data_intern->last_eventstate.shift = event->shift; - data_intern->last_eventstate.alt = event->alt; - data_intern->last_eventstate.oskey = event->oskey; + data_intern->last_eventstate.modifier = event->modifier; const int snap_on = data_intern->snap_on; @@ -530,10 +521,10 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w } if (kmi->propvalue == snap_on) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && (event->modifier & KM_CTRL)) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && (event->modifier & KM_SHIFT)) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || + ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { return true; } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index c4078c4a690..593c4f6e755 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1351,7 +1351,7 @@ static void draw_selected_name( } } } - else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) { + else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY)) { /* try to display active bone and active shapekey too (if they exist) */ if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d6bc7ded92e..5adce170e06 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -832,13 +832,13 @@ void ED_view3d_cursor3d_position(bContext *C, return; } - ED_view3d_calc_zfac(rv3d, cursor_co, &flip); + ED_view3d_calc_zfac_ex(rv3d, cursor_co, &flip); /* Reset the depth based on the view offset (we _know_ the offset is in front of us). */ if (flip) { negate_v3_v3(cursor_co, rv3d->ofs); /* re initialize, no need to check flip again */ - ED_view3d_calc_zfac(rv3d, cursor_co, NULL /* &flip */); + ED_view3d_calc_zfac(rv3d, cursor_co); } if (use_depth) { /* maybe this should be accessed some other way */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 6cc197c8a43..a0c010a6813 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -105,8 +105,8 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; /* Hack: Switch action mode based on key input */ - const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL; - const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT; + const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0; + const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0; EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); if (is_ctrl_pressed && !is_shift_pressed) { EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index e1fd96ca1d4..055aac041f1 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -25,6 +25,7 @@ #include "BKE_editmesh.h" #include "BKE_mesh_iterators.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "DEG_depsgraph.h" @@ -334,6 +335,7 @@ void mesh_foreachScreenVert( Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -396,6 +398,7 @@ void mesh_foreachScreenEdge(ViewContext *vc, Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -483,6 +486,7 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -554,6 +558,7 @@ void mesh_foreachScreenFace( Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); data.vc = *vc; diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 0305989d142..d1e7f6ffb12 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -396,7 +396,7 @@ ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOp { float tvec[3]; negate_v3_v3(tvec, rv3d->ofs); - vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec); } vod->reverse = 1.0f; @@ -544,26 +544,24 @@ static void axis_set_view(bContext *C, void viewmove_apply(ViewOpsData *vod, int x, int y) { - if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { - vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx; - vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy; + const float event_ofs[2] = { + vod->prev.event_xy[0] - x, + vod->prev.event_xy[1] - y, + }; + + if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { + ED_view3d_camera_view_pan(vod->region, event_ofs); } - else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { - const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; - vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac); - vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac); - CLAMP(vod->rv3d->camdx, -1.0f, 1.0f); - CLAMP(vod->rv3d->camdy, -1.0f, 1.0f); + else if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { + vod->rv3d->ofs_lock[0] -= (event_ofs[0] * 2.0f) / (float)vod->region->winx; + vod->rv3d->ofs_lock[1] -= (event_ofs[1] * 2.0f) / (float)vod->region->winy; } else { float dvec[3]; - float mval_f[2]; - mval_f[0] = x - vod->prev.event_xy[0]; - mval_f[1] = y - vod->prev.event_xy[1]; - ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac); + ED_view3d_win_to_delta(vod->region, event_ofs, vod->init.zfac, dvec); - add_v3_v3(vod->rv3d->ofs, dvec); + sub_v3_v3(vod->rv3d->ofs, dvec); if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->area, vod->region); diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c index 06b616e71da..7b6b119294d 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c @@ -50,9 +50,12 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf) /* disabled mode switching for now, can re-implement better, later on */ #if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); + WM_modalkeymap_add_item( + keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item( + keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item( + keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_MOVE); #endif /* assign map to operators */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c index d2fd505a703..071643e9314 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_move.c +++ b/source/blender/editors/space_view3d/view3d_navigate_move.c @@ -43,8 +43,8 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items); /* items for modal map */ - WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM); /* disabled mode switching for now, can re-implement better, later on */ #if 0 diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c index ced8eca710b..1ce9bdcb211 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_ndof.c +++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c @@ -48,7 +48,7 @@ static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND; if (rv3d->is_persp) { - speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL); + speed *= ED_view3d_calc_zfac(rv3d, depth_pt); } return speed; @@ -347,6 +347,70 @@ void view3d_ndof_fly(const wmNDOFMotionData *ndof, /** \} */ /* -------------------------------------------------------------------- */ +/** \name NDOF Camera View Support + * \{ */ + +/** + * 2D orthographic style NDOF navigation within the camera view. + * Support navigating the camera view instead of leaving the camera-view and navigating in 3D. + */ +static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event) +{ + const wmNDOFMotionData *ndof = event->customdata; + View3D *v3d = CTX_wm_view3d(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = region->regiondata; + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + if ((v3d->camera && (rv3d->persp == RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA) == 0)) { + /* pass */ + } + else { + return OPERATOR_PASS_THROUGH; + } + + const bool has_translate = !is_zero_v2(ndof->tvec); + const bool has_zoom = ndof->tvec[2] != 0.0f; + + /* NOTE(@campbellbarton): In principle rotating could pass through to regular + * non-camera NDOF behavior (exiting the camera-view and rotating). + * Disabled this block since in practice it's difficult to control NDOF devices + * to perform some rotation with absolutely no translation. Causing rotation to + * randomly exit from the user perspective. Adjusting the dead-zone could avoid + * the motion feeling *glitchy* although in my own tests even then it didn't work reliably. + * Leave rotating out of camera-view disabled unless it can be made to work reliably. */ + if (!(has_translate || has_zoom)) { + // return OPERATOR_PASS_THROUGH; + } + + bool changed = false; + + if (has_translate) { + const float speed = ndof->dt * NDOF_PIXELS_PER_SECOND; + float event_ofs[2] = {ndof->tvec[0] * speed, ndof->tvec[1] * speed}; + if (ED_view3d_camera_view_pan(region, event_ofs)) { + changed = true; + } + } + + if (has_zoom) { + const float scale = 1.0f + (ndof->dt * ndof->tvec[2]); + if (ED_view3d_camera_view_zoom_scale(rv3d, scale)) { + changed = true; + } + } + + if (changed) { + ED_region_tag_redraw(region); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name NDOF Orbit/Translate Operator * \{ */ @@ -436,6 +500,13 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev return OPERATOR_CANCELLED; } + if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) { + const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event); + if (camera_retval != OPERATOR_PASS_THROUGH) { + return camera_retval; + } + } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewOpsData *vod; View3D *v3d; @@ -550,6 +621,13 @@ static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *e return OPERATOR_CANCELLED; } + if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) { + const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event); + if (camera_retval != OPERATOR_PASS_THROUGH) { + return camera_retval; + } + } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 56bd9c93216..9c070fb0341 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -24,8 +24,17 @@ /** \name View Roll Operator * \{ */ -static void view_roll_angle( - ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle) +/** + * \param use_axis_view: When true, keep axis-aligned orthographic views + * (when rotating in 90 degree increments). While this may seem obscure some NDOF + * devices have key shortcuts to do this (see #NDOF_BUTTON_ROLL_CW & #NDOF_BUTTON_ROLL_CCW). + */ +static void view_roll_angle(ARegion *region, + float quat[4], + const float orig_quat[4], + const float dvec[3], + float angle, + bool use_axis_view) { RegionView3D *rv3d = region->regiondata; float quat_mul[4]; @@ -38,7 +47,16 @@ static void view_roll_angle( /* avoid precision loss over time */ normalize_qt(quat); - rv3d->view = RV3D_VIEW_USER; + if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) { + if (ED_view3d_quat_to_axis_view(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll)) { + if (rv3d->view != RV3D_VIEW_USER) { + ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_mul); + } + } + } + else { + rv3d->view = RV3D_VIEW_USER; + } } static void viewroll_apply(ViewOpsData *vod, int x, int y) @@ -46,7 +64,8 @@ static void viewroll_apply(ViewOpsData *vod, int x, int y) float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); if (angle != 0.0f) { - view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); + view_roll_angle( + vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle, false); } if (vod->use_dyn_ofs) { @@ -169,7 +188,7 @@ static int viewroll_exec(bContext *C, wmOperator *op) normalize_v3_v3(mousevec, rv3d->viewinv[2]); negate_v3(mousevec); - view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle); + view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle, true); const float *dyn_ofs_pt = NULL; float dyn_ofs[3]; diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c index 774a8983c67..11de5463cdb 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -383,7 +383,7 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) int event_xy[2]; if (event->type == MOUSEPAN) { - if (event->is_direction_inverted) { + if (event->flag & WM_EVENT_SCROLL_INVERT) { event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index 8eb8fffaa31..5f6f9fde324 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -125,18 +125,18 @@ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoo float dvec[3]; float tvec[3]; float tpos[3]; - float mval_f[2]; + float xy_delta[2]; float zfac; negate_v3_v3(tpos, rv3d->ofs); - mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; - mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; + xy_delta[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; + xy_delta[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; /* Project cursor position into 3D space */ - zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + zfac = ED_view3d_calc_zfac(rv3d, tpos); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); /* Calculate view target position for dolly */ add_v3_v3v3(tvec, tpos, dvec); diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c index 4e909151ce4..f834efe4a7b 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c @@ -125,7 +125,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) negate_v3_v3(new_ofs, p); } else { - float mval_f[2]; + float xy_delta[2]; float zfac; /* We can't use the depth, fallback to the old way that doesn't set the center depth */ @@ -134,12 +134,12 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) { float tvec[3]; negate_v3_v3(tvec, new_ofs); - zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + zfac = ED_view3d_calc_zfac(rv3d, tvec); } - mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; - mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + xy_delta[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; + xy_delta[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); /* center the view to the center of the rectangle */ sub_v3_v3(new_ofs, dvec); } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 06b848571d8..98fb914cda9 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -727,6 +727,17 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active(); if (snap_state_new) { ipd->snap_state = snap_state = snap_state_new; + + /* For drag events, update the location since it will be set from the drag-start. + * This is needed as cursor-drawing doesn't deal with drag events and will use + * the current cursor location instead of the drag-start. */ + if (event->val == KM_CLICK_DRAG) { + /* Set this flag so snapping always updated. */ + int flag_orig = snap_state_new->flag; + snap_state_new->flag |= V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE; + ED_view3d_cursor_snap_data_get(snap_state_new, C, event->mval[0], event->mval[1]); + snap_state_new->flag = flag_orig; + } } snap_state->draw_point = true; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 2cf9ac0a52e..85d239507ce 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -276,7 +276,7 @@ float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[ return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize; } -float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip) +float ED_view3d_calc_zfac_ex(const RegionView3D *rv3d, const float co[3], bool *r_flip) { float zfac = mul_project_m4_v3_zfac(rv3d->persmat, co); @@ -299,10 +299,15 @@ float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_f return zfac; } +float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3]) +{ + return ED_view3d_calc_zfac_ex(rv3d, co, NULL); +} + float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float co[3]) { if (rv3d->is_persp) { - return ED_view3d_calc_zfac(rv3d, co, NULL); + return ED_view3d_calc_zfac(rv3d, co); } return -dot_v3v3(rv3d->viewinv[2], co); } @@ -436,8 +441,8 @@ bool view3d_get_view_aligned_coordinate(ARegion *region, if (ret == V3D_PROJ_RET_OK) { const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]), (float)(mval_cpy[1] - mval[1])}; - const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + const float zfac = ED_view3d_calc_zfac(rv3d, fp); + ED_view3d_win_to_delta(region, mval_f, zfac, dvec); sub_v3_v3(fp, dvec); return true; @@ -584,57 +589,57 @@ bool ED_view3d_win_to_3d_on_plane_with_fallback(const ARegion *region, } void ED_view3d_win_to_delta(const ARegion *region, - const float mval[2], - float out[3], - const float zfac) + const float xy_delta[2], + const float zfac, + float r_out[3]) { RegionView3D *rv3d = region->regiondata; float dx, dy; - dx = 2.0f * mval[0] * zfac / region->winx; - dy = 2.0f * mval[1] * zfac / region->winy; + dx = 2.0f * xy_delta[0] * zfac / region->winx; + dy = 2.0f * xy_delta[1] * zfac / region->winy; - out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy); - out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy); - out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy); + r_out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy); + r_out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy); + r_out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy); } -void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float out[3]) +void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float r_out[3]) { RegionView3D *rv3d = region->regiondata; if (rv3d->is_persp) { - copy_v3_v3(out, rv3d->viewinv[3]); + copy_v3_v3(r_out, rv3d->viewinv[3]); } else { - out[0] = 2.0f * mval[0] / region->winx - 1.0f; - out[1] = 2.0f * mval[1] / region->winy - 1.0f; + r_out[0] = 2.0f * mval[0] / region->winx - 1.0f; + r_out[1] = 2.0f * mval[1] / region->winy - 1.0f; if (rv3d->persp == RV3D_CAMOB) { - out[2] = -1.0f; + r_out[2] = -1.0f; } else { - out[2] = 0.0f; + r_out[2] = 0.0f; } - mul_project_m4_v3(rv3d->persinv, out); + mul_project_m4_v3(rv3d->persinv, r_out); } } -void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float out[3]) +void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3]) { RegionView3D *rv3d = region->regiondata; if (rv3d->is_persp) { - out[0] = 2.0f * (mval[0] / region->winx) - 1.0f; - out[1] = 2.0f * (mval[1] / region->winy) - 1.0f; - out[2] = -0.5f; - mul_project_m4_v3(rv3d->persinv, out); - sub_v3_v3(out, rv3d->viewinv[3]); + r_out[0] = 2.0f * (mval[0] / region->winx) - 1.0f; + r_out[1] = 2.0f * (mval[1] / region->winy) - 1.0f; + r_out[2] = -0.5f; + mul_project_m4_v3(rv3d->persinv, r_out); + sub_v3_v3(r_out, rv3d->viewinv[3]); } else { - negate_v3_v3(out, rv3d->viewinv[2]); + negate_v3_v3(r_out, rv3d->viewinv[2]); } - normalize_v3(out); + normalize_v3(r_out); } bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index f08c53fff47..e380a08dcc7 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1313,7 +1313,7 @@ static bool view3d_lasso_select(bContext *C, case OB_MESH: changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = do_lasso_select_curve(vc, mcoords, mcoords_len, sel_op); break; @@ -2695,7 +2695,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) retval = ED_lattice_deselect_all_multi(C); } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle); if (!retval && deselect_all) { retval = ED_curve_deselect_all_multi(C); @@ -3586,7 +3586,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = do_nurbs_box_select(&vc, &rect, sel_op); if (changed) { @@ -4342,7 +4342,7 @@ static bool obedit_circle_select(bContext *C, case OB_MESH: changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = nurbscurve_circle_select(vc, sel_op, mval, rad); break; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 8a219cd96d1..3e788f2d643 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -510,6 +510,39 @@ bool ED_view3d_persp_ensure(const Depsgraph *depsgraph, View3D *v3d, ARegion *re /** \} */ /* -------------------------------------------------------------------- */ +/** \name Camera View Utilities + * + * Utilities for manipulating the camera-view. + * \{ */ + +bool ED_view3d_camera_view_zoom_scale(RegionView3D *rv3d, const float scale) +{ + const float camzoom_init = rv3d->camzoom; + float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom); + /* Clamp both before and after conversion to prevent NAN on negative values. */ + + zoomfac = zoomfac * scale; + CLAMP(zoomfac, RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR); + rv3d->camzoom = BKE_screen_view3d_zoom_from_fac(zoomfac); + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + return (rv3d->camzoom != camzoom_init); +} + +bool ED_view3d_camera_view_pan(ARegion *region, const float event_ofs[2]) +{ + RegionView3D *rv3d = region->regiondata; + const float camdxy_init[2] = {rv3d->camdx, rv3d->camdy}; + const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f; + rv3d->camdx += event_ofs[0] / (region->winx * zoomfac); + rv3d->camdy += event_ofs[1] / (region->winy * zoomfac); + CLAMP(rv3d->camdx, -1.0f, 1.0f); + CLAMP(rv3d->camdy, -1.0f, 1.0f); + return (camdxy_init[0] != rv3d->camdx) || (camdxy_init[1] != rv3d->camdy); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Camera Lock API * * Lock the camera to the 3D Viewport, allowing view manipulation to transform the camera. diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 3bcc1b2968e..fd01f708ed2 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -177,8 +177,8 @@ void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy) r_vec[1] = dy; } else { - const float mval_f[2] = {(float)dx, (float)dy}; - ED_view3d_win_to_delta(t->region, mval_f, r_vec, t->zfac); + const float xy_delta[2] = {(float)dx, (float)dy}; + ED_view3d_win_to_delta(t->region, xy_delta, t->zfac, r_vec); } } else if (t->spacetype == SPACE_IMAGE) { @@ -1150,10 +1150,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) else if (event->val == KM_PRESS) { switch (event->type) { case EVT_CKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (event->alt) { + if (event->modifier & KM_ALT) { if (!(t->options & CTX_NO_PET)) { t->flag ^= T_PROP_CONNECTED; sort_trans_data_dist(t); @@ -1164,10 +1164,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_OKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (t->flag & T_PROP_EDIT && event->shift) { + if ((t->flag & T_PROP_EDIT) && (event->modifier & KM_SHIFT)) { t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX; calculatePropRatio(t); t->redraw |= TREDRAW_HARD; @@ -1175,7 +1175,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_PADPLUSKEY: - if (event->alt && t->flag & T_PROP_EDIT) { + if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) { t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) { t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end); @@ -1186,7 +1186,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_PADMINUS: - if (event->alt && t->flag & T_PROP_EDIT) { + if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) { t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw = TREDRAW_HARD; @@ -1202,7 +1202,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_NKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } if (ELEM(t->mode, TFM_ROTATION)) { @@ -1697,7 +1697,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* Needed to translate tweak events to mouse buttons. */ t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1; - t->is_launch_event_tweak = event ? ISTWEAK(event->type) : false; + t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false; /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate * (which can have type = 0) */ @@ -1780,10 +1780,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && + (event->modifier & KM_CTRL)) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && + (event->modifier & KM_SHIFT)) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || + ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { t->modifiers |= MOD_SNAP_INVERT; } break; @@ -1967,7 +1969,7 @@ bool checkUseAxisMatrix(TransInfo *t) /* currently only checks for editmode */ if (t->flag & T_EDIT) { if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { + (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE))) { /* not all editmode supports axis-matrix */ return true; } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 757c11f1179..3ee5868d5be 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -592,9 +592,11 @@ typedef struct TransInfo { /*************** NEW STUFF *********************/ /** event type used to launch transform. */ short launch_event; - /** Is the actual launch event a tweak event? (launch_event above is set to the corresponding - * mouse button then.) */ - bool is_launch_event_tweak; + /** + * Is the actual launch event a drag event? + * (`launch_event` is set to the corresponding mouse button then.) + */ + bool is_launch_event_drag; bool is_orient_default_overwrite; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 64ef170a13f..81b35e4539b 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -1041,8 +1041,7 @@ static void setNearestAxis3d(TransInfo *t) * and to overflow the short integers. * The formula used is a bit stupid, just a simplification of the subtraction * of two 2D points 30 pixels apart (that's the last factor in the formula) after - * projecting them with ED_view3d_win_to_delta and then get the length of that vector. - */ + * projecting them with #ED_view3d_win_to_delta and then get the length of that vector. */ zfac = mul_project_m4_v3_zfac(t->persmat, t->center_global); zfac = len_v3(t->persinv[0]) * 2.0f / t->region->winx * zfac * 30.0f; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 95b810daeaf..4a2169b381e 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1040,7 +1040,7 @@ static void init_proportional_edit(TransInfo *t) /* Already calculated by uv_set_connectivity_distance. */ } else if (convert_type == TC_CURVE_VERTS) { - BLI_assert(t->obedit_type == OB_CURVE); + BLI_assert(t->obedit_type == OB_CURVES_LEGACY); set_prop_dist(t, false); } else { @@ -1049,7 +1049,7 @@ static void init_proportional_edit(TransInfo *t) sort_trans_data_dist(t); } - else if (ELEM(t->obedit_type, OB_CURVE)) { + else if (ELEM(t->obedit_type, OB_CURVES_LEGACY)) { /* Needed because bezier handles can be partially selected * and are still added into transform data. */ sort_trans_data_selected_first(t); @@ -1286,7 +1286,7 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur convert_type = TC_MESH_VERTS; } } - else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { + else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) { convert_type = TC_CURVE_VERTS; } else if (t->obedit_type == OB_LATTICE) { diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 608fd59c8b7..54222fbb117 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -161,7 +161,7 @@ static void graph_bezt_get_transform_selection(const TransInfo *t, bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key; bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key; - if (use_handle && t->is_launch_event_tweak) { + if (use_handle && t->is_launch_event_drag) { if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) { key = right = false; } diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c index e12d0db8758..763af1f3384 100644 --- a/source/blender/editors/transform/transform_convert_object_texspace.c +++ b/source/blender/editors/transform/transform_convert_object_texspace.c @@ -44,7 +44,7 @@ void createTransTexspace(TransInfo *t) } id = ob->data; - if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) { + if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU_LEGACY, ID_MB)) { BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform"); return; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 1d86a8f9413..8987325145c 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -731,7 +731,8 @@ void postTrans(bContext *C, TransInfo *t) if (t->data_len_all != 0) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { /* free data malloced per trans-data */ - if (ELEM(t->obedit_type, OB_CURVE, OB_SURF, OB_GPENCIL) || (t->spacetype == SPACE_GRAPH)) { + if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF, OB_GPENCIL) || + (t->spacetype == SPACE_GRAPH)) { TransData *td = tc->data; for (int a = 0; a < tc->data_len; a++, td++) { if (td->flag & TD_BEZTRIPLE) { @@ -1145,7 +1146,7 @@ void calculateCenter(TransInfo *t) projectFloatView(t, axis, t->center2d); - /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */ + /* Rotate only needs correct 2d center, grab needs #ED_view3d_calc_zfac() value. */ if (t->mode == TFM_TRANSLATION) { copy_v3_v3(t->center_global, axis); } @@ -1154,17 +1155,16 @@ void calculateCenter(TransInfo *t) } if (t->spacetype == SPACE_VIEW3D) { - /* ED_view3d_calc_zfac() defines a factor for perspective depth correction, - * used in ED_view3d_win_to_delta() */ + /* #ED_view3d_calc_zfac() defines a factor for perspective depth correction, + * used in #ED_view3d_win_to_delta(). */ - /* zfac is only used convertViewVec only in cases operator was invoked in RGN_TYPE_WINDOW - * and never used in other cases. + /* NOTE: `t->zfac` is only used #convertViewVec only in cases operator was invoked in + * #RGN_TYPE_WINDOW and never used in other cases. * - * We need special case here as well, since ED_view3d_calc_zfac will crash when called - * for a region different from RGN_TYPE_WINDOW. - */ + * We need special case here as well, since #ED_view3d_calc_zfac will crash when called + * for a region different from #RGN_TYPE_WINDOW. */ if (t->region->regiontype == RGN_TYPE_WINDOW) { - t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global, NULL); + t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global); } else { t->zfac = 0.0f; diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index d2b2d2f116e..da601328192 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -669,6 +669,7 @@ static void gizmo2d_xform_invoke_prepare(const bContext *C, float c[3] = {mid[0], mid[1], 0.0f}; float orient_matrix[3][3]; + unit_m3(orient_matrix); ScrArea *area = CTX_wm_area(C); diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 7e121a717aa..f07fadb7fc1 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -816,7 +816,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, } FOREACH_EDIT_OBJECT_END(); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) { Curve *cu = ob_iter->data; Nurb *nu; @@ -1869,7 +1869,7 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, if (axis != -1) { wmWindow *win = CTX_wm_window(C); /* Swap single axis for two-axis constraint. */ - bool flip = win->eventstate->shift; + bool flip = (win->eventstate->modifier & KM_SHIFT) != 0; BLI_assert(axis_idx != -1); const short axis_type = gizmo_get_axis_type(axis_idx); if (axis_type != MAN_AXES_ROTATE) { diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index 6873ea862ce..f6f43f867ae 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -153,7 +153,7 @@ static void gizmo_mesh_extrude_setup(const bContext *C, wmGizmoGroup *gzgroup) op_idname = "ARMATURE_OT_extrude_move"; ggd->normal_axis = 1; } - else if (obact->type == OB_CURVE) { + else if (obact->type == OB_CURVES_LEGACY) { op_idname = "CURVE_OT_extrude_move"; ggd->normal_axis = 2; } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b6c4002b1c7..6162dfc9bb5 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -57,7 +57,7 @@ bool transdata_check_local_center(const TransInfo *t, short around) return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((t->options & (CTX_OBJECT | CTX_POSE_BONE)) || /* implicit: (t->flag & T_EDIT) */ - (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || + (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || (t->spacetype == SPACE_GRAPH) || (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE)))); } diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 0afc527d4a8..77c5707d814 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -146,9 +146,9 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] * by finding the closest edge in local-space. * However this skews the outcome with non-uniform-scale. */ - /* first get the direction of the original mouse position */ + /* First get the direction of the original mouse position. */ sub_v2_v2v2(dir, imval_fl, mval_fl); - ED_view3d_win_to_delta(t->region, dir, dir, t->zfac); + ED_view3d_win_to_delta(t->region, dir, t->zfac, dir); normalize_v3(dir); for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) { @@ -425,18 +425,18 @@ void drawVertSlide(TransInfo *t) /* direction from active vertex! */ if ((t->mval[0] != t->mouse.imval[0]) || (t->mval[1] != t->mouse.imval[1])) { float zfac; - float mval_ofs[2]; + float xy_delta[2]; float co_orig_3d[3]; float co_dest_3d[3]; - mval_ofs[0] = t->mval[0] - t->mouse.imval[0]; - mval_ofs[1] = t->mval[1] - t->mouse.imval[1]; + xy_delta[0] = t->mval[0] - t->mouse.imval[0]; + xy_delta[1] = t->mval[1] - t->mouse.imval[1]; mul_v3_m4v3( co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d); - zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d, NULL); + zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d); - ED_view3d_win_to_delta(t->region, mval_ofs, co_dest_3d, zfac); + ED_view3d_win_to_delta(t->region, xy_delta, zfac, co_dest_3d); invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 3722e83e451..936aca7d2e0 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -628,7 +628,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) "Proportional Falloff", "Falloff type for proportional editing mode"); /* Abusing id_curve :/ */ - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY); RNA_def_float(ot->srna, "proportional_size", 1, diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 6ca61d415ea..c0d943e17ee 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -350,7 +350,7 @@ bool BIF_createTransformOrientation(bContext *C, else if (obedit->type == OB_ARMATURE) { ts = createBoneSpace(C, reports, name, overwrite); } - else if (obedit->type == OB_CURVE) { + else if (obedit->type == OB_CURVES_LEGACY) { ts = createCurveSpace(C, reports, name, overwrite); } } @@ -984,7 +984,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer, negate_v3(plane); } /* end editmesh */ - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; Nurb *nu = NULL; int a; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index ce0cf3aa6cb..2f3c021ba38 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -86,7 +86,7 @@ int BIF_snappingSupported(Object *obedit) int status = 0; /* only support object mesh, armature, curves */ - if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) { status = 1; } @@ -308,7 +308,8 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event) eRedrawFlag status = TREDRAW_NOTHING; #if 0 /* XXX need a proper selector for all snap mode */ - if (BIF_snappingSupported(t->obedit) && event->type == TABKEY && event->shift) { + if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) && + (event->modifier & KM_SHIFT)) { /* toggle snap and reinit */ t->settings->snap_flag ^= SCE_SNAP; initSnapping(t, NULL); @@ -583,7 +584,7 @@ static short snap_mode_from_scene(TransInfo *t) /* All obedit types will match. */ const int obedit_type = t->obedit_type; if ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) || - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL, -1)) { + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL, -1)) { r_snap_mode = ts->snap_mode; if ((r_snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) && (t->mode == TFM_TRANSLATION)) { @@ -617,7 +618,7 @@ static short snap_select_type_get(TransInfo *t) * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ } else if ((obedit_type != -1) && - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) { /* Edit mode */ /* Temporary limited to edit mode meshes, armature, curves, metaballs. */ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index ca6940040b2..8b7133892ff 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -477,7 +477,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, } } else if (snap_select == SNAP_NOT_SELECTED) { - if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) { + if (is_object_active && base->object->mode != OB_MODE_OBJECT) { /* Pass. Consider the selection of elements being edited. */ } else if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) { @@ -1054,7 +1054,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, dt->r_hit_list); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: { if (!is_object_active) { @@ -2743,7 +2743,7 @@ static void snap_obj_fn(SnapObjectContext *sctx, dt->r_no, dt->r_index); break; - case OB_CURVE: + case OB_CURVES_LEGACY: retval = snapCurve( sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */ @@ -3117,7 +3117,7 @@ static short transform_snap_context_project_view3d_mixed_impl( sctx->runtime.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ - if (has_hit && ob_eval->type != OB_CURVE) { + if (has_hit && ob_eval->type != OB_CURVES_LEGACY) { /* Compute the new clip_pane but do not add it yet. */ float new_clipplane[4]; BLI_ASSERT_UNIT_V3(no); diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 2300e664dfa..7b4551fdb0c 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC ../include/ED_clip.h ../include/ED_curve.h ../include/ED_curves.h + ../include/ED_curves_sculpt.h ../include/ED_datafiles.h ../include/ED_file_indexer.h ../include/ED_fileselect.h diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index 0523a58825e..c1e093d5555 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -45,7 +45,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit) BMEditMesh *em = BKE_editmesh_from_object(obedit); BM_mesh_normals_update(em->bm); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(cu); Nurb *nu = nurbs->first; @@ -181,7 +181,8 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f bool ED_transverts_check_obedit(const Object *obedit) { - return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL)); + return ( + ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_MBALL)); } void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode) @@ -351,7 +352,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, } } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; int totmalloc = 0; ListBase *nurbs = BKE_curve_editNurbs_get(cu); diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 15fe944be49..32d405df841 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -306,7 +306,7 @@ bool ED_editors_flush_edits(Main *bmain) /* ***** XXX: functions are using old blender names, cleanup later ***** */ void apply_keyb_grid( - int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert) + bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert) { /* fac1 is for 'nothing', fac2 for CTRL, fac3 for SHIFT */ if (invert) { diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 014944da916..25deacbcdd1 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -226,7 +226,7 @@ static int lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op) ID *id = (ID *)idptr.data; - if ((id->lib != nullptr) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { + if (ID_IS_LINKED(id) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 3bf226f6ba1..be6ac6e13e6 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -321,7 +321,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (U.flag & USER_FLAG_NUMINPUT_ADVANCED) #endif { - if ((event->ctrl == 0) && (event->alt == 0) && (event->ascii != '\0') && + if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') && strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDITED; @@ -339,7 +339,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] |= NUM_EDITED; return true; } - if (event->ctrl) { + if (event->modifier & KM_CTRL) { n->flag &= ~NUM_EDIT_FULL; return true; } @@ -375,7 +375,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) updated = true; break; } - else if (event->shift || !n->str[0]) { + else if ((event->modifier & KM_SHIFT) || !n->str[0]) { n->val[idx] = n->val_org[idx]; n->val_flag[idx] &= ~NUM_EDITED; n->str[0] = '\0'; @@ -390,7 +390,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) case EVT_DELKEY: if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) { int t_cur = cur = n->str_cur; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mode = STRCUR_JUMP_DELIM; } BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true); @@ -416,7 +416,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) ATTR_FALLTHROUGH; case EVT_RIGHTARROWKEY: cur = n->str_cur; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mode = STRCUR_JUMP_DELIM; } BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true); @@ -442,7 +442,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE); #endif - idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1); + idx = (idx + idx_max + ((event->modifier & KM_CTRL) ? 0 : 2)) % (idx_max + 1); n->idx = idx; if (n->val_flag[idx] & NUM_EDITED) { value_to_editstr(n, idx); @@ -470,7 +470,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] |= NUM_EDITED; return true; } - else if (event->ctrl) { + else if (event->modifier & KM_CTRL) { n->flag &= ~NUM_EDIT_FULL; return true; } @@ -480,28 +480,28 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) #ifdef USE_FAKE_EDIT case EVT_PADMINUS: case EVT_MINUSKEY: - if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) { + if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) { n->val_flag[idx] ^= NUM_NEGATE; updated = true; } break; case EVT_PADSLASHKEY: case EVT_SLASHKEY: - if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) { + if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) { n->val_flag[idx] ^= NUM_INVERSE; updated = true; } break; #endif case EVT_CKEY: - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* Copy current `str` to the copy/paste buffer. */ WM_clipboard_text_set(n->str, 0); updated = true; } break; case EVT_VKEY: - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* extract the first line from the clipboard */ int pbuf_len; char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len); @@ -531,7 +531,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) /* Up to this point, if we have a ctrl modifier, skip. * This allows to still access most of modals' shortcuts even in numinput mode. */ - if (!updated && event->ctrl) { + if (!updated && (event->modifier & KM_CTRL)) { return false; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 91b29049ecf..c0d0fe95c8c 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -2598,7 +2598,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Increase limit */ case EVT_PADPLUSKEY: case WHEELUPMOUSE: - if (event->val == KM_PRESS && event->alt) { + if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) { ssc->limit_dist += 0.01f; if (!stitch_process_data(ssc, active_state, scene, false)) { stitch_cancel(C, op); @@ -2612,7 +2612,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Decrease limit */ case EVT_PADMINUS: case WHEELDOWNMOUSE: - if (event->val == KM_PRESS && event->alt) { + if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) { ssc->limit_dist -= 0.01f; ssc->limit_dist = MAX2(0.01f, ssc->limit_dist); if (!stitch_process_data(ssc, active_state, scene, false)) { @@ -2673,7 +2673,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Select geometry */ case RIGHTMOUSE: - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { stitch_cancel(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index 2655cd26bfe..ced0c2b9546 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -139,7 +139,7 @@ class GVArrayCommon { */ bool is_span() const; /** - * Returns the internally used span of the virtual array. This invokes undefined behavior is the + * Returns the internally used span of the virtual array. This invokes undefined behavior if the * virtual array is not stored as a span internally. */ GSpan get_internal_span() const; diff --git a/source/blender/functions/FN_multi_function_procedure_optimization.hh b/source/blender/functions/FN_multi_function_procedure_optimization.hh index 6e1e804c804..f5a02d84d42 100644 --- a/source/blender/functions/FN_multi_function_procedure_optimization.hh +++ b/source/blender/functions/FN_multi_function_procedure_optimization.hh @@ -38,9 +38,9 @@ namespace blender::fn::procedure_optimization { * For simplicity, and because this is the most common use case, this optimization currently only * works on a single chain of instructions. Destruct instructions are not moved across branches. * - * \param procedure The procedure that should be optimized. - * \param block_end_instr The instruction that points to the last instruction within a linear chain - * of instructions. The algorithm moves instructions backward starting at this instruction. + * \param procedure: The procedure that should be optimized. + * \param block_end_instr: The instruction that points to the last instruction within a linear + * chain of instructions. The algorithm moves instructions backward starting at this instruction. */ void move_destructs_up(MFProcedure &procedure, MFInstruction &block_end_instr); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 054a6b51adf..7b514b6a49b 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -26,7 +26,7 @@ struct FieldTreeInfo { */ MultiValueMap<GFieldRef, GFieldRef> field_users; /** - * The same field input may exist in the field tree as as separate nodes due to the way + * The same field input may exist in the field tree as separate nodes due to the way * the tree is constructed. This set contains every different input only once. */ VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs; @@ -137,7 +137,7 @@ static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info, } /** - * Builds the #procedure so that it computes the the fields. + * Builds the #procedure so that it computes the fields. */ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, ResourceScope &scope, diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index c98bd9d6b74..4f7024eea82 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -13,6 +13,7 @@ #include "BLI_task.hh" #include "BKE_collection.h" +#include "BKE_curves.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" @@ -118,7 +119,7 @@ struct RealizeMeshTask { }; struct RealizeCurveInfo { - const CurveEval *curve = nullptr; + const Curves *curves; /** * Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional` * will be empty. @@ -163,7 +164,7 @@ struct AllCurvesInfo { /** Ordering of all attributes that are propagated to the output curve generically. */ OrderedAttributes attributes; /** Ordering of the original curves that are joined. */ - VectorSet<const CurveEval *> order; + VectorSet<const Curves *> order; /** Preprocessed data about every original curve. This is ordered by #order. */ Array<RealizeCurveInfo> realize_info; bool create_id_attribute = false; @@ -443,16 +444,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve != nullptr && !curve->splines().is_empty()) { - const int curve_index = gather_info.curves.order.index_of(curve); + const Curves *curves = curve_component.get_for_read(); + if (curves != nullptr && curves->geometry.curve_size > 0) { + const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset, &curve_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.spline_offset += curve->splines().size(); + gather_info.r_offsets.spline_offset += curves->geometry.curve_size; } break; } @@ -1038,11 +1039,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( } static void gather_curves_to_realize(const GeometrySet &geometry_set, - VectorSet<const CurveEval *> &r_curves) + VectorSet<const Curves *> &r_curves) { - if (const CurveEval *curve = geometry_set.get_curve_for_read()) { - if (!curve->splines().is_empty()) { - r_curves.add(curve); + if (const Curves *curves = geometry_set.get_curves_for_read()) { + if (curves->geometry.curve_size != 0) { + r_curves.add(curves); } } if (const InstancesComponent *instances = @@ -1064,12 +1065,12 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set, info.realize_info.reinitialize(info.order.size()); for (const int curve_index : info.realize_info.index_range()) { RealizeCurveInfo &curve_info = info.realize_info[curve_index]; - const CurveEval *curve = info.order[curve_index]; - curve_info.curve = curve; + const Curves *curves = info.order[curve_index]; + curve_info.curves = curves; /* Access attributes. */ CurveComponent component; - component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly); + component.replace(const_cast<Curves *>(curves), GeometryOwnershipType::ReadOnly); curve_info.spline_attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeDomain domain = info.attributes.kinds[attribute_index].domain; @@ -1095,9 +1096,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, MutableSpan<GMutableSpan> dst_spline_attributes) { const RealizeCurveInfo &curve_info = *task.curve_info; - const CurveEval &curve = *curve_info.curve; + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_info.curves); - const Span<SplinePtr> src_splines = curve.splines(); + const Span<SplinePtr> src_splines = curve->splines(); /* Initialize point attributes. */ threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) { @@ -1206,12 +1207,12 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, } const RealizeCurveTask &last_task = tasks.last(); - const CurveEval &last_curve = *last_task.curve_info->curve; - const int tot_splines = last_task.start_spline_index + last_curve.splines().size(); + const Curves &last_curves = *last_task.curve_info->curves; + const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size; Array<SplinePtr> dst_splines(tot_splines); - CurveEval *dst_curve = new CurveEval(); + std::unique_ptr<CurveEval> dst_curve = std::make_unique<CurveEval>(); dst_curve->attributes.reallocate(tot_splines); CustomDataAttributes &spline_attributes = dst_curve->attributes; @@ -1242,7 +1243,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, dst_curve->add_splines(dst_splines); CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); - dst_component.replace(dst_curve); + dst_component.replace(curve_eval_to_curves(*dst_curve)); } /** \} */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index e036b814916..fba83579ab1 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -104,7 +104,9 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob, float obinv[4][4]; unit_m4(mat_offset); - add_v3_v3(mat_offset[3], mmd->offset); + if (mmd->flag & GP_ARRAY_USE_OFFSET) { + add_v3_v3(mat_offset[3], mmd->offset); + } invert_m4_m4(obinv, ob->obmat); mul_m4_series(r_offset, mat_offset, obinv, mmd->object->obmat); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index ba61ea148ca..1058f861be3 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -219,7 +219,7 @@ static void add_this_collection(Collection *c, return; } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) { - if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) { DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); @@ -393,7 +393,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); - uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_back_face_culling", 0, IFACE_("Force Backface Culling"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index 3b7b82e3085..365b9afe348 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -88,7 +88,7 @@ static void deformStroke(GpencilModifierData *md, break; } case GP_SIMPLIFY_SAMPLE: { - BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false); + BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false, mmd->sharp_threshold); break; } case GP_SIMPLIFY_MERGE: { @@ -143,6 +143,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } else if (mode == GP_SIMPLIFY_SAMPLE) { uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "sharp_threshold", 0, NULL, ICON_NONE); } else if (mode == GP_SIMPLIFY_MERGE) { uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c index 5e916a13f2c..dcec2865f29 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -79,11 +79,6 @@ static void deformStroke(GpencilModifierData *md, short type = gps->totpoints < 3 ? GP_SUBDIV_SIMPLE : mmd->type; BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, type); - - /* If the stroke is cyclic, must generate the closing geometry. */ - if (gps->flag & GP_STROKE_CYCLIC) { - BKE_gpencil_stroke_close(gps); - } } static void bakeModifier(struct Main *UNUSED(bmain), diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 2c7999699c7..5d952991cf7 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -343,6 +343,7 @@ typedef enum eLineartTriangleFlags { LRT_CULL_GENERATED = (1 << 2), LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3), LRT_TRIANGLE_NO_INTERSECTION = (1 << 4), + LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5), } eLineartTriangleFlags; /** diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 6177b9ac366..2f648d7ed4b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1388,21 +1388,20 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) LineartVert *vt; int i; - if (!rb->cam_is_persp) { - return; - } - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) { vt = eln->pointer; for (i = 0; i < eln->element_count; i++) { - /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ - vt[i].fbcoord[0] /= vt[i].fbcoord[3]; - vt[i].fbcoord[1] /= vt[i].fbcoord[3]; - /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates) - * at the moment. - * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in - * the future, the line below correctly transforms it to view space coordinates. */ - // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + if (rb->cam_is_persp) { + /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ + vt[i].fbcoord[0] /= vt[i].fbcoord[3]; + vt[i].fbcoord[1] /= vt[i].fbcoord[3]; + /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates) + * at the moment. + * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed + * in the future, the line below correctly transforms it to view space coordinates. */ + // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + } + /* Shifting is always needed. */ vt[i].fbcoord[0] -= rb->shift_x * 2; vt[i].fbcoord[1] -= rb->shift_y * 2; } @@ -1529,8 +1528,9 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, double *view_vector = vv; double dot_1 = 0, dot_2 = 0; double result; + bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); - if (rb->use_contour || rb->use_back_face_culling) { + if (rb->use_contour || rb->use_back_face_culling || material_back_face) { if (rb->cam_is_persp) { sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); @@ -1555,14 +1555,19 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, tri2->flags |= LRT_CULL_DISCARD; } } + if (material_back_face) { + if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) { + tri1->flags |= LRT_CULL_DISCARD; + } + if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) { + tri2->flags |= LRT_CULL_DISCARD; + } + } } else { view_vector = rb->view_vector; } - dot_1 = dot_v3v3_db(view_vector, tri1->gn); - dot_2 = dot_v3v3_db(view_vector, tri2->gn); - if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } @@ -1573,6 +1578,16 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, return edge_flag_result; } + /* Do not show lines other than contour on back face (because contour has one adjacent face that + * isn't a back face). + * TODO(Yiming): Do we need separate option for this? */ + if (rb->use_back_face_culling || + ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) { + if (dot_1 < 0 && dot_2 < 0) { + return edge_flag_result; + } + } + if (rb->use_crease) { if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; @@ -1859,6 +1874,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu mat->lineart.material_mask_bits : 0); tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); + tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? + LRT_TRIANGLE_MAT_BACK_FACE_CULLING : + 0; tri->intersection_mask = obi->override_intersection_mask; @@ -2193,7 +2211,7 @@ static void lineart_main_load_geometries( mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); - if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { continue; } @@ -2205,7 +2223,7 @@ static void lineart_main_load_geometries( } if (use_ob->type == OB_MESH) { - use_mesh = use_ob->data; + use_mesh = BKE_object_get_evaluated_mesh(use_ob); } else { /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh @@ -2502,19 +2520,16 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut); mul_v4_m4v3_db(trans, vp, gloc); mul_v3db_db(trans, (1 / trans[3])); - } - else { - interp_v3_v3v3_db(trans, e->v1->fbcoord, e->v2->fbcoord, cut); - } - trans[0] -= cam_shift_x * 2; - trans[1] -= cam_shift_y * 2; - - /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */ - if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) { - cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]); - } - else { - cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]); + trans[0] -= cam_shift_x * 2; + trans[1] -= cam_shift_y * 2; + /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */ + if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > + fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) { + cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]); + } + else { + cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]); + } } #define LRT_GUARD_NOT_FOUND \ @@ -2940,9 +2955,10 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, * them as well. */ mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc); mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc); - mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); - mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); - + if (rb->cam_is_persp) { + mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); + mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); + } v1->fbcoord[0] -= rb->shift_x * 2; v1->fbcoord[1] -= rb->shift_y * 2; v2->fbcoord[0] -= rb->shift_x * 2; @@ -4453,7 +4469,7 @@ static void lineart_gpencil_generate(LineartCache *cache, if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) { if (eval_ob && eval_ob->type == OB_MESH) { int dindex = 0; - Mesh *me = (Mesh *)eval_ob->data; + Mesh *me = BKE_object_get_evaluated_mesh(eval_ob); if (me->dvert) { LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) { if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e883a12a5b2..3ad43ef05af 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -147,6 +147,7 @@ set(SRC intern/gpu_select_private.h intern/gpu_shader_create_info.hh intern/gpu_shader_create_info_private.hh + intern/gpu_shader_dependency_private.h intern/gpu_shader_interface.hh intern/gpu_shader_private.hh intern/gpu_state_private.hh @@ -384,7 +385,7 @@ file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}") list(APPEND SRC ${glsl_source_list_file}) list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) -set(SHADER_CREATE_INFOS +set(SRC_SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_composite_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh @@ -435,7 +436,7 @@ set(SHADER_CREATE_INFOS ) set(SHADER_CREATE_INFOS_CONTENT "") -foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS}) +foreach(DESCRIPTOR_FILE ${SRC_SHADER_CREATE_INFOS}) string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n") endforeach() @@ -452,12 +453,21 @@ if(WITH_IMAGE_DDS) add_definitions(-DWITH_DDS) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") target_link_libraries(bf_gpu PUBLIC bf_draw_shaders bf_gpu_shaders ) +if(WITH_OPENCOLORIO) + target_link_libraries(bf_gpu PUBLIC bf_ocio_shaders) +endif() + + if(CXX_WARN_NO_SUGGEST_OVERRIDE) target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>) endif() @@ -477,18 +487,22 @@ if(WITH_GPU_SHADER_BUILDER) ) target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR}) - set(BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) + set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) add_custom_command( OUTPUT - ${BAKED_CREATE_INFOS_FILE} + ${SRC_BAKED_CREATE_INFOS_FILE} COMMAND - "$<TARGET_FILE:shader_builder>" ${BAKED_CREATE_INFOS_FILE} + "$<TARGET_FILE:shader_builder>" ${SRC_BAKED_CREATE_INFOS_FILE} DEPENDS shader_builder ) set(GPU_SHADER_INFO_SRC intern/gpu_shader_info_baked.cc - ${BAKED_CREATE_INFOS_FILE} + ${SRC_BAKED_CREATE_INFOS_FILE} + + # For project files to be aware of these headers. + ${SRC_SHADER_CREATE_INFOS} + shaders/infos/gpu_interface_info.hh ) blender_add_lib(bf_gpu_shader_infos "${GPU_SHADER_INFO_SRC}" "" "" "") diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 803a5825c94..7fad8dd23be 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -119,6 +119,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); * Returns the index of verts in the batch. */ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); +bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index d689fbe14b5..734d407d011 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -226,6 +226,19 @@ GPUTexture *GPU_texture_create_compressed_2d( * Create an error texture that will bind an invalid texture (pink) at draw time. */ GPUTexture *GPU_texture_create_error(int dimension, bool array); +/** + * Create an alias of the source texture data. + * If \a src is freed, the texture view will continue to be valid. + * If \a mip_start or \a mip_len is bigger than available mips they will be clamped. + * TODO(@fclem): Target conversion is not implemented yet. + */ +GPUTexture *GPU_texture_create_view(const char *name, + const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len); void GPU_texture_update_mipmap(GPUTexture *tex, int miplvl, diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 44e95b1f5f5..32b117dac12 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -191,6 +191,16 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) return -1; } +bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts) +{ + for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { + if (batch->verts[v] == verts) { + return true; + } + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index da4e49f0257..57e415a8183 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -165,7 +165,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type) /** * Link stack uniform buffer. - * This is called for the input/output sockets that are note connected. + * This is called for the input/output sockets that are not connected. */ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat, bNode *node, diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index bd0187e2dc5..bf74d44d9a7 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -255,7 +255,7 @@ struct StageInterfaceInfo { }; /** - * @brief Describe inputs & outputs, stage interfaces, resources and sources of a shader. + * \brief Describe inputs & outputs, stage interfaces, resources and sources of a shader. * If all data is correctly provided, this is all that is needed to create and compile * a GPUShader. * @@ -497,9 +497,9 @@ struct ShaderCreateInfo { /** * IMPORTANT: invocations count is only used if GL_ARB_gpu_shader5 is supported. On - * implementations that do not supports it, the max_vertices will be be multiplied by - * invocations. Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` - * and make a code path that does not rely on gl_InvocationID. + * implementations that do not supports it, the max_vertices will be multiplied by invocations. + * Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` and make a code + * path that does not rely on #gl_InvocationID. */ Self &geometry_layout(PrimitiveIn prim_in, PrimitiveOut prim_out, diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 8a842ef4d7c..5b7df035acd 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -21,6 +21,9 @@ extern "C" { #define SHADER_SOURCE(datatoc, filename, filepath) extern char datatoc[]; #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" +#ifdef WITH_OCIO +# include "glsl_ocio_source_list.h" +#endif #undef SHADER_SOURCE } @@ -348,6 +351,9 @@ void gpu_shader_dependency_init() g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" +#ifdef WITH_OCIO +# include "glsl_ocio_source_list.h" +#endif #undef SHADER_SOURCE int errors = 0; diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h index 29ee70962d1..7b9ecef5835 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency_private.h +++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h @@ -38,7 +38,7 @@ StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name); /** * \brief Find the name of the file from which the given string was generated. - * \return Return filename or empty string. + * \return filename or empty string. * \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source() */ StringRefNull gpu_shader_dependency_get_filename_from_source_string( diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index da5f08f003e..9e6a6f75391 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -52,11 +52,13 @@ Texture::~Texture() #endif } -bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) +bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = layers; d_ = 0; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_1D_ARRAY : GPU_TEXTURE_1D; @@ -66,11 +68,13 @@ bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) +bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = layers; + int mips_max = 1 + floorf(log2f(max_ii(w, h))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_2D_ARRAY : GPU_TEXTURE_2D; @@ -80,11 +84,13 @@ bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) +bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = d; + int mips_max = 1 + floorf(log2f(max_iii(w, h, d))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = GPU_TEXTURE_3D; @@ -94,11 +100,13 @@ bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_cubemap(int w, int layers, eGPUTextureFormat format) +bool Texture::init_cubemap(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = w; d_ = max_ii(1, layers) * 6; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_CUBE_ARRAY : GPU_TEXTURE_CUBE; @@ -123,6 +131,42 @@ bool Texture::init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format) return this->init_internal(vbo); } +bool Texture::init_view(const GPUTexture *src_, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len) +{ + const Texture *src = unwrap(src_); + w_ = src->w_; + h_ = src->h_; + d_ = src->d_; + switch (type_) { + case GPU_TEXTURE_1D_ARRAY: + h_ = layer_len; + break; + case GPU_TEXTURE_CUBE_ARRAY: + BLI_assert(layer_len % 6 == 0); + ATTR_FALLTHROUGH; + case GPU_TEXTURE_2D_ARRAY: + d_ = layer_len; + break; + default: + BLI_assert(layer_len == 1 && layer_start == 0); + break; + } + mip_start = min_ii(mip_start, src->mipmaps_ - 1); + mip_len = min_ii(mip_len, (src->mipmaps_ - mip_start)); + mipmaps_ = mip_len; + format_ = format; + format_flag_ = to_format_flag(format); + /* For now always copy the target. Target aliasing could be exposed later. */ + type_ = src->type_; + sampler_state = src->sampler_state; + return this->init_internal(src_, mip_start, layer_start); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -187,28 +231,29 @@ static inline GPUTexture *gpu_texture_create(const char *name, const int h, const int d, const eGPUTextureType type, - int UNUSED(mips), + int mips, eGPUTextureFormat tex_format, eGPUDataFormat data_format, const void *pixels) { + BLI_assert(mips > 0); Texture *tex = GPUBackend::get()->texture_alloc(name); bool success = false; switch (type) { case GPU_TEXTURE_1D: case GPU_TEXTURE_1D_ARRAY: - success = tex->init_1D(w, h, tex_format); + success = tex->init_1D(w, h, mips, tex_format); break; case GPU_TEXTURE_2D: case GPU_TEXTURE_2D_ARRAY: - success = tex->init_2D(w, h, d, tex_format); + success = tex->init_2D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_3D: - success = tex->init_3D(w, h, d, tex_format); + success = tex->init_3D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: - success = tex->init_cubemap(w, d, tex_format); + success = tex->init_cubemap(w, d, mips, tex_format); break; default: break; @@ -286,7 +331,7 @@ GPUTexture *GPU_texture_create_compressed_2d( const char *name, int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) { Texture *tex = GPUBackend::get()->texture_alloc(name); - bool success = tex->init_2D(w, h, 0, tex_format); + bool success = tex->init_2D(w, h, 0, miplen, tex_format); if (!success) { delete tex; @@ -334,6 +379,21 @@ GPUTexture *GPU_texture_create_error(int dimension, bool is_array) return gpu_texture_create("invalid_tex", w, h, d, type, 1, GPU_RGBA8, GPU_DATA_FLOAT, pixel); } +GPUTexture *GPU_texture_create_view(const char *name, + const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len) +{ + BLI_assert(mip_len > 0); + BLI_assert(layer_len > 0); + Texture *view = GPUBackend::get()->texture_alloc(name); + view->init_view(src, format, mip_start, mip_len, layer_start, layer_len); + return wrap(view); +} + /* ------ Update ------ */ void GPU_texture_update_mipmap(GPUTexture *tex_, diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 2c57c467bcf..ec11fab421c 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -101,11 +101,17 @@ class Texture { virtual ~Texture(); /* Return true on success. */ - bool init_1D(int w, int layers, eGPUTextureFormat format); - bool init_2D(int w, int h, int layers, eGPUTextureFormat format); - bool init_3D(int w, int h, int d, eGPUTextureFormat format); - bool init_cubemap(int w, int layers, eGPUTextureFormat format); + bool init_1D(int w, int layers, int mips, eGPUTextureFormat format); + bool init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format); + bool init_3D(int w, int h, int d, int mips, eGPUTextureFormat format); + bool init_cubemap(int w, int layers, int mips, eGPUTextureFormat format); bool init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format); + bool init_view(const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len); virtual void generate_mipmap() = 0; virtual void copy_to(Texture *tex) = 0; @@ -234,6 +240,7 @@ class Texture { protected: virtual bool init_internal() = 0; virtual bool init_internal(GPUVertBuf *vbo) = 0; + virtual bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) = 0; }; /* Syntactic sugar. */ diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index fd4a87bc544..b5a572bccbe 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -277,8 +277,6 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_matrix_identity_set(); GPU_matrix_identity_projection_set(); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE); - immUniform1i("overlayTexture", 0); - immUniform1i("imageTexture", 1); int settings = stereo_format->display_mode; if (settings == S3D_DISPLAY_ANAGLYPH) { switch (stereo_format->anaglyph_type) { @@ -432,8 +430,6 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); GPU_batch_uniform_1i(batch, "overlay", do_overlay_merge); GPU_batch_uniform_1i(batch, "display_transform", display_colorspace); - GPU_batch_uniform_1i(batch, "image_texture", 0); - GPU_batch_uniform_1i(batch, "overlays_texture", 1); } GPU_texture_bind(color, 0); diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 7a1674a5f01..302d8249914 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -240,6 +240,7 @@ static void detect_workarounds() GLContext::texture_cube_map_array_support = false; GLContext::texture_filter_anisotropic_support = false; GLContext::texture_gather_support = false; + GLContext::texture_storage_support = false; GLContext::vertex_attrib_binding_support = false; return; } @@ -439,6 +440,7 @@ bool GLContext::shader_draw_parameters_support = false; bool GLContext::texture_cube_map_array_support = false; bool GLContext::texture_filter_anisotropic_support = false; bool GLContext::texture_gather_support = false; +bool GLContext::texture_storage_support = false; bool GLContext::vertex_attrib_binding_support = false; /** Workarounds. */ @@ -501,6 +503,7 @@ void GLBackend::capabilities_init() GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array; GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic; GLContext::texture_gather_support = GLEW_ARB_texture_gather; + GLContext::texture_storage_support = GLEW_VERSION_4_3; GLContext::vertex_attrib_binding_support = GLEW_ARB_vertex_attrib_binding; detect_workarounds(); diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index fe2ad0a4747..369b667cbcc 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -64,6 +64,7 @@ class GLContext : public Context { static bool texture_cube_map_array_support; static bool texture_filter_anisotropic_support; static bool texture_gather_support; + static bool texture_storage_support; static bool vertex_attrib_binding_support; /** Workarounds. */ diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc index cfedfe348f1..c32a6afd8cf 100644 --- a/source/blender/gpu/opengl/gl_immediate.cc +++ b/source/blender/gpu/opengl/gl_immediate.cc @@ -139,7 +139,7 @@ void GLImmediate::end() GLContext::get()->state_manager->apply_state(); /* We convert the offset in vertex offset from the buffer's start. - * This works because we added some padding to align the first vertex vertex. */ + * This works because we added some padding to align the first vertex. */ uint v_first = buffer_offset() / vertex_format.stride; GLVertArray::update_bindings( vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface); diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 22af2dd463f..0a5c7f8e79e 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -69,9 +69,79 @@ bool GLTexture::init_internal() return false; } - this->ensure_mipmaps(0); + GLenum internal_format = to_gl_internal_format(format_); + const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE); + const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY); + const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY); + const int dimensions = (is_cubemap) ? 2 : this->dimensions_count(); + GLenum gl_format = to_gl_data_format(format_); + GLenum gl_type = to_gl(to_data_format(format_)); + + auto mip_size = [&](int h, int w = 1, int d = 1) -> size_t { + return divide_ceil_u(w, 4) * divide_ceil_u(h, 4) * divide_ceil_u(d, 4) * + to_block_size(format_); + }; + switch (dimensions) { + default: + case 1: + if (GLContext::texture_storage_support) { + glTexStorage1D(target_, mipmaps_, internal_format, w_); + } + else { + for (int i = 0, w = w_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage1D(target_, i, internal_format, w, 0, mip_size(w), nullptr); + } + else { + glTexImage1D(target_, i, internal_format, w, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + } + } + break; + case 2: + if (GLContext::texture_storage_support) { + glTexStorage2D(target_, mipmaps_, internal_format, w_, h_); + } + else { + for (int i = 0, w = w_, h = h_; i < mipmaps_; i++) { + for (int f = 0; f < (is_cubemap ? 6 : 1); f++) { + GLenum target = (is_cubemap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + f : target_; + if (is_compressed) { + glCompressedTexImage2D(target, i, internal_format, w, h, 0, mip_size(w, h), nullptr); + } + else { + glTexImage2D(target, i, internal_format, w, h, 0, gl_format, gl_type, nullptr); + } + } + w = max_ii(1, (w / 2)); + h = is_layered ? h_ : max_ii(1, (h / 2)); + } + } + break; + case 3: + if (GLContext::texture_storage_support) { + glTexStorage3D(target_, mipmaps_, internal_format, w_, h_, d_); + } + else { + for (int i = 0, w = w_, h = h_, d = d_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage3D( + target_, i, internal_format, w, h, d, 0, mip_size(w, h, d), nullptr); + } + else { + glTexImage3D(target_, i, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + h = max_ii(1, (h / 2)); + d = is_layered ? d_ : max_ii(1, (d / 2)); + } + } + break; + } + this->mip_range_set(0, mipmaps_ - 1); - /* Avoid issue with incomplete textures. */ + /* Avoid issue with formats not supporting filtering. Nearest by default. */ if (GLContext::direct_state_access_support) { glTextureParameteri(tex_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } @@ -105,65 +175,26 @@ bool GLTexture::init_internal(GPUVertBuf *vbo) return true; } -void GLTexture::ensure_mipmaps(int miplvl) +bool GLTexture::init_internal(const GPUTexture *src, int mip_offset, int layer_offset) { - int effective_h = (type_ == GPU_TEXTURE_1D_ARRAY) ? 0 : h_; - int effective_d = (type_ != GPU_TEXTURE_3D) ? 0 : d_; - int max_dimension = max_iii(w_, effective_h, effective_d); - int max_miplvl = floor(log2(max_dimension)); - miplvl = min_ii(miplvl, max_miplvl); - - while (mipmaps_ < miplvl) { - int mip = ++mipmaps_; - const int dimensions = this->dimensions_count(); - - int w = mip_width_get(mip); - int h = mip_height_get(mip); - int d = mip_depth_get(mip); - GLenum internal_format = to_gl_internal_format(format_); - GLenum gl_format = to_gl_data_format(format_); - GLenum gl_type = to_gl(to_data_format(format_)); + BLI_assert(GLContext::texture_storage_support); - GLContext::state_manager_active_get()->texture_bind_temp(this); + const GLTexture *gl_src = static_cast<const GLTexture *>(unwrap(src)); + GLenum internal_format = to_gl_internal_format(format_); + target_ = to_gl_target(type_); - if (type_ == GPU_TEXTURE_CUBE) { - for (int i = 0; i < d; i++) { - GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; - glTexImage2D(target, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - } - } - else if (format_flag_ & GPU_FORMAT_COMPRESSED) { - size_t size = ((w + 3) / 4) * ((h + 3) / 4) * to_block_size(format_); - switch (dimensions) { - default: - case 1: - glCompressedTexImage1D(target_, mip, internal_format, w, 0, size, nullptr); - break; - case 2: - glCompressedTexImage2D(target_, mip, internal_format, w, h, 0, size, nullptr); - break; - case 3: - glCompressedTexImage3D(target_, mip, internal_format, w, h, d, 0, size, nullptr); - break; - } - } - else { - switch (dimensions) { - default: - case 1: - glTexImage1D(target_, mip, internal_format, w, 0, gl_format, gl_type, nullptr); - break; - case 2: - glTexImage2D(target_, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - break; - case 3: - glTexImage3D(target_, mip, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); - break; - } - } - } + glTextureView(tex_id_, + target_, + gl_src->tex_id_, + internal_format, + mip_offset, + mipmaps_, + layer_offset, + this->layer_count()); - this->mip_range_set(0, mipmaps_); + debug::object_label(GL_TEXTURE, tex_id_, name_); + + return true; } /** \} */ @@ -216,9 +247,7 @@ void GLTexture::update_sub( BLI_assert(validate_data_format(format_, type)); BLI_assert(data != nullptr); - this->ensure_mipmaps(mip); - - if (mip > mipmaps_) { + if (mip >= mipmaps_) { debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels."); return; } @@ -283,7 +312,6 @@ void GLTexture::update_sub( */ void GLTexture::generate_mipmap() { - this->ensure_mipmaps(9999); /* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789). * In this case we just create a complete texture with mipmaps manually without * down-sampling. You must initialize the texture levels using other methods like diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 07d3bb8c946..d4d024f5e3e 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -74,11 +74,11 @@ class GLTexture : public Texture { bool init_internal() override; /** Return true on success. */ bool init_internal(GPUVertBuf *vbo) override; + /** Return true on success. */ + bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) override; private: bool proxy_check(int mip); - /** Will create enough mipmaps up to get to the given level. */ - void ensure_mipmaps(int mip); void update_sub_direct_state_access( int mip, int offset[3], int extent[3], GLenum gl_format, GLenum gl_type, const void *data); GPUFrameBuffer *framebuffer_get(); diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 88b69eb5b62..04f60f10d41 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -54,7 +54,7 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); const ShaderInput *input = interface->attr_get(name); - if (input == nullptr) { + if (input == nullptr || input->location == -1) { continue; } diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh index d3b70cb67f9..2798846b310 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh @@ -16,6 +16,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge) .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .push_constant(Type::BOOL, "display_transform") .push_constant(Type::BOOL, "overlay") + /* Sampler slots should match OCIO's. */ .sampler(0, ImageType::FLOAT_2D, "image_texture") .sampler(1, ImageType::FLOAT_2D, "overlays_texture") .vertex_source("gpu_shader_2D_image_vert.glsl") diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index 6dcfd3d014e..470dfb01bdd 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -244,9 +244,9 @@ static int initialize_chain(Object *ob, bPoseChannel *pchan_tip, bConstraint *co } if (BLI_listbase_is_empty(&curchan->iktree) == false) { - /* Oh oh, there is already a chain starting from this channel and our chain is longer... + /* Oh, there is already a chain starting from this channel and our chain is longer. * Should handle this by moving the previous chain up to the beginning of our chain - * For now we just stop here */ + * For now we just stop here. */ break; } } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 23b9c85bd5b..8929a467670 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -560,6 +560,9 @@ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf); * Create char buffer, color corrected if necessary, for ImBufs that lack one. */ void IMB_rect_from_float(struct ImBuf *ibuf); +void IMB_float_from_rect_ex(struct ImBuf *dst, + const struct ImBuf *src, + const struct rcti *region_to_update); void IMB_float_from_rect(struct ImBuf *ibuf); /** * No profile conversion. @@ -897,16 +900,16 @@ typedef enum eIMBTransformMode { /** * \brief Transform source image buffer onto destination image buffer using a transform matrix. * - * \param src Image buffer to read from. - * \param dst Image buffer to write to. rect or rect_float must already be initialized. + * \param src: Image buffer to read from. + * \param dst: Image buffer to write to. rect or rect_float must already be initialized. * - dst buffer must be a 4 channel buffers. * - Only one data type buffer will be used (rect_float has priority over rect) - * \param mode Cropping/Wrap repeat effect to apply during transformation. - * \param filter Interpolation to use during sampling. - * \param transform_matrix Transformation matrix to use. + * \param mode: Cropping/Wrap repeat effect to apply during transformation. + * \param filter: Interpolation to use during sampling. + * \param transform_matrix: Transformation matrix to use. * The given matrix should transform between dst pixel space to src pixel space. * One unit is one pixel. - * \param src_crop cropping region how to crop the source buffer. Should only be passed when mode + * \param src_crop: Cropping region how to crop the source buffer. Should only be passed when mode * is set to #IMB_TRANSFORM_MODE_CROP_SRC. For any other mode this should be empty. * * During transformation no data/color conversion will happens. diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index bad2081448f..e99572adbb0 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -108,7 +108,7 @@ struct anim { #ifdef WITH_FFMPEG AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; - AVCodec *pCodec; + const AVCodec *pCodec; AVFrame *pFrame; int pFrameComplete; AVFrame *pFrameRGB; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 4125662d35f..f97a50ecf47 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -492,7 +492,7 @@ static int startffmpeg(struct anim *anim) { int i, video_stream_index; - AVCodec *pCodec; + const AVCodec *pCodec; AVFormatContext *pFormatCtx = NULL; AVCodecContext *pCodecCtx; AVRational frame_rate; @@ -867,6 +867,17 @@ static void ffmpeg_decode_store_frame_pts(struct anim *anim) (int64_t)anim->cur_pts); } +static int ffmpeg_read_video_frame(struct anim *anim, AVPacket *packet) +{ + int ret = 0; + while (ret = av_read_frame(anim->pFormatCtx, packet) >= 0) { + if (packet->stream_index == anim->videoStream) { + break; + } + } + return ret; +} + /* decode one video frame also considering the packet read into cur_packet */ static int ffmpeg_decode_video_frame(struct anim *anim) { @@ -887,7 +898,7 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->cur_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) { + while ((rval = ffmpeg_read_video_frame(anim, anim->cur_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", @@ -897,14 +908,13 @@ static int ffmpeg_decode_video_frame(struct anim *anim) (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts, (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts, (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->cur_packet->stream_index == anim->videoStream) { - avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); - anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; - if (anim->pFrameComplete) { - ffmpeg_decode_store_frame_pts(anim); - break; - } + avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; + + if (anim->pFrameComplete) { + ffmpeg_decode_store_frame_pts(anim); + break; } av_packet_unref(anim->cur_packet); anim->cur_packet->stream_index = -1; @@ -1159,13 +1169,59 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD); } +/* Read packet until timestamp matches `anim->cur_packet`, thus recovering internal `anim` stream + * position state. */ +static void ffmpeg_seek_recover_stream_position(struct anim *anim) +{ + AVPacket *temp_packet = av_packet_alloc(); + while (ffmpeg_read_video_frame(anim, temp_packet)) { + int64_t current_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts); + int64_t temp_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts); + av_packet_unref(temp_packet); + + if (current_pts == temp_pts) { + break; + } + } + av_packet_free(&temp_packet); +} + +/* Check if seeking and mainly flushing codec buffers is needed. */ +static bool ffmpeg_seek_buffers_need_flushing(struct anim *anim, int position, int64_t seek_pos) +{ + /* Get timestamp of packet read after seeking. */ + AVPacket *temp_packet = av_packet_alloc(); + ffmpeg_read_video_frame(anim, temp_packet); + int64_t gop_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts); + av_packet_unref(temp_packet); + av_packet_free(&temp_packet); + + /* Seeking gives packet, that is currently read. No seeking was necessary, so buffers don't have + * to be flushed. */ + if (gop_pts == timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts)) { + return false; + } + + /* Packet after seeking is same key frame as current, and further in time. No seeking was + * necessary, so buffers don't have to be flushed. But stream position has to be recovered. */ + if (gop_pts == anim->cur_key_frame_pts && position > anim->cur_position) { + ffmpeg_seek_recover_stream_position(anim); + return false; + } + + /* Seeking was necessary, but we have read packets. Therefore we must seek again. */ + av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD); + anim->cur_key_frame_pts = gop_pts; + return true; +} + /* Seek to last necessary key frame. */ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index, int64_t pts_to_search) { - int64_t pos; + int64_t seek_pos; int ret; if (tc_index) { @@ -1180,23 +1236,23 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, uint64_t pts; uint64_t dts; - pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); + seek_pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek seek_pos = %" PRId64 "\n", seek_pos); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); if (ffmpeg_seek_by_byte(anim->pFormatCtx)) { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n"); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE seek_pos\n"); - ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE); + ret = av_seek_frame(anim->pFormatCtx, -1, seek_pos, AVSEEK_FLAG_BYTE); } else { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n"); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS seek_pos\n"); ret = av_seek_frame( anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD); } @@ -1204,58 +1260,25 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, else { /* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from. */ - pos = ffmpeg_get_seek_pts(anim, pts_to_search); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); + seek_pos = ffmpeg_get_seek_pts(anim, pts_to_search); + av_log( + anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek seek_pos = %" PRId64 "\n", seek_pos); AVFormatContext *format_ctx = anim->pFormatCtx; if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) { - ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); + ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos); + ret = ffmpeg_generic_seek_workaround(anim, &seek_pos, pts_to_search); + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "Adjusted final seek seek_pos = %" PRId64 "\n", + seek_pos); } - if (ret >= 0) { - /* Double check if we need to seek and decode all packets. */ - AVPacket *current_gop_start_packet = av_packet_alloc(); - while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) { - if (current_gop_start_packet->stream_index == anim->videoStream) { - break; - } - av_packet_unref(current_gop_start_packet); - } - int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts, - current_gop_start_packet->dts); - - av_packet_free(¤t_gop_start_packet); - bool same_gop = gop_pts == anim->cur_key_frame_pts; - - if (same_gop && position > anim->cur_position) { - /* Change back to our old frame position so we can simply continue decoding from there. */ - int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts); - - if (cur_pts == gop_pts) { - /* We are already at the correct position. */ - return 0; - } - AVPacket *temp = av_packet_alloc(); - - while (av_read_frame(anim->pFormatCtx, temp) >= 0) { - int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts); - if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) { - break; - } - av_packet_unref(temp); - } - av_packet_free(&temp); - return 0; - } - - anim->cur_key_frame_pts = gop_pts; - /* Seek back so we are at the correct position after we decoded a frame. */ - av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); + if (ret <= 0 && !ffmpeg_seek_buffers_need_flushing(anim, position, seek_pos)) { + return 0; } } @@ -1265,7 +1288,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, "FETCH: " "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64 "): errcode = %d\n", - pos, + seek_pos, position, pts_to_search, ret); @@ -1290,7 +1313,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ return NULL; } - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: seek_pos=%d\n", position); struct anim_index *tc_index = IMB_anim_open_index(anim, tc); int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position); diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 0bf50937674..588c92d748d 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -6,6 +6,7 @@ */ #include "BLI_math.h" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "IMB_filter.h" @@ -752,6 +753,61 @@ void IMB_rect_from_float(ImBuf *ibuf) ibuf->userflags &= ~IB_RECT_INVALID; } +void IMB_float_from_rect_ex(struct ImBuf *dst, + const struct ImBuf *src, + const rcti *region_to_update) +{ + BLI_assert_msg(dst->rect_float != NULL, + "Destination buffer should have a float buffer assigned."); + BLI_assert_msg(src->rect != NULL, "Source buffer should have a byte buffer assigned."); + BLI_assert_msg(dst->x == src->x, "Source and destination buffer should have the same dimension"); + BLI_assert_msg(dst->y == src->y, "Source and destination buffer should have the same dimension"); + BLI_assert_msg(dst->channels = 4, "Destination buffer should have 4 channels."); + BLI_assert_msg(region_to_update->xmin >= 0, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->ymin >= 0, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->xmax <= dst->x, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->ymax <= dst->y, + "Region to update should be clipped to the given buffers."); + + float *rect_float = dst->rect_float; + rect_float += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; + unsigned char *rect = (unsigned char *)src->rect; + rect += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; + const int region_width = BLI_rcti_size_x(region_to_update); + const int region_height = BLI_rcti_size_y(region_to_update); + + /* Convert byte buffer to float buffer without color or alpha conversion. */ + IMB_buffer_float_from_byte(rect_float, + rect, + IB_PROFILE_SRGB, + IB_PROFILE_SRGB, + false, + region_width, + region_height, + src->x, + dst->x); + + /* Perform color space conversion from rect color space to linear. */ + float *float_ptr = rect_float; + for (int i = 0; i < region_height; i++) { + IMB_colormanagement_colorspace_to_scene_linear( + float_ptr, region_width, 1, dst->channels, src->rect_colorspace, false); + float_ptr += 4 * dst->x; + } + + /* Perform alpha conversion. */ + if (IMB_alpha_affects_rgb(src)) { + float_ptr = rect_float; + for (int i = 0; i < region_height; i++) { + IMB_premultiply_rect_float(float_ptr, dst->channels, region_width, 1); + float_ptr += 4 * dst->x; + } + } +} + void IMB_float_from_rect(ImBuf *ibuf) { float *rect_float; @@ -775,33 +831,14 @@ void IMB_float_from_rect(ImBuf *ibuf) } ibuf->channels = 4; - } - - /* first, create float buffer in non-linear space */ - IMB_buffer_float_from_byte(rect_float, - (unsigned char *)ibuf->rect, - IB_PROFILE_SRGB, - IB_PROFILE_SRGB, - false, - ibuf->x, - ibuf->y, - ibuf->x, - ibuf->x); - - /* then make float be in linear space */ - IMB_colormanagement_colorspace_to_scene_linear( - rect_float, ibuf->x, ibuf->y, ibuf->channels, ibuf->rect_colorspace, false); - - /* byte buffer is straight alpha, float should always be premul */ - if (IMB_alpha_affects_rgb(ibuf)) { - IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y); - } - - if (ibuf->rect_float == NULL) { ibuf->rect_float = rect_float; ibuf->mall |= IB_rectfloat; ibuf->flags |= IB_rectfloat; } + + rcti region_to_update; + BLI_rcti_init(®ion_to_update, 0, ibuf->x, 0, ibuf->y); + IMB_float_from_rect_ex(ibuf, ibuf, ®ion_to_update); } /** \} */ diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 2cf82ca5c48..c1e00642a6d 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -478,7 +478,7 @@ struct proxy_output_ctx { AVFormatContext *of; AVStream *st; AVCodecContext *c; - AVCodec *codec; + const AVCodec *codec; struct SwsContext *sws_ctx; AVFrame *frame; int cfra; @@ -510,12 +510,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->st = avformat_new_stream(rv->of, NULL); rv->st->id = 0; - rv->c = avcodec_alloc_context3(NULL); - rv->c->codec_type = AVMEDIA_TYPE_VIDEO; - rv->c->codec_id = AV_CODEC_ID_H264; + rv->codec = avcodec_find_encoder(AV_CODEC_ID_H264); - rv->of->oformat->video_codec = rv->c->codec_id; - rv->codec = avcodec_find_encoder(rv->c->codec_id); + rv->c = avcodec_alloc_context3(rv->codec); if (!rv->codec) { fprintf(stderr, @@ -527,8 +524,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( return NULL; } - avcodec_get_context_defaults3(rv->c, rv->codec); - rv->c->width = width; rv->c->height = height; rv->c->gop_size = 10; @@ -779,7 +774,7 @@ typedef struct FFmpegIndexBuilderContext { AVFormatContext *iFormatCtx; AVCodecContext *iCodecCtx; - AVCodec *iCodec; + const AVCodec *iCodec; AVStream *iStream; int videoStream; @@ -1167,7 +1162,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte } /* Assess scrubbing performance of provided file. This function is not meant to be very exact. - * It compares number number of frames decoded in reasonable time with largest detected GOP size. + * It compares number of frames decoded in reasonable time with largest detected GOP size. * Because seeking happens in single GOP, it means, that maximum seek time can be detected this * way. * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index a47009e3abd..418a4724c00 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -16,30 +16,46 @@ #include <stdexcept> #include <string> -#include <Iex.h> -#include <ImathBox.h> -#include <ImfArray.h> -#include <ImfChannelList.h> -#include <ImfCompression.h> -#include <ImfCompressionAttribute.h> -#include <ImfIO.h> -#include <ImfInputFile.h> -#include <ImfOutputFile.h> -#include <ImfPixelType.h> -#include <ImfStandardAttributes.h> -#include <ImfStringAttribute.h> -#include <ImfVersion.h> -#include <half.h> +/* The OpenEXR version can reliably be found in this header file from OpenEXR, + * for both 2.x and 3.x: + */ +#include <OpenEXR/OpenEXRConfig.h> +#define COMBINED_OPENEXR_VERSION \ + ((10000 * OPENEXR_VERSION_MAJOR) + (100 * OPENEXR_VERSION_MINOR) + OPENEXR_VERSION_PATCH) + +#if COMBINED_OPENEXR_VERSION >= 20599 +/* >=2.5.99 -> OpenEXR >=3.0 */ +# include <Imath/half.h> +# include <OpenEXR/ImfFrameBuffer.h> +# define exr_file_offset_t uint64_t +#else +/* OpenEXR 2.x, use the old locations. */ +# include <OpenEXR/half.h> +# define exr_file_offset_t Int64 +#endif + +#include <OpenEXR/Iex.h> +#include <OpenEXR/ImfArray.h> +#include <OpenEXR/ImfChannelList.h> +#include <OpenEXR/ImfCompression.h> +#include <OpenEXR/ImfCompressionAttribute.h> +#include <OpenEXR/ImfIO.h> +#include <OpenEXR/ImfInputFile.h> +#include <OpenEXR/ImfOutputFile.h> +#include <OpenEXR/ImfPixelType.h> +#include <OpenEXR/ImfStandardAttributes.h> +#include <OpenEXR/ImfStringAttribute.h> +#include <OpenEXR/ImfVersion.h> /* multiview/multipart */ -#include <ImfInputPart.h> -#include <ImfMultiPartInputFile.h> -#include <ImfMultiPartOutputFile.h> -#include <ImfMultiView.h> -#include <ImfOutputPart.h> -#include <ImfPartHelper.h> -#include <ImfPartType.h> -#include <ImfTiledOutputPart.h> +#include <OpenEXR/ImfInputPart.h> +#include <OpenEXR/ImfMultiPartInputFile.h> +#include <OpenEXR/ImfMultiPartOutputFile.h> +#include <OpenEXR/ImfMultiView.h> +#include <OpenEXR/ImfOutputPart.h> +#include <OpenEXR/ImfPartHelper.h> +#include <OpenEXR/ImfPartType.h> +#include <OpenEXR/ImfTiledOutputPart.h> #include "DNA_scene_types.h" /* For OpenEXR compression constants */ @@ -115,12 +131,12 @@ class IMemStream : public Imf::IStream { return false; } - Int64 tellg() override + exr_file_offset_t tellg() override { return _exrpos; } - void seekg(Int64 pos) override + void seekg(exr_file_offset_t pos) override { _exrpos = pos; } @@ -130,8 +146,8 @@ class IMemStream : public Imf::IStream { } private: - Int64 _exrpos; - Int64 _exrsize; + exr_file_offset_t _exrpos; + exr_file_offset_t _exrsize; unsigned char *_exrbuf; }; @@ -166,12 +182,12 @@ class IFileStream : public Imf::IStream { return check_error(); } - Int64 tellg() override + exr_file_offset_t tellg() override { return std::streamoff(ifs.tellg()); } - void seekg(Int64 pos) override + void seekg(exr_file_offset_t pos) override { ifs.seekg(pos); check_error(); @@ -215,19 +231,19 @@ class OMemStream : public OStream { ibuf->encodedsize += n; } - Int64 tellp() override + exr_file_offset_t tellp() override { return offset; } - void seekp(Int64 pos) override + void seekp(exr_file_offset_t pos) override { offset = pos; ensure_size(offset); } private: - void ensure_size(Int64 size) + void ensure_size(exr_file_offset_t size) { /* if buffer is too small increase it. */ while (size > ibuf->encodedbuffersize) { @@ -238,7 +254,7 @@ class OMemStream : public OStream { } ImBuf *ibuf; - Int64 offset; + exr_file_offset_t offset; }; /* File Output Stream */ @@ -268,12 +284,12 @@ class OFileStream : public OStream { check_error(); } - Int64 tellp() override + exr_file_offset_t tellp() override { return std::streamoff(ofs.tellp()); } - void seekp(Int64 pos) override + void seekp(exr_file_offset_t pos) override { ofs.seekp(pos); check_error(); diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index cabd6070400..241f1a736f4 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -250,7 +250,7 @@ static int isffmpeg(const char *filepath) AVFormatContext *pFormatCtx = NULL; unsigned int i; int videoStream; - AVCodec *pCodec; + const AVCodec *pCodec; if (BLI_path_extension_check_n(filepath, ".swf", diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index 4723b89de08..d33adfdb4b9 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -190,7 +190,7 @@ ABCAbstractWriter *ABCHierarchyIterator::create_data_writer_for_object_type( return new ABCMeshWriter(writer_args); case OB_CAMERA: return new ABCCameraWriter(writer_args); - case OB_CURVE: + case OB_CURVES_LEGACY: if (params_.curves_as_mesh) { return new ABCCurveMeshWriter(writer_args); } diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 67e9846030a..11693eeb4de 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -309,7 +309,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me } if (args_.export_params->orcos) { - write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config); + write_generated_coordinates(abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config); } if (!edge_crease_indices.empty()) { diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc index 5e307d59ed4..89ed7c5ce06 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -84,7 +84,7 @@ bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const { - return ELEM(context->object->type, OB_SURF, OB_CURVE); + return ELEM(context->object->type, OB_SURF, OB_CURVES_LEGACY); } static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots) diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index a6400dc6f6c..d8859acdf5f 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -66,7 +66,7 @@ bool AbcCurveReader::accepts_object_type( return false; } - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { *err_str = "Object type mismatch, Alembic object path points to Curves."; return false; } @@ -76,7 +76,7 @@ bool AbcCurveReader::accepts_object_type( void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); + Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVES_LEGACY); cu->flag |= CU_3D; cu->actvert = CU_ACT_NONE; @@ -91,7 +91,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele } } - m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); + m_object = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, m_object_name.c_str()); m_object->data = cu; read_curve_sample(cu, m_curves_schema, sample_sel); diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index b744106ff95..78f9c0a85b4 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -69,7 +69,7 @@ bool AbcNurbsReader::accepts_object_type( return false; } - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { *err_str = "Object type mismatch, Alembic object path points to NURBS."; return false; } diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt index 34949da622a..3289a7c6e66 100644 --- a/source/blender/io/collada/CMakeLists.txt +++ b/source/blender/io/collada/CMakeLists.txt @@ -14,6 +14,10 @@ if(OPENCOLLADA_ANIMATION_CLIP) add_definitions(-DWITH_OPENCOLLADA_ANIMATION_CLIP) endif() +# In cmake version 3.21 and up, we can instead use the NO_CACHE option for +# find_file so we don't need to clear it from the cache here. +unset(OPENCOLLADA_ANIMATION_CLIP CACHE) + set(INC . ../../blenkernel diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index 9d4311bd693..9379e72bdd9 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -158,7 +158,7 @@ void GpencilIO::create_object_list() float zdepth = 0; if (rv3d_) { if (rv3d_->is_persp) { - zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3], nullptr); + zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3]); } else { zdepth = -dot_v3v3(rv3d_->viewinv[2], object->obmat[3]); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 3c4676beb75..cc3eab02e07 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -196,7 +196,7 @@ void GpencilExporterPDF::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index d21af4c09eb..f8d30546e39 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -221,7 +221,7 @@ void GpencilExporterSVG::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_path(gpl, gps_perimeter, node_gpl, false); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 1f384bdf127..6186e2bb61f 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -502,6 +502,9 @@ CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, const char *filename, ListBase *object_paths) { + /* Must call this so that USD file format plugins are loaded. */ + ensure_usd_plugin_path_registered(); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename); if (!stage) { diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index a86deb7c9f0..7a0d896fb3e 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -95,7 +95,7 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch break; case OB_EMPTY: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_SPEAKER: diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index e331030164e..0d3c2feb8f3 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -26,13 +26,13 @@ namespace blender::io::usd { void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */) { - curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY); curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; - object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str()); object_->data = curve_; } diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc index c7fd788e072..d0a5dc1c4b4 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.cc +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -43,13 +43,13 @@ namespace blender::io::usd { void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */) { - curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY); curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; - object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str()); object_->data = curve_; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index 7016dff2a29..3bee93b36a5 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -310,7 +310,7 @@ class FormatHandler : NonCopyable, NonMovable { /** * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`. * - * \param key Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for + * \param key: Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for * `eFileType::MTL`. */ template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T> diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 9b9c8483464..5b11c85b7a4 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -189,7 +189,7 @@ class OBJMesh : NonCopyable { } /** * Calculate a polygon's polygon/loop normal indices. - * \param poly_index Index of the polygon to calculate indices for. + * \param poly_index: Index of the polygon to calculate indices for. * \return Vector of normal indices, aligned with vertices of polygon. */ Vector<int> calc_poly_normal_indices(int poly_index) const; diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 9ea75e6eb4c..2cef5192337 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -93,7 +93,7 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par case OB_MESH: r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object)); break; - case OB_CURVE: { + case OB_CURVES_LEGACY: { Curve *curve = static_cast<Curve *>(object->data); Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)}; if (!nurb) { diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh index 9f9ec5a6083..676b1f3598c 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh @@ -64,11 +64,12 @@ void export_frame(Depsgraph *depsgraph, * Find the objects to be exported in the `view_layer` of the dependency graph`depsgraph`, * and return them in vectors `unique_ptr`s of `OBJMesh` and `OBJCurve`. * If `export_params.export_selected_objects` is set, then only selected objects are to be - * exported, else all objects are to be exported. But only objects of type `OB_MESH`, `OB_CURVE`, - * and `OB_SURF` are supported; the rest will be ignored. If `export_params.export_curves_as_nurbs` - * is set, then curves of type `CU_NURBS` are exported in curve form in the .obj file, otherwise - * they are converted to mesh and returned in the `OBJMesh` vector. All other exportable types are - * always converted to mesh and returned in the `OBJMesh` vector. + * exported, else all objects are to be exported. But only objects of type `OB_MESH`, + * `OB_CURVES_LEGACY`, and `OB_SURF` are supported; the rest will be ignored. If + * `export_params.export_curves_as_nurbs` is set, then curves of type `CU_NURBS` are exported in + * curve form in the .obj file, otherwise they are converted to mesh and returned in the `OBJMesh` + * vector. All other exportable types are always converted to mesh and returned in the `OBJMesh` + * vector. */ std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>> filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c045082d569..011f3618e15 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -330,7 +330,7 @@ enum { /* 2 characters for ID code and 64 for actual name */ #define MAX_ID_NAME 66 -/* ID_Runtime.remapping_status */ +/* ID_Runtime_Remap.status */ enum { /** new_id is directly linked in current .blend. */ ID_REMAP_IS_LINKED_DIRECT = 1 << 0, @@ -879,7 +879,7 @@ typedef enum IDRecalcFlag { #define FILTER_ID_AR (1ULL << 1) #define FILTER_ID_BR (1ULL << 2) #define FILTER_ID_CA (1ULL << 3) -#define FILTER_ID_CU (1ULL << 4) +#define FILTER_ID_CU_LEGACY (1ULL << 4) #define FILTER_ID_GD (1ULL << 5) #define FILTER_ID_GR (1ULL << 6) #define FILTER_ID_IM (1ULL << 7) @@ -912,12 +912,12 @@ typedef enum IDRecalcFlag { #define FILTER_ID_SIM (1ULL << 35) #define FILTER_ID_ALL \ - (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD | \ - FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | \ - FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \ - FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \ - FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \ - FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) + (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ + FILTER_ID_GD | FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | \ + FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \ + FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \ + FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \ + FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) /** * This enum defines the index assigned to each type of IDs in the array returned by @@ -998,7 +998,7 @@ enum { /* Object data types. */ INDEX_ID_AR, INDEX_ID_ME, - INDEX_ID_CU, + INDEX_ID_CU_LEGACY, INDEX_ID_MB, INDEX_ID_CV, INDEX_ID_PT, diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h index a6f950517eb..b0ca13615b8 100644 --- a/source/blender/makesdna/DNA_ID_enums.h +++ b/source/blender/makesdna/DNA_ID_enums.h @@ -40,46 +40,46 @@ enum eIconSizes { * and the first 2 bytes of #ID.name (for runtime checks, see #GS macro). */ typedef enum ID_Type { - ID_SCE = MAKE_ID2('S', 'C'), /* Scene */ - ID_LI = MAKE_ID2('L', 'I'), /* Library */ - ID_OB = MAKE_ID2('O', 'B'), /* Object */ - ID_ME = MAKE_ID2('M', 'E'), /* Mesh */ - ID_CU = MAKE_ID2('C', 'U'), /* Curve */ - ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */ - ID_MA = MAKE_ID2('M', 'A'), /* Material */ - ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */ - ID_IM = MAKE_ID2('I', 'M'), /* Image */ - ID_LT = MAKE_ID2('L', 'T'), /* Lattice */ - ID_LA = MAKE_ID2('L', 'A'), /* Light */ - ID_CA = MAKE_ID2('C', 'A'), /* Camera */ - ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */ - ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */ - ID_WO = MAKE_ID2('W', 'O'), /* World */ - ID_SCR = MAKE_ID2('S', 'R'), /* Screen */ - ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */ - ID_TXT = MAKE_ID2('T', 'X'), /* Text */ - ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ - ID_SO = MAKE_ID2('S', 'O'), /* Sound */ - ID_GR = MAKE_ID2('G', 'R'), /* Collection */ - ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ - ID_AC = MAKE_ID2('A', 'C'), /* bAction */ - ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ - ID_BR = MAKE_ID2('B', 'R'), /* Brush */ - ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */ - ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */ - ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */ - ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */ - ID_MSK = MAKE_ID2('M', 'S'), /* Mask */ - ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ - ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ - ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ - ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ - ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ - ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ - ID_CV = MAKE_ID2('C', 'V'), /* Curves */ - ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ - ID_VO = MAKE_ID2('V', 'O'), /* Volume */ - ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ + ID_SCE = MAKE_ID2('S', 'C'), /* Scene */ + ID_LI = MAKE_ID2('L', 'I'), /* Library */ + ID_OB = MAKE_ID2('O', 'B'), /* Object */ + ID_ME = MAKE_ID2('M', 'E'), /* Mesh */ + ID_CU_LEGACY = MAKE_ID2('C', 'U'), /* Curve. ID_CV should be used in the future (see T95355). */ + ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */ + ID_MA = MAKE_ID2('M', 'A'), /* Material */ + ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */ + ID_IM = MAKE_ID2('I', 'M'), /* Image */ + ID_LT = MAKE_ID2('L', 'T'), /* Lattice */ + ID_LA = MAKE_ID2('L', 'A'), /* Light */ + ID_CA = MAKE_ID2('C', 'A'), /* Camera */ + ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */ + ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */ + ID_WO = MAKE_ID2('W', 'O'), /* World */ + ID_SCR = MAKE_ID2('S', 'R'), /* Screen */ + ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */ + ID_TXT = MAKE_ID2('T', 'X'), /* Text */ + ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ + ID_SO = MAKE_ID2('S', 'O'), /* Sound */ + ID_GR = MAKE_ID2('G', 'R'), /* Collection */ + ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ + ID_AC = MAKE_ID2('A', 'C'), /* bAction */ + ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ + ID_BR = MAKE_ID2('B', 'R'), /* Brush */ + ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */ + ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */ + ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */ + ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */ + ID_MSK = MAKE_ID2('M', 'S'), /* Mask */ + ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ + ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ + ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ + ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ + ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ + ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ + ID_CV = MAKE_ID2('C', 'V'), /* Curves */ + ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ + ID_VO = MAKE_ID2('V', 'O'), /* Volume */ + ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked data-blocks. */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 2a6b86711ab..fa0898e6ea5 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -104,12 +104,14 @@ typedef struct bAnimVizSettings { short path_type; /** Number of frames between points indicated on the paths. */ short path_step; + /** #eMotionPath_Ranges. */ + short path_range; /** #eMotionPaths_ViewFlag. */ short path_viewflag; /** #eMotionPaths_BakeFlag. */ short path_bakeflag; - char _pad[6]; + char _pad[4]; /** Start and end frames of path-calculation range. */ int path_sf, path_ef; @@ -131,6 +133,14 @@ typedef enum eMotionPaths_Types { MOTIONPATH_TYPE_ACFRA = 1, } eMotionPath_Types; +/* bAnimVizSettings->path_range */ +typedef enum eMotionPath_Ranges { + /* Default is scene */ + MOTIONPATH_RANGE_SCENE = 0, + MOTIONPATH_RANGE_KEYS_SELECTED = 1, + MOTIONPATH_RANGE_KEYS_ALL = 2, +} eMotionPath_Ranges; + /* bAnimVizSettings->path_viewflag */ typedef enum eMotionPaths_ViewFlag { /* show frames on path */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 0667e508a82..bca177f6d7a 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -455,6 +455,12 @@ typedef enum eBrushUVSculptTool { UV_SCULPT_TOOL_PINCH = 2, } eBrushUVSculptTool; +/* Brush.curves_sculpt_tool. */ +typedef enum eBrushCurvesSculptTool { + CURVES_SCULPT_TOOL_TEST1 = 0, + CURVES_SCULPT_TOOL_TEST2 = 1, +} eBrushCurvesSculptTool; + /** When #BRUSH_ACCUMULATE is used */ #define SCULPT_TOOL_HAS_ACCUMULATE(t) \ ELEM(t, \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 77c49393029..fe80220b1dd 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -256,7 +256,9 @@ typedef struct Brush { char gpencil_sculpt_tool; /** Active grease pencil weight tool. */ char gpencil_weight_tool; - char _pad1[6]; + /** Active curves sculpt tool. */ + char curves_sculpt_tool; + char _pad1[5]; float autosmooth_factor; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 14a06f197bf..66206bcfddd 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -306,14 +306,6 @@ enum { CU_AUTOSPACE_EVALUATED = 2, }; -#if 0 /* Moved to overlay options in 2.8 */ -/* Curve.drawflag */ -enum { - CU_HIDE_HANDLES = 1 << 0, - CU_HIDE_NORMALS = 1 << 1, -}; -#endif - /* Curve.flag */ enum { CU_3D = 1 << 0, diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index 03a587c896b..98d2aa4b295 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -13,6 +13,33 @@ extern "C" { #endif +#ifdef __cplusplus +namespace blender::bke { +class CurvesGeometryRuntime; +} // namespace blender::bke +using CurvesGeometryRuntimeHandle = blender::bke::CurvesGeometryRuntime; +#else +typedef struct CurvesGeometryRuntimeHandle CurvesGeometryRuntimeHandle; +#endif + +typedef enum CurveType { + CURVE_TYPE_CATMULL_ROM = 0, + CURVE_TYPE_POLY = 1, + CURVE_TYPE_BEZIER = 2, + CURVE_TYPE_NURBS = 3, +} CurveType; + +typedef enum HandleType { + /** The handle can be moved anywhere, and doesn't influence the point's other handle. */ + BEZIER_HANDLE_FREE = 0, + /** The location is automatically calculated to be smooth. */ + BEZIER_HANDLE_AUTO = 1, + /** The location is calculated to point to the next/previous control point. */ + BEZIER_HANDLE_VECTOR = 2, + /** The location is constrained to point in the opposite direction as the other handle. */ + BEZIER_HANDLE_ALIGN = 3, +} HandleType; + /** * A reusable data structure for geometry consisting of many curves. All control point data is * stored contiguously for better efficiency. Data for each curve is stored as a slice of the @@ -34,13 +61,19 @@ typedef struct CurvesGeometry { float *radius; /** + * The type of each curve. #CurveType. + * \note This data is owned by #curve_data. + */ + int8_t *curve_type; + + /** * The start index of each curve in the point data. The size of each curve can be calculated by * subtracting the offset from the next offset. That is valid even for the last curve because * this array is allocated with a length one larger than the number of splines. * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ - int *offsets; + int *curve_offsets; /** * All attributes stored on control points (#ATTR_DOMAIN_POINT). @@ -60,6 +93,11 @@ typedef struct CurvesGeometry { * The number of curves in the data-block. */ int curve_size; + + /** + * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. + */ + CurvesGeometryRuntimeHandle *runtime; } CurvesGeometry; typedef struct Curves { @@ -77,6 +115,15 @@ typedef struct Curves { short totcol; short _pad2[3]; + /** + * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes. + * When set, the curves will have attributes that indicate a position on this surface. This is + * used for deforming the curves when the surface is deformed dynamically. + * + * This is expected to be a mesh object. + */ + struct Object *surface; + /* Draw Cache. */ void *batch_cache; } Curves; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 23406033126..2f9f63cb966 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -98,6 +98,10 @@ typedef enum CustomDataType { CD_MTFACE = 5, CD_MCOL = 6, CD_ORIGINDEX = 7, + /** + * Used for derived face corner normals on mesh `ldata`, since currently they are not computed + * lazily. Derived vertex and polygon normals are stored in #Mesh_Runtime. + */ CD_NORMAL = 8, CD_FACEMAP = 9, /* exclusive face group, each face can only be part of one */ CD_PROP_FLOAT = 10, diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 953a15f7502..11780d99af8 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -611,7 +611,7 @@ typedef struct FluidDomainSettings { /* Fluid guiding options. */ float guide_alpha; /* Guiding weight scalar (determines strength). */ - int guide_beta; /* Guiding blur radius (affects size of vortices vortices). */ + int guide_beta; /* Guiding blur radius (affects size of vortices). */ float guide_vel_factor; /* Multiply guiding velocity by this factor. */ int guide_res[3]; /* Res for velocity guide grids - independent from base res. */ short guide_source; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 64099de5a5e..e30dd4e1c8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -649,9 +649,10 @@ typedef struct SimplifyGpencilModifierData { int layer_pass; /** Sample length */ float length; + /** Sample sharp threshold */ + float sharp_threshold; /** Merge distance */ float distance; - char _pad[4]; } SimplifyGpencilModifierData; typedef enum eSimplifyGpencil_Flag { diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h index f40fb55e72e..70ee7c99d01 100644 --- a/source/blender/makesdna/DNA_ipo_types.h +++ b/source/blender/makesdna/DNA_ipo_types.h @@ -272,7 +272,7 @@ typedef struct Ipo { #define SEQ_FAC_SPEED 2 #define SEQ_FAC_OPACITY 3 -/* ********* Curve (ID_CU) *********** */ +/* ********* Curve (ID_CU_LEGACY) *********** */ #define CU_TOTIPO 1 #define CU_TOTNAM 1 diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index c566ad93335..d8a853681fd 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -116,27 +116,25 @@ typedef struct Mesh_Runtime { */ char wrapper_type_finalize; + int subsurf_resolution; /** * Settings for lazily evaluating the subdivision on the CPU if needed. These are * set in the modifier when GPU subdivision can be performed. */ char subsurf_apply_render; char subsurf_use_optimal_display; - char _pad[2]; - int subsurf_resolution; - - void *_pad2; /** - * Used to mark when derived data needs to be recalculated for a certain layer. - * Currently only normals. + * Caches for lazily computed vertex and polygon normals. These are stored here rather than in + * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a + * const mesh is not thread-safe. */ + char vert_normals_dirty; + char poly_normals_dirty; + float (*vert_normals)[3]; + float (*poly_normals)[3]; - int64_t cd_dirty_vert; - int64_t cd_dirty_edge; - int64_t cd_dirty_loop; - int64_t cd_dirty_poly; - + void *_pad2; } Mesh_Runtime; typedef struct Mesh { diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index db5a3d69e4d..807a615f7f9 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1604,6 +1604,10 @@ enum { MOD_WVG_MIX_DIF = 6, /** Average of both weights. */ MOD_WVG_MIX_AVG = 7, + /** Minimum of both weights. */ + MOD_WVG_MIX_MIN = 8, + /** Maximum of both weights. */ + MOD_WVG_MIX_MAX = 9, }; /** #WeightVGMixModifierData.mix_set (what vertices to affect). */ @@ -1694,7 +1698,7 @@ enum { MOD_WVG_PROXIMITY_GEOM_FACES = (1 << 2), MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK = (1 << 3), MOD_WVG_PROXIMITY_INVERT_FALLOFF = (1 << 4), - MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 3), + MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 5), }; /* Defines common to all WeightVG modifiers. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 963a34aa645..18b79a6fc25 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1610,6 +1610,11 @@ typedef struct NodeGeometryDeleteGeometry { int8_t mode; } NodeGeometryDeleteGeometry; +typedef struct NodeGeometryDuplicateElements { + /* AttributeDomain. */ + int8_t domain; +} NodeGeometryDuplicateElements; + typedef struct NodeGeometrySeparateGeometry { /* AttributeDomain. */ int8_t domain; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 96c60fcac97..9e0bf7dcc5a 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -471,7 +471,8 @@ typedef struct ObHook { enum { OB_EMPTY = 0, OB_MESH = 1, - OB_CURVE = 2, + /** Curve object is still used but replaced by "Curves" for the future (see T95355). */ + OB_CURVES_LEGACY = 2, OB_SURF = 3, OB_FONT = 4, OB_MBALL = 5, @@ -515,17 +516,26 @@ enum { OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) -#define OB_TYPE_SUPPORT_PARVERT(_type) (ELEM(_type, OB_MESH, OB_SURF, OB_CURVE, OB_LATTICE)) + (ELEM(_type, \ + OB_MESH, \ + OB_FONT, \ + OB_CURVES_LEGACY, \ + OB_SURF, \ + OB_MBALL, \ + OB_LATTICE, \ + OB_ARMATURE, \ + OB_CURVES)) +#define OB_TYPE_SUPPORT_PARVERT(_type) \ + (ELEM(_type, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_LATTICE)) /** Matches #OB_TYPE_SUPPORT_EDITMODE. */ -#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) +#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ (ELEM(_id_type, \ ID_ME, \ - ID_CU, \ + ID_CU_LEGACY, \ ID_MB, \ ID_LA, \ ID_SPK, \ @@ -540,7 +550,7 @@ enum { #define OB_DATA_SUPPORT_ID_CASE \ ID_ME: \ - case ID_CU: \ + case ID_CU_LEGACY: \ case ID_MB: \ case ID_LA: \ case ID_SPK: \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 0d42abdb363..daecb3d7a31 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -141,7 +141,6 @@ typedef struct FFMpegCodecData { int audio_bitrate; int audio_mixrate; int audio_channels; - char _pad0[4]; float audio_volume; int gop_size; /** Only used if FFMPEG_USE_MAX_B_FRAMES flag is set. */ @@ -156,9 +155,7 @@ typedef struct FFMpegCodecData { int rc_buffer_size; int mux_packet_size; int mux_rate; - char _pad1[4]; - - IDProperty *properties; + void *_pad1; } FFMpegCodecData; /* ************************************************************* */ @@ -995,6 +992,10 @@ typedef struct Sculpt { struct Object *gravity_object; } Sculpt; +typedef struct CurvesSculpt { + Paint paint; +} CurvesSculpt; + typedef struct UvSculpt { Paint paint; } UvSculpt; @@ -1380,6 +1381,8 @@ typedef struct ToolSettings { GpSculptPaint *gp_sculptpaint; /** Gpencil weight paint. */ GpWeightPaint *gp_weightpaint; + /** Curves sculpt. */ + CurvesSculpt *curves_sculpt; /* Vertex group weight - used only for editmode, not weight * paint */ @@ -1988,7 +1991,7 @@ extern const char *RE_engine_id_CYCLES; ((v3d == NULL) || (((1 << (base)->object->type) & (v3d)->object_type_exclude_select) == 0)) && \ (((base)->flag & BASE_SELECTABLE) != 0)) #define BASE_SELECTED(v3d, base) (BASE_VISIBLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0)) -#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && ((base)->object->id.lib == NULL)) +#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && !ID_IS_LINKED((base)->object)) #define BASE_SELECTED_EDITABLE(v3d, base) \ (BASE_EDITABLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0)) diff --git a/source/blender/makesdna/DNA_userdef_enums.h b/source/blender/makesdna/DNA_userdef_enums.h index bb061e73c9c..e90aa0e0f07 100644 --- a/source/blender/makesdna/DNA_userdef_enums.h +++ b/source/blender/makesdna/DNA_userdef_enums.h @@ -10,7 +10,14 @@ extern "C" { #endif -/** #UserDef.dupflag */ +/** + * #UserDef.dupflag + * + * The flag tells #BKE_object_duplicate() whether to copy data linked to the object, + * or to reference the existing data. + * #U.dupflag should be used for default operations or you can construct a flag as Python does. + * If #eDupli_ID_Flags is 0 then no data will be copied (linked duplicate). + */ typedef enum eDupli_ID_Flags { USER_DUP_MESH = (1 << 0), USER_DUP_CURVE = (1 << 1), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e081be73a1c..80a107e4bae 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1318,6 +1318,7 @@ typedef enum eNdof_Flag { NDOF_PANY_INVERT_AXIS = (1 << 13), NDOF_PANZ_INVERT_AXIS = (1 << 14), NDOF_TURNTABLE = (1 << 15), + NDOF_CAMERA_PAN_ZOOM = (1 << 16), } eNdof_Flag; #define NDOF_PIXELS_PER_SECOND 600.0f diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 626c2b2a81f..dabef04583b 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -295,9 +295,6 @@ typedef struct wmWindow { /** Storage for event system. */ struct wmEvent *eventstate; - /** Internal for wm_operators.c. */ - struct wmGesture *tweak; - /* Input Method Editor data - complex character input (especially for Asian character input) * Currently WIN32 and APPLE, runtime-only data. */ struct wmIMEData *ime_data; @@ -364,7 +361,9 @@ typedef struct wmKeyMapItem { /** Event code itself. */ short type; /** KM_ANY, KM_PRESS, KM_NOTHING etc. */ - short val; + int8_t val; + /** Use when `val == WM_CLICK_DRAG`, */ + int8_t direction; /** `oskey` also known as apple, windows-key or super, value denotes order of pressed. */ short shift, ctrl, alt, oskey; /** Raw-key modifier. */ @@ -422,7 +421,7 @@ enum { enum { KMI_TYPE_KEYBOARD = 0, KMI_TYPE_MOUSE = 1, - KMI_TYPE_TWEAK = 2, + /* 2 is deprecated, was tweak. */ KMI_TYPE_TEXTINPUT = 3, KMI_TYPE_TIMER = 4, KMI_TYPE_NDOF = 5, diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index bf77339a494..09eab0d7bf7 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -106,8 +106,23 @@ typedef enum eXrPoseFlag { XR_POSE_AIM = (1 << 1), } eXrPoseFlag; +/** + * The following user and component path lengths are dependent on OpenXR's XR_MAX_PATH_LENGTH + * (256). A user path will be combined with a component path to identify an action binding, and + * that combined path should also have a max of XR_MAX_PATH_LENGTH (e.g. user_path = + * /user/hand/left, component_path = /input/trigger/value, full_path = + * /user/hand/left/input/trigger/value). + */ +#define XR_MAX_USER_PATH_LENGTH 64 +#define XR_MAX_COMPONENT_PATH_LENGTH 192 + /* -------------------------------------------------------------------- */ +typedef struct XrComponentPath { + struct XrComponentPath *next, *prev; + char path[192]; /* XR_MAX_COMPONENT_PATH_LENGTH */ +} XrComponentPath; + typedef struct XrActionMapBinding { struct XrActionMapBinding *next, *prev; @@ -117,8 +132,7 @@ typedef struct XrActionMapBinding { /** OpenXR interaction profile path. */ char profile[256]; /** OpenXR component paths. */ - char component_path0[192]; - char component_path1[192]; + ListBase component_paths; /* XrComponentPath */ /** Input threshold/region. */ float float_threshold; @@ -132,6 +146,11 @@ typedef struct XrActionMapBinding { /* -------------------------------------------------------------------- */ +typedef struct XrUserPath { + struct XrUserPath *next, *prev; + char path[64]; /* XR_MAX_USER_PATH_LENGTH */ +} XrUserPath; + typedef struct XrActionMapItem { struct XrActionMapItem *next, *prev; @@ -142,8 +161,7 @@ typedef struct XrActionMapItem { char _pad[7]; /** OpenXR user paths. */ - char user_path0[64]; - char user_path1[64]; + ListBase user_paths; /* XrUserPath */ /** Operator to be called on XR events. */ char op[64]; /* OP_MAX_TYPENAME */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index eb25733a88a..bc4e7314512 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -997,7 +997,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char * /** * Same as RNA_property_editable(), except this checks individual items in an array. */ -bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index); +bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index); /** * Without lib check, only checks the flag. diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 4b68416f5d7..31db8e39e6f 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -87,10 +87,11 @@ DEF_ENUM(rna_enum_keying_flag_items_api) DEF_ENUM(rna_enum_fmodifier_type_items) DEF_ENUM(rna_enum_motionpath_bake_location_items) +DEF_ENUM(rna_enum_motionpath_display_type_items) +DEF_ENUM(rna_enum_motionpath_range_items) -DEF_ENUM(rna_enum_event_value_all_items) -DEF_ENUM(rna_enum_event_value_keymouse_items) -DEF_ENUM(rna_enum_event_value_tweak_items) +DEF_ENUM(rna_enum_event_value_items) +DEF_ENUM(rna_enum_event_direction_items) DEF_ENUM(rna_enum_event_type_items) DEF_ENUM(rna_enum_event_type_mask_items) @@ -107,6 +108,7 @@ DEF_ENUM(rna_enum_brush_gpencil_types_items) DEF_ENUM(rna_enum_brush_gpencil_vertex_types_items) DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items) DEF_ENUM(rna_enum_brush_gpencil_weight_types_items) +DEF_ENUM(rna_enum_brush_curves_sculpt_tool_items); DEF_ENUM(rna_enum_brush_image_tool_items) DEF_ENUM(rna_enum_axis_xy_items) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 90e979e9fbe..94ffa330064 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -35,7 +35,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = { {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, - {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""}, + {ID_CU_LEGACY, "CURVE", ICON_CURVE_DATA, "Curve", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, {ID_GR, "COLLECTION", ICON_OUTLINER_COLLECTION, "Collection", ""}, @@ -126,7 +126,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"}, {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"}, {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"}, - {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, + {FILTER_ID_CU_LEGACY, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, {FILTER_ID_GD, "filter_grease_pencil", ICON_GREASEPENCIL, @@ -348,7 +348,7 @@ short RNA_type_to_ID_code(const StructRNA *type) return ID_CA; } if (base_type == &RNA_Curve) { - return ID_CU; + return ID_CU_LEGACY; } if (base_type == &RNA_GreasePencil) { return ID_GD; @@ -472,7 +472,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) return &RNA_Camera; case ID_CF: return &RNA_CacheFile; - case ID_CU: + case ID_CU_LEGACY: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; @@ -696,7 +696,7 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) } static ID *rna_ID_override_hierarchy_create( - ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference) + ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_instance_hint) { if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { return NULL; @@ -706,7 +706,7 @@ static ID *rna_ID_override_hierarchy_create( ID *id_root_override = NULL; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, id_reference, &id_root_override); + bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); @@ -2057,7 +2057,7 @@ static void rna_def_ID(BlenderRNA *brna) "reference", "ID", "", - "Another ID (usually an Object or Collection) used to decide where to " + "Another ID (usually an Object or Collection) used as a hint to decide where to " "instantiate the new overrides"); func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create"); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 6c3b46c4408..7ccb8181b51 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1898,59 +1898,63 @@ int RNA_property_ui_icon(const PropertyRNA *prop) return rna_ensure_property((PropertyRNA *)prop)->icon; } -bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig) +static bool rna_property_editable_do(PointerRNA *ptr, + PropertyRNA *prop_orig, + const int index, + const char **r_info) { ID *id = ptr->owner_id; - int flag; - const char *dummy_info; PropertyRNA *prop = rna_ensure_property(prop_orig); - flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag; - - return ( - (flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 && - (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) && - (!ID_IS_OVERRIDE_LIBRARY(id) || RNA_property_overridable_get(ptr, prop_orig))))); -} - -bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info) -{ - ID *id = ptr->owner_id; - int flag; - - PropertyRNA *prop_type = rna_ensure_property(prop); - *r_info = ""; - /* get flag */ - if (prop_type->editable) { - flag = prop_type->editable(ptr, r_info); + const char *info = ""; + const int flag = (prop->itemeditable != NULL && index >= 0) ? + prop->itemeditable(ptr, index) : + (prop->editable != NULL ? prop->editable(ptr, &info) : prop->flag); + if (r_info != NULL) { + *r_info = info; } - else { - flag = prop_type->flag; - if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) { + + /* Early return if the property itself is not editable. */ + if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER) != 0) { + if (r_info != NULL && (*r_info)[0] == '\0') { *r_info = N_("This property is for internal use only and can't be edited"); } + return false; } - /* property from linked data-block */ - if (id) { - if (ID_IS_LINKED(id) && (prop_type->flag & PROP_LIB_EXCEPTION) == 0) { - if (!(*r_info)[0]) { - *r_info = N_("Can't edit this property from a linked data-block"); - } - return false; + /* If there is no owning ID, the property is editable at this point. */ + if (id == NULL) { + return true; + } + + /* Handle linked or liboverride ID cases. */ + const bool is_linked_prop_exception = (prop->flag & PROP_LIB_EXCEPTION) != 0; + if (ID_IS_LINKED(id) && !is_linked_prop_exception) { + if (r_info != NULL && (*r_info)[0] == '\0') { + *r_info = N_("Can't edit this property from a linked data-block"); } - if (ID_IS_OVERRIDE_LIBRARY(id)) { - if (!RNA_property_overridable_get(ptr, prop)) { - if (!(*r_info)[0]) { - *r_info = N_("Can't edit this property from an override data-block"); - } - return false; - } + return false; + } + if (ID_IS_OVERRIDE_LIBRARY(id) && !RNA_property_overridable_get(ptr, prop_orig)) { + if (r_info != NULL && (*r_info)[0] == '\0') { + *r_info = N_("Can't edit this property from an override data-block"); } + return false; } - return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0); + /* At this point, property is owned by a local ID and therefore fully editable. */ + return true; +} + +bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop) +{ + return rna_property_editable_do(ptr, prop, -1, NULL); +} + +bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info) +{ + return rna_property_editable_do(ptr, prop, -1, r_info); } bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop) @@ -1963,29 +1967,11 @@ bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop) return (flag & PROP_EDITABLE) != 0; } -bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index) +bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index) { - ID *id; - int flag; - BLI_assert(index >= 0); - prop = rna_ensure_property(prop); - - flag = prop->flag; - - if (prop->editable) { - const char *dummy_info; - flag &= prop->editable(ptr, &dummy_info); - } - - if (prop->itemeditable) { - flag &= prop->itemeditable(ptr, index); - } - - id = ptr->owner_id; - - return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)); + return rna_property_editable_do(ptr, prop, index, NULL); } bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop) @@ -4918,7 +4904,7 @@ static char *rna_path_token_in_brackets(const char **path, } /** - * \return true when when the key in the path is correctly parsed and found in the collection + * \return true when the key in the path is correctly parsed and found in the collection * or when the path is empty. */ static bool rna_path_parse_collection_key(const char **path, diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 1aa51c93ee8..fd65cc464f2 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -218,7 +218,7 @@ bool RNA_property_copy( /* IDprops: destination may not exist, if source does and is set, try to create it. */ /* NOTE: this is sort of quick hack/bandage to fix the issue, - * we need to rethink how IDProps are handled in 'diff' RNA code completely, imho... */ + * we need to rethink how IDProps are handled in 'diff' RNA code completely, IMHO. */ if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) { BLI_assert(prop_src->magic != RNA_MAGIC); IDProperty *idp_dst = RNA_struct_idprops(ptr, true); diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c index cb993931296..0453b327b45 100644 --- a/source/blender/makesrna/intern/rna_animviz.c +++ b/source/blender/makesrna/intern/rna_animviz.c @@ -36,6 +36,27 @@ const EnumPropertyItem rna_enum_motionpath_bake_location_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_motionpath_display_type_items[] = { + {MOTIONPATH_TYPE_ACFRA, + "CURRENT_FRAME", + 0, + "Around Frame", + "Display Paths of poses within a fixed number of frames around the current frame"}, + {MOTIONPATH_TYPE_RANGE, + "RANGE", + 0, + "In Range", + "Display Paths of poses within specified range"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_motionpath_range_items[] = { + {MOTIONPATH_RANGE_KEYS_ALL, "KEYS_ALL", 0, "All keys range", ""}, + {MOTIONPATH_RANGE_KEYS_SELECTED, "KEYS_SELECTED", 0, "Selected keys range", ""}, + {MOTIONPATH_RANGE_SCENE, "SCENE", 0, "Scene frame range", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME static PointerRNA rna_AnimViz_motion_paths_get(PointerRNA *ptr) @@ -173,20 +194,6 @@ static void rna_def_animviz_paths(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem prop_type_items[] = { - {MOTIONPATH_TYPE_ACFRA, - "CURRENT_FRAME", - 0, - "Around Frame", - "Display Paths of poses within a fixed number of frames around the current frame"}, - {MOTIONPATH_TYPE_RANGE, - "RANGE", - 0, - "In Range", - "Display Paths of poses within specified range"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "AnimVizMotionPaths", NULL); RNA_def_struct_sdna(srna, "bAnimVizSettings"); RNA_def_struct_nested(brna, srna, "AnimViz"); @@ -198,10 +205,16 @@ static void rna_def_animviz_paths(BlenderRNA *brna) /* Enums */ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "path_type"); - RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_enum_items(prop, rna_enum_motionpath_display_type_items); RNA_def_property_ui_text(prop, "Paths Type", "Type of range to show for Motion Paths"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + prop = RNA_def_property(srna, "range", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "path_range"); + RNA_def_property_enum_items(prop, rna_enum_motionpath_range_items); + RNA_def_property_ui_text(prop, "Paths Range", "Type of range to calculate for Motion Paths"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + prop = RNA_def_property(srna, "bake_location", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "path_bakeflag"); RNA_def_property_enum_items(prop, rna_enum_motionpath_bake_location_items); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f3c7d2747ac..667114a3598 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -243,6 +243,12 @@ const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { + {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, + {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = { {GP_BRUSH_ERASER_SOFT, @@ -2312,6 +2318,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grease Pencil Weight Paint Tool", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "curves_sculpt_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_brush_curves_sculpt_tool_items); + RNA_def_property_ui_text(prop, "Curves Sculpt Tool", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + /** End per mode tool properties. */ prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index edcf121a8c4..114fe30acf7 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -2969,7 +2969,7 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem splineik_xz_scale_mode[] = { - {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes (Default)"}, + {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes"}, {CONSTRAINT_SPLINEIK_XZS_ORIGINAL, "BONE_ORIGINAL", 0, diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index bc4bd9ad3d6..e723be2ab71 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -21,6 +21,7 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { {CTX_MODE_EDIT_MESH, "EDIT_MESH", 0, "Mesh Edit", ""}, {CTX_MODE_EDIT_CURVE, "EDIT_CURVE", 0, "Curve Edit", ""}, + {CTX_MODE_EDIT_CURVES, "EDIT_CURVES", 0, "Curves Edit", ""}, {CTX_MODE_EDIT_SURFACE, "EDIT_SURFACE", 0, "Surface Edit", ""}, {CTX_MODE_EDIT_TEXT, "EDIT_TEXT", 0, "Text Edit", ""}, /* PARSKEL reuse will give issues */ diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index b37a6e5fd0e..891de95a2a2 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -441,7 +441,7 @@ static void rna_Curve_bevelObject_set(PointerRNA *ptr, if (ob) { /* If bevel object has got the save curve, as object, for which it's set as bevobj, * there could be infinity loop in #DispList calculation. */ - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { cu->bevobj = ob; id_lib_extern((ID *)ob); } @@ -486,7 +486,7 @@ static bool rna_Curve_otherObject_poll(PointerRNA *ptr, PointerRNA value) Object *ob = (Object *)value.data; if (ob) { - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { return 1; } } @@ -516,7 +516,7 @@ static void rna_Curve_taperObject_set(PointerRNA *ptr, if (ob) { /* If taper object has got the save curve, as object, for which it's set as bevobj, * there could be infinity loop in #DispList calculation. */ - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { cu->taperobj = ob; id_lib_extern((ID *)ob); } diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 8e7fb415c7d..7a1a368551f 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -16,6 +16,8 @@ #include "BLI_math_base.h" #include "BLI_string.h" +#include "WM_types.h" + #ifdef RNA_RUNTIME # include "BLI_math_vector.h" @@ -43,7 +45,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, { const Curves *curves = rna_curves(ptr); rna_iterator_array_begin(iter, - (void *)curves->geometry.offsets, + (void *)curves->geometry.curve_offsets, sizeof(int), curves->geometry.curve_size + 1, false, @@ -95,7 +97,7 @@ static char *rna_CurvePoint_path(PointerRNA *ptr) static int rna_CurveSlice_index_get(PointerRNA *ptr) { Curves *curves = rna_curves(ptr); - return (int)((int *)ptr->data - curves->geometry.offsets); + return (int)((int *)ptr->data - curves->geometry.curve_offsets); } static char *rna_CurveSlice_path(PointerRNA *ptr) @@ -220,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna) /* Point and Curve RNA API helpers. */ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -243,7 +245,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(1); prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", NULL); RNA_def_property_struct_type(prop, "IntAttributeValue"); RNA_def_property_collection_funcs(prop, "rna_Curves_curve_offset_data_begin", @@ -265,6 +267,13 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_collection_funcs( prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int"); + prop = RNA_def_property(srna, "surface", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Mesh_object_poll"); + RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* attributes */ rna_def_attributes_common(srna); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 850accd0b24..5127b418b7f 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -4391,24 +4391,21 @@ void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropert if (tot == 0) { *items = MEM_callocN(sizeof(EnumPropertyItem[8]), __func__); + /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */ +#ifdef DEBUG + memset(*items, 0xff, sizeof(EnumPropertyItem[8])); +#endif } else if (tot >= 8 && (tot & (tot - 1)) == 0) { /* power of two > 8 */ *items = MEM_recallocN_id(*items, sizeof(EnumPropertyItem) * tot * 2, __func__); +#ifdef DEBUG + memset((*items) + tot, 0xff, sizeof(EnumPropertyItem) * tot); +#endif } (*items)[tot] = *item; *totitem = tot + 1; - - /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */ -#ifdef DEBUG - static const EnumPropertyItem item_error = { - -1, POINTER_FROM_INT(-1), -1, POINTER_FROM_INT(-1), POINTER_FROM_INT(-1)}; - if (item != &item_error) { - RNA_enum_item_add(items, totitem, &item_error); - *totitem -= 1; - } -#endif } void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem) diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index f24803af654..8f418fcc7c7 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1880,7 +1880,7 @@ static void rna_def_drivervar(BlenderRNA *brna) "SINGLE_PROP", ICON_RNA, "Single Property", - "Use the value from some RNA property (Default)"}, + "Use the value from some RNA property"}, {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", ICON_DRIVER_TRANSFORM, diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 6a0e5156234..a619d179a33 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1223,6 +1223,14 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "sharp_threshold"); + RNA_def_property_range(prop, 0, M_PI); + RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1); + RNA_def_property_ui_text( + prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + /* Merge */ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "distance"); @@ -2482,7 +2490,8 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_gphook_falloff_items); /* share the enum */ RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index e40fafd2069..56e23278176 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -103,6 +103,7 @@ static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), Pointe { Image *ima = (Image *)ptr->owner_id; BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_FREE); + BKE_image_partial_update_mark_full_update(ima); } static void rna_Image_colormanage_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -141,6 +142,7 @@ static void rna_Image_views_format_update(Main *bmain, Scene *scene, PointerRNA } BKE_image_release_ibuf(ima, ibuf, lock); + BKE_image_partial_update_mark_full_update(ima); } static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 2c5f264f7e9..44bf51d9770 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -649,7 +649,7 @@ const char *rna_translate_ui_text(const char *text, struct PropertyRNA *prop, bool translate); -/* Internal functions that cycles uses so we need to declare (tsk tsk) */ +/* Internal functions that cycles uses so we need to declare (tsk!). */ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values); #ifdef RNA_RUNTIME diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 51dcfd13657..7687dcbb11f 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -47,7 +47,7 @@ static Key *rna_ShapeKey_find_key(ID *id) { switch (GS(id->name)) { - case ID_CU: + case ID_CU_LEGACY: return ((Curve *)id)->key; case ID_KE: return (Key *)id; @@ -556,7 +556,7 @@ static void rna_ShapeKey_data_begin(CollectionPropertyIterator *iter, PointerRNA KeyBlock *kb = (KeyBlock *)ptr->data; int tot = kb->totelem, size = key->elemsize; - if (GS(key->from->name) == ID_CU && tot > 0) { + if (GS(key->from->name) == ID_CU_LEGACY && tot > 0) { Curve *cu = (Curve *)key->from; StructRNA *type = NULL; NurbInfo info = {0}; @@ -593,7 +593,7 @@ static int rna_ShapeKey_data_length(PointerRNA *ptr) KeyBlock *kb = (KeyBlock *)ptr->data; int tot = kb->totelem; - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { tot = rna_ShapeKey_curve_find_index(key, tot); } @@ -613,7 +613,7 @@ static PointerRNA rna_ShapeKey_data_get(CollectionPropertyIterator *iter) return rna_pointer_inherit_refine(&iter->parent, point->type, point->data); } - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { Curve *cu = (Curve *)key->from; type = rna_ShapeKey_curve_point_type(cu->nurb.first); @@ -635,7 +635,7 @@ int rna_ShapeKey_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) return false; } - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { NurbInfo info; rna_ShapeKey_NurbInfo_find_index(key, index, false, &info); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index e9d39d708a9..b239260c8eb 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -322,7 +322,7 @@ static Mesh *rna_Main_meshes_new_from_object(Main *bmain, { switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_MBALL: case OB_MESH: @@ -822,7 +822,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(screens, screens, ID_SCR) RNA_MAIN_ID_TAG_FUNCS_DEF(window_managers, wm, ID_WM) RNA_MAIN_ID_TAG_FUNCS_DEF(images, images, ID_IM) RNA_MAIN_ID_TAG_FUNCS_DEF(lattices, lattices, ID_LT) -RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU) +RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU_LEGACY) RNA_MAIN_ID_TAG_FUNCS_DEF(metaballs, metaballs, ID_MB) RNA_MAIN_ID_TAG_FUNCS_DEF(fonts, fonts, ID_VF) RNA_MAIN_ID_TAG_FUNCS_DEF(textures, textures, ID_TE) diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index e8b8c6e3274..2247a16a7a0 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -1025,7 +1025,8 @@ static void rna_def_mask_layer(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL); /* filling options */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 6b3ae373295..87459587a9e 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -456,6 +456,7 @@ static void rna_MeshPolygon_flip(ID *id, MPoly *mp) BKE_mesh_polygon_flip(mp, me->mloop, &me->ldata); BKE_mesh_tessface_clear(me); BKE_mesh_runtime_clear_geometry(me); + BKE_mesh_normals_tag_dirty(me); } static void rna_MeshLoopTriangle_verts_get(PointerRNA *ptr, int *values) diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index ebacced8b3f..8447074a3ef 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -167,7 +167,7 @@ static void rna_Mesh_flip_normals(Mesh *mesh) { BKE_mesh_polygons_flip(mesh->mpoly, mesh->mloop, &mesh->ldata, mesh->totpoly); BKE_mesh_tessface_clear(mesh); - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); BKE_mesh_runtime_clear_geometry(mesh); DEG_id_tag_update(&mesh->id, 0); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 45d22981205..b12b33c67af 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -869,10 +869,10 @@ static void modifier_object_set(Object *self, Object **ob_p, int type, PointerRN RNA_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_MOD_OBJECT_SET(Array, start_cap, OB_MESH); RNA_MOD_OBJECT_SET(Array, end_cap, OB_MESH); -RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVE); +RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVES_LEGACY); RNA_MOD_OBJECT_SET(Boolean, object, OB_MESH); RNA_MOD_OBJECT_SET(Cast, object, OB_EMPTY); -RNA_MOD_OBJECT_SET(Curve, object, OB_CURVE); +RNA_MOD_OBJECT_SET(Curve, object, OB_CURVES_LEGACY); RNA_MOD_OBJECT_SET(DataTransfer, ob_source, OB_MESH); RNA_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_MOD_OBJECT_SET(Mask, ob_arm, OB_ARMATURE); @@ -1849,7 +1849,8 @@ static void rna_def_modifier_warp(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_warp_falloff_items); RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); @@ -2567,7 +2568,8 @@ static void rna_def_modifier_hook(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_warp_falloff_items); /* share the enum */ RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); @@ -4947,6 +4949,7 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna) static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), StructRNA *srna, const char *mask_flags, + const int invert_vgroup_mask_flag, const char *mask_vgroup_setter, const char *mask_uvlayer_setter) { @@ -4992,7 +4995,7 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_mask_vertex_group", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, mask_flags, MOD_WVG_EDIT_INVERT_VGROUP_MASK); + RNA_def_property_boolean_sdna(prop, NULL, mask_flags, invert_vgroup_mask_flag); RNA_def_property_ui_text(prop, "Invert", "Invert vertex group mask influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -5076,7 +5079,8 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, weightvg_edit_falloff_type_items); RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE); @@ -5148,6 +5152,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "edit_flags", + MOD_WVG_EDIT_INVERT_VGROUP_MASK, "rna_WeightVGEditModifier_mask_defgrp_name_set", "rna_WeightVGEditModifier_mask_tex_uvlayer_name_set"); } @@ -5166,6 +5171,8 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna) "Difference", "Difference between VGroup A's and VGroup B's weights"}, {MOD_WVG_MIX_AVG, "AVG", 0, "Average", "Average value of VGroup A's and VGroup B's weights"}, + {MOD_WVG_MIX_MIN, "MIN", 0, "Minimum", "Minimum of VGroup A's and VGroup B's weights"}, + {MOD_WVG_MIX_MAX, "MAX", 0, "Maximum", "Maximum of VGroup A's and VGroup B's weights"}, {0, NULL, 0, NULL, NULL}, }; @@ -5263,6 +5270,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "flag", + MOD_WVG_MIX_INVERT_VGROUP_MASK, "rna_WeightVGMixModifier_mask_defgrp_name_set", "rna_WeightVGMixModifier_mask_tex_uvlayer_name_set"); } @@ -5364,7 +5372,8 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, weightvg_proximity_falloff_type_items); RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE); @@ -5392,6 +5401,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "proximity_flags", + MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK, "rna_WeightVGProximityModifier_mask_defgrp_name_set", "rna_WeightVGProximityModifier_mask_tex_uvlayer_name_set"); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3ae9ab3cec2..387166e77b4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -7196,7 +7196,8 @@ static void def_cmp_dilate_erode(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -8974,7 +8975,8 @@ static void def_cmp_keying(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "feather_falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Feather Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "feather_distance", PROP_INT, PROP_NONE); @@ -11285,6 +11287,27 @@ static void def_geo_delete_geometry(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_duplicate_elements(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""}, + {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", ""}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", ""}, + {ATTR_DOMAIN_CURVE, "SPLINE", 0, "Spline", ""}, + {ATTR_DOMAIN_INSTANCE, "INSTANCE", 0, "Instance", ""}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_struct_sdna_from(srna, "NodeGeometryDuplicateElements", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to duplicate"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_string_to_curves(StructRNA *srna) { static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = { diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 9c4ebf79a08..c598e63a32a 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -241,7 +241,7 @@ const EnumPropertyItem rna_enum_lightprobes_type_items[] = { /* used for 2 enums */ #define OBTYPE_CU_CURVE \ { \ - OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \ + OB_CURVES_LEGACY, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \ } #define OBTYPE_CU_SURF \ { \ @@ -479,7 +479,7 @@ static void rna_Object_active_shape_update(Main *bmain, Scene *UNUSED(scene), Po BKE_editmesh_looptri_and_normals_calc(em); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: ED_curve_editnurb_load(bmain, ob); ED_curve_editnurb_make(ob); @@ -571,7 +571,7 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, struct Report ob->data = id; BKE_object_materials_test(G_MAIN, ob, id); - if (GS(id->name) == ID_CU) { + if (GS(id->name) == ID_CU_LEGACY) { BKE_curve_type_test(ob); } else if (ob->type == OB_ARMATURE) { @@ -590,7 +590,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) return &RNA_Image; case OB_MESH: return &RNA_Mesh; - case OB_CURVE: + case OB_CURVES_LEGACY: return &RNA_Curve; case OB_SURF: return &RNA_Curve; @@ -2182,7 +2182,7 @@ bool rna_Lattice_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) bool rna_Curve_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { - return ((Object *)value.owner_id)->type == OB_CURVE; + return ((Object *)value.owner_id)->type == OB_CURVES_LEGACY; } bool rna_Armature_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 9676be69d05..ece1c5e5815 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -407,7 +407,7 @@ static Mesh *rna_Object_to_mesh(Object *object, * rna_Main_meshes_new_from_object. */ switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_MBALL: case OB_MESH: @@ -430,7 +430,7 @@ static Curve *rna_Object_to_curve(Object *object, Depsgraph *depsgraph, bool apply_modifiers) { - if (!ELEM(object->type, OB_FONT, OB_CURVE)) { + if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) { BKE_report(reports, RPT_ERROR, "Object is not a curve or a text"); return NULL; } @@ -785,7 +785,7 @@ bool rna_Object_generate_gpencil_strokes(Object *ob, float scale_thickness, float sample) { - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { BKE_reportf(reports, RPT_ERROR, "Object '%s' is not valid for this operation! Only curves are supported", diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 9ca78033199..8579f188428 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -691,7 +691,7 @@ static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, Point rna_FieldSettings_shape_update(bmain, scene, ptr); - if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE) { + if (ob->type == OB_CURVES_LEGACY && ob->pd->forcefield == PFIELD_GUIDE) { DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } else { @@ -899,7 +899,7 @@ static const EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), ob = (Object *)ptr->owner_id; - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { if (ob->pd->forcefield == PFIELD_VORTEX) { return curve_vortex_shape_items; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 178029ef340..29b06060256 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1466,18 +1466,6 @@ static void rna_FFmpegSettings_lossless_output_set(PointerRNA *ptr, bool value) else { rd->ffcodecdata.flags &= ~FFMPEG_LOSSLESS_OUTPUT; } - - BKE_ffmpeg_codec_settings_verify(rd); -} - -static void rna_FFmpegSettings_codec_settings_update(Main *UNUSED(bmain), - Scene *UNUSED(scene_unused), - PointerRNA *ptr) -{ - Scene *scene = (Scene *)ptr->owner_id; - RenderData *rd = &scene->r; - - BKE_ffmpeg_codec_settings_verify(rd); } # endif @@ -1659,7 +1647,7 @@ static void rna_Scene_mesh_quality_update(Main *bmain, Scene *UNUSED(scene), Poi Scene *scene = (Scene *)ptr->owner_id; FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_VOLUME, OB_MBALL)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_VOLUME, OB_MBALL)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } @@ -2910,6 +2898,10 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); + prop = RNA_def_property(srna, "curves_sculpt", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CurvesSculpt"); + RNA_def_property_ui_text(prop, "Curves Sculpt", ""); + prop = RNA_def_property(srna, "use_auto_normalize", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1); @@ -3072,7 +3064,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Proportional Editing Falloff", "Falloff type for proportional editing mode"); /* Abusing id_curve :/ */ - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ prop = RNA_def_property(srna, "proportional_size", PROP_FLOAT, PROP_DISTANCE); @@ -5727,8 +5719,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, ffmpeg_format_items); RNA_def_property_enum_default(prop, FFMPEG_MKV); RNA_def_property_ui_text(prop, "Container", "Output file container"); - RNA_def_property_update( - prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update"); prop = RNA_def_property(srna, "codec", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "codec"); @@ -5736,8 +5726,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, ffmpeg_codec_items); RNA_def_property_enum_default(prop, AV_CODEC_ID_H264); RNA_def_property_ui_text(prop, "Video Codec", "FFmpeg codec to use for video output"); - RNA_def_property_update( - prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update"); prop = RNA_def_property(srna, "video_bitrate", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "video_bitrate"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 1e7c67ef95e..473711fb74b 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -352,6 +352,12 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) } mode = OB_MODE_WEIGHT_GPENCIL; } + else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) { + if (slot_index != brush->curves_sculpt_tool) { + return false; + } + mode = OB_MODE_SCULPT_CURVES; + } return brush->ob_mode & mode; } @@ -417,6 +423,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.uv_sculpt"); } +static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.curves_sculpt"); +} + static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint"); @@ -1492,6 +1503,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } +static void rna_def_curves_sculpt(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "CurvesSculpt", "Paint"); + RNA_def_struct_path_func(srna, "rna_CurvesSculpt_path"); + RNA_def_struct_ui_text(srna, "Curves Sculpt Paint", ""); +} + void RNA_def_sculpt_paint(BlenderRNA *brna) { /* *** Non-Animated *** */ @@ -1510,6 +1530,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) rna_def_particle_edit(brna); rna_def_gpencil_guides(brna); rna_def_gpencil_sculpt(brna); + rna_def_curves_sculpt(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c51f0c00498..2344aa42838 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3263,8 +3263,8 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ ICON_OUTLINER_COLLECTION, "Objects & Collections", "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | - FILTER_ID_PT | FILTER_ID_VO, + {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | + FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", ICON_NODETREE, "Geometry", @@ -4959,7 +4959,9 @@ static void rna_def_space_view3d(BlenderRNA *brna) const char *identifier[2]; } info[] = { {"Mesh", (1 << OB_MESH), {"show_object_viewport_mesh", "show_object_select_mesh"}}, - {"Curve", (1 << OB_CURVE), {"show_object_viewport_curve", "show_object_select_curve"}}, + {"Curve", + (1 << OB_CURVES_LEGACY), + {"show_object_viewport_curve", "show_object_select_curve"}}, {"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}}, {"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}}, {"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}}, @@ -5467,7 +5469,7 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "show_cursor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR); - RNA_def_property_ui_text(prop, "2D cursor", ""); + RNA_def_property_ui_text(prop, "2D Cursor", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index e32bedf3ed0..2a759dde39a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6040,6 +6040,13 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Helicopter Mode", "Device up/down directly controls the Z position of the 3D viewport"); + prop = RNA_def_property(srna, "ndof_lock_camera_pan_zoom", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_CAMERA_PAN_ZOOM); + RNA_def_property_ui_text( + prop, + "Lock Camera Pan/Zoom", + "Pan/zoom the camera view instead of leaving the camera view when orbiting"); + /* let Python know whether NDOF is enabled */ prop = RNA_def_boolean(srna, "use_ndof", true, "", ""); # else diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index ca3b09c61c5..5b323629a80 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -26,7 +26,6 @@ const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, - {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b0021bd0f3d..8835591f303 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -28,13 +28,6 @@ #ifdef RNA_RUNTIME -static const EnumPropertyItem event_tweak_type_items[] = { - {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Left", ""}, - {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Middle", ""}, - {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Right", ""}, - {0, NULL, 0, NULL, NULL}, -}; - static const EnumPropertyItem event_mouse_type_items[] = { {LEFTMOUSE, "LEFTMOUSE", 0, "Left", ""}, {MIDDLEMOUSE, "MIDDLEMOUSE", 0, "Middle", ""}, @@ -156,10 +149,6 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", "WhIn"}, {WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, "Wheel Out", "WhOut"}, {0, "", 0, NULL, NULL}, - {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Tweak Left", "TwkL"}, - {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Tweak Middle", "TwkM"}, - {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Tweak Right", "TwkR"}, - {0, "", 0, NULL, NULL}, {EVT_AKEY, "A", 0, "A", ""}, {EVT_BKEY, "B", 0, "B", ""}, {EVT_CKEY, "C", 0, "C", ""}, @@ -362,26 +351,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { * This is needed for `km.keymap_items.new` value argument, * to accept values from different types. */ -const EnumPropertyItem rna_enum_event_value_all_items[] = { - {KM_ANY, "ANY", 0, "Any", ""}, - {KM_PRESS, "PRESS", 0, "Press", ""}, - {KM_RELEASE, "RELEASE", 0, "Release", ""}, - {KM_CLICK, "CLICK", 0, "Click", ""}, - {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""}, - {KM_CLICK_DRAG, "CLICK_DRAG", 0, "Click Drag", ""}, - {EVT_GESTURE_N, "NORTH", 0, "North", ""}, - {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""}, - {EVT_GESTURE_E, "EAST", 0, "East", ""}, - {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""}, - {EVT_GESTURE_S, "SOUTH", 0, "South", ""}, - {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""}, - {EVT_GESTURE_W, "WEST", 0, "West", ""}, - {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""}, - {KM_NOTHING, "NOTHING", 0, "Nothing", ""}, - {0, NULL, 0, NULL, NULL}, -}; - -const EnumPropertyItem rna_enum_event_value_keymouse_items[] = { +const EnumPropertyItem rna_enum_event_value_items[] = { {KM_ANY, "ANY", 0, "Any", ""}, {KM_PRESS, "PRESS", 0, "Press", ""}, {KM_RELEASE, "RELEASE", 0, "Release", ""}, @@ -393,16 +363,16 @@ const EnumPropertyItem rna_enum_event_value_keymouse_items[] = { {0, NULL, 0, NULL, NULL}, }; -const EnumPropertyItem rna_enum_event_value_tweak_items[] = { +const EnumPropertyItem rna_enum_event_direction_items[] = { {KM_ANY, "ANY", 0, "Any", ""}, - {EVT_GESTURE_N, "NORTH", 0, "North", ""}, - {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""}, - {EVT_GESTURE_E, "EAST", 0, "East", ""}, - {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""}, - {EVT_GESTURE_S, "SOUTH", 0, "South", ""}, - {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""}, - {EVT_GESTURE_W, "WEST", 0, "West", ""}, - {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""}, + {KM_DIRECTION_N, "NORTH", 0, "North", ""}, + {KM_DIRECTION_NE, "NORTH_EAST", 0, "North-East", ""}, + {KM_DIRECTION_E, "EAST", 0, "East", ""}, + {KM_DIRECTION_SE, "SOUTH_EAST", 0, "South-East", ""}, + {KM_DIRECTION_S, "SOUTH", 0, "South", ""}, + {KM_DIRECTION_SW, "SOUTH_WEST", 0, "South-West", ""}, + {KM_DIRECTION_W, "WEST", 0, "West", ""}, + {KM_DIRECTION_NW, "NORTH_WEST", 0, "North-West", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -420,7 +390,6 @@ const EnumPropertyItem rna_enum_event_type_mask_items[] = { {EVT_TYPE_MASK_MOUSE_BUTTON, "MOUSE_BUTTON", 0, "Mouse Button", ""}, {EVT_TYPE_MASK_MOUSE, "MOUSE", 0, "Mouse", ""}, {EVT_TYPE_MASK_NDOF, "NDOF", 0, "NDOF", ""}, - {EVT_TYPE_MASK_TWEAK, "TWEAK", 0, "Tweak", ""}, {EVT_TYPE_MASK_ACTIONZONE, "ACTIONZONE", 0, "Action Zone", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -612,18 +581,6 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr) return result; } -static const EnumPropertyItem *rna_Event_value_itemf(bContext *UNUSED(C), - PointerRNA *ptr, - PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) -{ - const wmEvent *event = ptr->data; - if (ISTWEAK(event->type)) { - return rna_enum_event_value_tweak_items; - } - return rna_enum_event_value_all_items; -} - static void rna_Event_ascii_get(PointerRNA *ptr, char *value) { const wmEvent *event = ptr->data; @@ -668,7 +625,7 @@ static int rna_Event_unicode_length(PointerRNA *ptr) static bool rna_Event_is_repeat_get(PointerRNA *ptr) { const wmEvent *event = ptr->data; - return event->is_repeat; + return (event->flag & WM_EVENT_IS_REPEAT) != 0; } static float rna_Event_pressure_get(PointerRNA *ptr) @@ -915,10 +872,6 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value) kmi->type = EVT_AKEY; kmi->val = KM_PRESS; break; - case KMI_TYPE_TWEAK: - kmi->type = EVT_TWEAK_L; - kmi->val = KM_ANY; - break; case KMI_TYPE_MOUSE: kmi->type = LEFTMOUSE; kmi->val = KM_PRESS; @@ -969,9 +922,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), if (map_type == KMI_TYPE_MOUSE) { return event_mouse_type_items; } - if (map_type == KMI_TYPE_TWEAK) { - return event_tweak_type_items; - } if (map_type == KMI_TYPE_TIMER) { return event_timer_type_items; } @@ -986,24 +936,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), } } -static const EnumPropertyItem *rna_KeyMapItem_value_itemf(bContext *UNUSED(C), - PointerRNA *ptr, - PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) -{ - int map_type = rna_wmKeyMapItem_map_type_get(ptr); - - if (map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD || map_type == KMI_TYPE_NDOF) { - return rna_enum_event_value_keymouse_items; - } - if (map_type == KMI_TYPE_TWEAK) { - return rna_enum_event_value_tweak_items; - } - else { - return rna_enum_event_value_all_items; - } -} - static const EnumPropertyItem *rna_KeyMapItem_propvalue_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), @@ -2106,8 +2038,7 @@ static void rna_def_event(BlenderRNA *brna) /* enums */ prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "val"); - RNA_def_property_enum_items(prop, rna_enum_event_value_all_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Event_value_itemf"); + RNA_def_property_enum_items(prop, rna_enum_event_value_items); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Value", "The type of event, only applies to some"); @@ -2118,6 +2049,12 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Type", ""); + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "direction"); + RNA_def_property_enum_items(prop, rna_enum_event_direction_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)"); + /* keyboard */ prop = RNA_def_property(srna, "is_repeat", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -2194,23 +2131,23 @@ static void rna_def_event(BlenderRNA *brna) /* modifiers */ prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shift", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_SHIFT); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Shift", "True when the Shift key is held"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER); prop = RNA_def_property(srna, "ctrl", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "ctrl", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_CTRL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Ctrl", "True when the Ctrl key is held"); prop = RNA_def_property(srna, "alt", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "alt", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_ALT); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Alt", "True when the Alt/Option key is held"); prop = RNA_def_property(srna, "oskey", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "oskey", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_OSKEY); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "OS Key", "True when the Cmd key is held"); @@ -2538,7 +2475,6 @@ static void rna_def_keyconfig(BlenderRNA *brna) static const EnumPropertyItem map_type_items[] = { {KMI_TYPE_KEYBOARD, "KEYBOARD", 0, "Keyboard", ""}, - {KMI_TYPE_TWEAK, "TWEAK", 0, "Tweak", ""}, {KMI_TYPE_MOUSE, "MOUSE", 0, "Mouse", ""}, {KMI_TYPE_NDOF, "NDOF", 0, "NDOF", ""}, {KMI_TYPE_TEXTINPUT, "TEXTINPUT", 0, "Text Input", ""}, @@ -2679,11 +2615,16 @@ static void rna_def_keyconfig(BlenderRNA *brna) prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "val"); - RNA_def_property_enum_items(prop, rna_enum_event_value_all_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_KeyMapItem_value_itemf"); + RNA_def_property_enum_items(prop, rna_enum_event_value_items); RNA_def_property_ui_text(prop, "Value", ""); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "direction"); + RNA_def_property_enum_items(prop, rna_enum_event_direction_items); + RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)"); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + prop = RNA_def_property(srna, "id", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "id"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 81cc72088a6..0589fa7a96e 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -258,6 +258,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, int alt, int oskey, int keymodifier, + int direction, bool repeat, bool head) { @@ -275,7 +276,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, WM_operator_bl_idname(idname_bl, idname); /* create keymap item */ - kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier); + kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier, direction); if (!repeat) { kmi->flag |= KMI_REPEAT_IGNORE; @@ -324,6 +325,7 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km, int alt, int oskey, int keymodifier, + int direction, bool repeat) { /* only modal maps */ @@ -338,13 +340,14 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km, /* not initialized yet, do delayed lookup */ if (!km->modal_items) { - kmi = WM_modalkeymap_add_item_str(km, type, value, modifier, keymodifier, propvalue_str); + kmi = WM_modalkeymap_add_item_str( + km, type, value, modifier, keymodifier, direction, propvalue_str); } else { if (RNA_enum_value_from_id(km->modal_items, propvalue_str, &propvalue) == 0) { BKE_report(reports, RPT_WARNING, "Property value not in enumeration"); } - kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, propvalue); + kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, direction, propvalue); } if (!repeat) { @@ -635,14 +638,23 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, wmEvent e = *win->eventstate; e.type = type; e.val = value; - e.is_repeat = false; + e.flag = 0; e.xy[0] = x; e.xy[1] = y; - e.shift = shift; - e.ctrl = ctrl; - e.alt = alt; - e.oskey = oskey; + e.modifier = 0; + if (shift) { + e.modifier |= KM_SHIFT; + } + if (ctrl) { + e.modifier |= KM_CTRL; + } + if (alt) { + e.modifier |= KM_ALT; + } + if (oskey) { + e.modifier |= KM_OSKEY; + } e.ascii = '\0'; e.utf8_buf[0] = '\0'; @@ -720,7 +732,7 @@ void RNA_api_window(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_string(func, "unicode", NULL, 0, "", ""); RNA_def_parameter_clear_flags(parm, PROP_NEVER_NULL, 0); @@ -1125,7 +1137,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); @@ -1133,6 +1145,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); + RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); RNA_def_boolean(func, "head", @@ -1149,7 +1162,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); @@ -1157,6 +1170,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); + RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", "Added key map item"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 87d8bc8d844..9fe3153eb1e 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -46,6 +46,43 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) /** \name XR Action Map * \{ */ +static XrComponentPath *rna_XrComponentPath_new(XrActionMapBinding *amb, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + XrComponentPath *component_path = MEM_callocN(sizeof(XrComponentPath), __func__); + BLI_strncpy(component_path->path, path_str, sizeof(component_path->path)); + BLI_addtail(&amb->component_paths, component_path); + return component_path; +# else + UNUSED_VARS(amb, path_str); + return NULL; +# endif +} + +static void rna_XrComponentPath_remove(XrActionMapBinding *amb, PointerRNA *component_path_ptr) +{ +# ifdef WITH_XR_OPENXR + XrComponentPath *component_path = component_path_ptr->data; + int idx = BLI_findindex(&amb->component_paths, component_path); + if (idx != -1) { + BLI_freelinkN(&amb->component_paths, component_path); + } + RNA_POINTER_INVALIDATE(component_path_ptr); +# else + UNUSED_VARS(amb, component_path_ptr); +# endif +} + +static XrComponentPath *rna_XrComponentPath_find(XrActionMapBinding *amb, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + return BLI_findstring(&amb->component_paths, path_str, offsetof(XrComponentPath, path)); +# else + UNUSED_VARS(amb, path_str); + return NULL; +# endif +} + static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami, const char *name, bool replace_existing) @@ -99,6 +136,28 @@ static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, con # endif } +static void rna_XrActionMapBinding_component_paths_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data; + rna_iterator_listbase_begin(iter, &amb->component_paths, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapBinding_component_paths_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data; + return BLI_listbase_count(&amb->component_paths); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -174,6 +233,43 @@ static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene) # endif } +static XrUserPath *rna_XrUserPath_new(XrActionMapItem *ami, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + XrUserPath *user_path = MEM_callocN(sizeof(XrUserPath), __func__); + BLI_strncpy(user_path->path, path_str, sizeof(user_path->path)); + BLI_addtail(&ami->user_paths, user_path); + return user_path; +# else + UNUSED_VARS(ami, path_str); + return NULL; +# endif +} + +static void rna_XrUserPath_remove(XrActionMapItem *ami, PointerRNA *user_path_ptr) +{ +# ifdef WITH_XR_OPENXR + XrUserPath *user_path = user_path_ptr->data; + int idx = BLI_findindex(&ami->user_paths, user_path); + if (idx != -1) { + BLI_freelinkN(&ami->user_paths, user_path); + } + RNA_POINTER_INVALIDATE(user_path_ptr); +# else + UNUSED_VARS(ami, user_path_ptr); +# endif +} + +static XrUserPath *rna_XrUserPath_find(XrActionMapItem *ami, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + return BLI_findstring(&ami->user_paths, path_str, offsetof(XrUserPath, path)); +# else + UNUSED_VARS(ami, path_str); + return NULL; +# endif +} + static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am, const char *name, bool replace_existing) @@ -222,6 +318,27 @@ static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *na # endif } +static void rna_XrActionMapItem_user_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + rna_iterator_listbase_begin(iter, &ami->user_paths, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapItem_user_paths_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + return BLI_listbase_count(&ami->user_paths); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value) { # ifdef WITH_XR_OPENXR @@ -395,6 +512,27 @@ static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool # endif } +static void rna_XrActionMapItem_bindings_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + rna_iterator_listbase_begin(iter, &ami->bindings, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapItem_bindings_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + return BLI_listbase_count(&ami->bindings); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -471,6 +609,27 @@ static XrActionMap *rna_XrActionMap_find(PointerRNA *ptr, const char *name) # endif } +static void rna_XrActionMap_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = (XrActionMap *)ptr->data; + rna_iterator_listbase_begin(iter, &actionmap->items, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMap_items_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = (XrActionMap *)ptr->data; + return BLI_listbase_count(&actionmap->items); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -576,26 +735,8 @@ static bool rna_XrSessionState_action_create(bContext *C, { # ifdef WITH_XR_OPENXR wmWindowManager *wm = CTX_wm_manager(C); - unsigned int count_subaction_paths = 0; - const char *subaction_paths[2]; - - if (ami->user_path0[0]) { - subaction_paths[0] = ami->user_path0; - ++count_subaction_paths; - - if (ami->user_path1[0]) { - subaction_paths[1] = ami->user_path1; - ++count_subaction_paths; - } - } - else { - if (ami->user_path1[0]) { - subaction_paths[0] = ami->user_path1; - ++count_subaction_paths; - } - else { - return false; - } + if (BLI_listbase_is_empty(&ami->user_paths)) { + return false; } const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); @@ -621,8 +762,7 @@ static bool rna_XrSessionState_action_create(bContext *C, actionmap->name, ami->name, ami->type, - count_subaction_paths, - subaction_paths, + &ami->user_paths, ot, op_properties, is_button_action ? ami->haptic_name : NULL, @@ -645,30 +785,10 @@ static bool rna_XrSessionState_action_binding_create(bContext *C, { # ifdef WITH_XR_OPENXR wmWindowManager *wm = CTX_wm_manager(C); - unsigned int count_subaction_paths = 0; - const char *subaction_paths[2]; - const char *component_paths[2]; - - if (ami->user_path0[0]) { - subaction_paths[0] = ami->user_path0; - component_paths[0] = amb->component_path0; - ++count_subaction_paths; - - if (ami->user_path1[0]) { - subaction_paths[1] = ami->user_path1; - component_paths[1] = amb->component_path1; - ++count_subaction_paths; - } - } - else { - if (ami->user_path1[0]) { - subaction_paths[0] = ami->user_path1; - component_paths[0] = amb->component_path1; - ++count_subaction_paths; - } - else { - return false; - } + const int count_user_paths = BLI_listbase_count(&ami->user_paths); + const int count_component_paths = BLI_listbase_count(&amb->component_paths); + if (count_user_paths < 1 || (count_user_paths != count_component_paths)) { + return false; } const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); @@ -695,9 +815,8 @@ static bool rna_XrSessionState_action_binding_create(bContext *C, actionmap->name, ami->name, amb->profile, - count_subaction_paths, - subaction_paths, - component_paths, + &ami->user_paths, + &amb->component_paths, is_float_action ? float_thresholds : NULL, is_button_action ? axis_flags : NULL, is_pose_action ? poses : NULL); @@ -954,6 +1073,18 @@ static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter # endif } +static int rna_XrSessionState_actionmaps_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + ListBase *lb = WM_xr_actionmaps_get(xr->runtime); + return BLI_listbase_count(lb); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -1240,6 +1371,42 @@ static const EnumPropertyItem rna_enum_xr_axis1_flags[] = { /** \name XR Action Map * \{ */ +static void rna_def_xr_component_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrComponentPaths"); + srna = RNA_def_struct(brna, "XrComponentPaths", NULL); + RNA_def_struct_sdna(srna, "XrActionMapBinding"); + RNA_def_struct_ui_text(srna, "XR Component Paths", "Collection of OpenXR component paths"); + + func = RNA_def_function(srna, "new", "rna_XrComponentPath_new"); + parm = RNA_def_string( + func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "component_path", "XrComponentPath", "Component Path", "Added component path"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrComponentPath_remove"); + parm = RNA_def_pointer(func, "component_path", "XrComponentPath", "Component Path", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrComponentPath_find"); + parm = RNA_def_string( + func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "component_path", + "XrComponentPath", + "Component Path", + "The component path with the given path"); + RNA_def_function_return(func, parm); +} + static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1289,6 +1456,36 @@ static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); } +static void rna_def_xr_user_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrUserPaths"); + srna = RNA_def_struct(brna, "XrUserPaths", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR User Paths", "Collection of OpenXR user paths"); + + func = RNA_def_function(srna, "new", "rna_XrUserPath_new"); + parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", "Added user path"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrUserPath_remove"); + parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); + + func = RNA_def_function(srna, "find", "rna_XrUserPath_find"); + parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "user_path", "XrUserPath", "User Path", "The user path with the given path"); + RNA_def_function_return(func, parm); +} + static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1404,6 +1601,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "items", NULL); RNA_def_property_struct_type(prop, "XrActionMapItem"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMap_items_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMap_items_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text( prop, "Items", @@ -1414,6 +1620,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "selitem"); RNA_def_property_ui_text(prop, "Selected Item", ""); + /* XrUserPath */ + srna = RNA_def_struct(brna, "XrUserPath", NULL); + RNA_def_struct_sdna(srna, "XrUserPath"); + RNA_def_struct_ui_text(srna, "XR User Path", ""); + + prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, XR_MAX_USER_PATH_LENGTH); + RNA_def_property_ui_text(prop, "Path", "OpenXR user path"); + /* XrActionMapItem */ srna = RNA_def_struct(brna, "XrActionMapItem", NULL); RNA_def_struct_sdna(srna, "XrActionMapItem"); @@ -1429,13 +1644,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Type", "Action type"); RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update"); - prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE); - RNA_def_property_string_maxlength(prop, 64); - RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path"); - - prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE); - RNA_def_property_string_maxlength(prop, 64); - RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path"); + prop = RNA_def_property(srna, "user_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrUserPath"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapItem_user_paths_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapItem_user_paths_length", + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "User Paths", "OpenXR user paths"); + rna_def_xr_user_paths(brna, prop); prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); @@ -1520,6 +1741,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "XrActionMapBinding"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapItem_bindings_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapItem_bindings_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text( prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input"); rna_def_xr_actionmap_bindings(brna, prop); @@ -1528,6 +1758,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "selbinding"); RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); + /* XrComponentPath */ + srna = RNA_def_struct(brna, "XrComponentPath", NULL); + RNA_def_struct_sdna(srna, "XrComponentPath"); + RNA_def_struct_ui_text(srna, "XR Component Path", ""); + + prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, XR_MAX_COMPONENT_PATH_LENGTH); + RNA_def_property_ui_text(prop, "Path", "OpenXR component path"); + /* XrActionMapBinding */ srna = RNA_def_struct(brna, "XrActionMapBinding", NULL); RNA_def_struct_sdna(srna, "XrActionMapBinding"); @@ -1542,13 +1781,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_string_maxlength(prop, 256); RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path"); - prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE); - RNA_def_property_string_maxlength(prop, 192); - RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path"); - - prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE); - RNA_def_property_string_maxlength(prop, 192); - RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path"); + prop = RNA_def_property(srna, "component_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrComponentPath"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapBinding_component_paths_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapBinding_component_paths_length", + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "Component Paths", "OpenXR component paths"); + rna_def_xr_component_paths(brna, prop); prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "float_threshold"); @@ -1812,7 +2057,7 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_boolean(func, "result", 0, "Result", ""); RNA_def_function_return(func, parm); @@ -1823,19 +2068,19 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name"); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string(func, "grip_action", NULL, - 64, + MAX_NAME, "Grip Action", "Name of the action representing the controller grips"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string(func, "aim_action", NULL, - 64, + MAX_NAME, "Aim Action", "Name of the action representing the controller aims"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -1847,11 +2092,12 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path"); + parm = RNA_def_string( + func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_float_array( func, @@ -1871,15 +2117,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string( func, "user_path", NULL, - 64, + XR_MAX_USER_PATH_LENGTH, "User Path", "Optional OpenXR user path. If not set, the action will be applied to all paths"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -1922,15 +2168,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna) RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string( func, "user_path", NULL, - 64, + XR_MAX_USER_PATH_LENGTH, "User Path", "Optional OpenXR user path. If not set, the action will be stopped for all paths"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -2068,16 +2314,16 @@ static void rna_def_xr_session_state(BlenderRNA *brna) "Additional scale multiplier to apply to base scale when determining viewer scale"); prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrActionMap"); RNA_def_property_collection_funcs(prop, "rna_XrSessionState_actionmaps_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", - NULL, + "rna_XrSessionState_actionmaps_length", NULL, NULL, NULL); - RNA_def_property_struct_type(prop, "XrActionMap"); RNA_def_property_ui_text(prop, "XR Action Maps", ""); rna_def_xr_actionmaps(brna, prop); diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 2cc3bb8c916..ea42ac761ea 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -371,7 +371,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; - const bool use_recalc_normals = (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || use_merge; + const bool use_recalc_normals = BKE_mesh_vertex_normals_are_dirty(mesh) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != NULL); int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; @@ -819,7 +819,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene), * In other cases it should be impossible to have a type mismatch. */ - if (amd->curve_ob && amd->curve_ob->type != OB_CURVE) { + if (amd->curve_ob && amd->curve_ob->type != OB_CURVES_LEGACY) { return true; } if (amd->start_cap && amd->start_cap->type != OB_MESH) { diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index ca5d59433f0..d1c7dd43f15 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -71,7 +71,7 @@ static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED * * In other cases it should be impossible to have a type mismatch. */ - return !cmd->object || cmd->object->type != OB_CURVE; + return !cmd->object || cmd->object->type != OB_CURVES_LEGACY; } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 0411bcd7c34..ddbed4f498e 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -313,8 +313,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd, if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) { float(*clnors)[3] = NULL; - if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || - !CustomData_has_layer(ldata, CD_NORMAL)) { + if (!CustomData_has_layer(ldata, CD_NORMAL)) { BKE_mesh_calc_normals_split(mesh); } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f54ed8f5a25..210a7edac54 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -454,6 +454,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) ui_data->base.rna_subtype = PROP_COLOR; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); ui_data->default_array_len = 4; + ui_data->min = 0.0; + ui_data->max = FLT_MAX; + ui_data->soft_min = 0.0; + ui_data->soft_max = 1.0; for (const int i : IndexRange(4)) { ui_data->default_array[i] = double(value->value[i]); } @@ -1381,7 +1385,7 @@ static void add_attribute_search_button(const bContext &C, 0.0f, 0.0f, 0.0f, - ""); + socket.description); const Object *object = ED_object_context(&C); BLI_assert(object != nullptr); diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index e6aebbfeb56..3649808f4f0 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -535,8 +535,8 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, CustomData *ldata = &result->ldata; - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); - const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(mesh); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(result); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(result); clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); if (use_current_clnors) { diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 0313c37283a..fdaf7bd41d1 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -953,7 +953,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex } /* must recalculate normals with vgroups since they can displace unevenly T26888. */ - if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || do_rim || dvert) { + if (BKE_mesh_vertex_normals_are_dirty(mesh) || do_rim || dvert) { BKE_mesh_normals_tag_dirty(result); } else if (do_shell) { @@ -1009,9 +1009,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS - /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above, + /* NOTE(@sybren): due to the code setting normals dirty a few lines above, * do_side_normals is always false. */ - const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); + const bool do_side_normals = !BKE_mesh_vertex_normals_are_dirty(result); /* annoying to allocate these since we only need the edge verts, */ float(*edge_vert_nos)[3] = do_side_normals ? MEM_calloc_arrayN(numVerts, sizeof(float[3]), __func__) : diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index ed7acef4cdc..ff25c1afd49 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1405,21 +1405,26 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, for (uint j = 0; g->valid; j++, g++) { if (!g->is_singularity) { float *nor = g->no; + /* During vertex position calculation, the algorithm decides if it wants to disable the + * boundary fix to maintain correct thickness. If the used algorithm does not produce a + * free move direction (move_nor), it can use approximate_free_direction to decide on + * a movement direction based on the connected edges. */ float move_nor[3] = {0, 0, 0}; bool disable_boundary_fix = (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE || (g->is_orig_closed || g->split)); + bool approximate_free_direction = false; /* Constraints Method. */ if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) { NewEdgeRef *first_edge = NULL; NewEdgeRef **edge_ptr = g->edges; /* Contains normal and offset [nx, ny, nz, ofs]. */ - float(*normals_queue)[4] = MEM_malloc_arrayN( - g->edges_len + 1, sizeof(*normals_queue), "normals_queue in solidify"); + float(*planes_queue)[4] = MEM_malloc_arrayN( + g->edges_len + 1, sizeof(*planes_queue), "planes_queue in solidify"); uint queue_index = 0; - float face_nors[3][3]; - float nor_ofs[3]; + float fallback_nor[3]; + float fallback_ofs = 0.0f; const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split; for (uint k = 0; k < g->edges_len; k++, edge_ptr++) { @@ -1436,17 +1441,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } if (!null_faces[face->index]) { - /* And normal to the queue. */ - mul_v3_v3fl(normals_queue[queue_index], + /* And plane to the queue. */ + mul_v3_v3fl(planes_queue[queue_index], poly_nors[face->index], face->reversed ? -1 : 1); - normals_queue[queue_index++][3] = ofs; + planes_queue[queue_index++][3] = ofs; } else { /* Just use this approximate normal of the null face if there is no other * normal to use. */ - mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1); - nor_ofs[0] = ofs; + mul_v3_v3fl(fallback_nor, poly_nors[face->index], face->reversed ? -1 : 1); + fallback_ofs = ofs; } } } @@ -1455,131 +1460,173 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } } - uint face_nors_len = 0; - const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; - while (queue_index > 0) { - if (face_nors_len == 0) { - if (queue_index <= 2) { - for (uint k = 0; k < queue_index; k++) { - copy_v3_v3(face_nors[k], normals_queue[k]); - nor_ofs[k] = normals_queue[k][3]; + if (queue_index > 2) { + /* Find the two most different normals. */ + float min_p = 2.0f; + uint min_n0 = 0; + uint min_n1 = 0; + for (uint k = 0; k < queue_index; k++) { + for (uint m = k + 1; m < queue_index; m++) { + float p = dot_v3v3(planes_queue[k], planes_queue[m]); + if (p < min_p) { + min_p = p; + min_n0 = k; + min_n1 = m; } - face_nors_len = queue_index; - queue_index = 0; } - else { - /* Find most different two normals. */ - float min_p = 2; - uint min_n0 = 0; - uint min_n1 = 0; - for (uint k = 0; k < queue_index; k++) { - for (uint m = k + 1; m < queue_index; m++) { - float p = dot_v3v3(normals_queue[k], normals_queue[m]); - if (p <= min_p + FLT_EPSILON) { - min_p = p; - min_n0 = m; - min_n1 = k; - } - } - } - copy_v3_v3(face_nors[0], normals_queue[min_n0]); - copy_v3_v3(face_nors[1], normals_queue[min_n1]); - nor_ofs[0] = normals_queue[min_n0][3]; - nor_ofs[1] = normals_queue[min_n1][3]; - face_nors_len = 2; - queue_index--; - memmove(normals_queue + min_n0, - normals_queue + min_n0 + 1, - (queue_index - min_n0) * sizeof(*normals_queue)); - queue_index--; - memmove(normals_queue + min_n1, - normals_queue + min_n1 + 1, - (queue_index - min_n1) * sizeof(*normals_queue)); - min_p = 1; - min_n1 = 0; - float max_p = -1; - for (uint k = 0; k < queue_index; k++) { - max_p = -1; - for (uint m = 0; m < face_nors_len; m++) { - float p = dot_v3v3(face_nors[m], normals_queue[k]); - if (p > max_p + FLT_EPSILON) { - max_p = p; - } - } - if (max_p <= min_p + FLT_EPSILON) { - min_p = max_p; - min_n1 = k; - } - } - if (min_p < 0.8) { - copy_v3_v3(face_nors[2], normals_queue[min_n1]); - nor_ofs[2] = normals_queue[min_n1][3]; - face_nors_len++; - queue_index--; - memmove(normals_queue + min_n1, - normals_queue + min_n1 + 1, - (queue_index - min_n1) * sizeof(*normals_queue)); + } + /* Put the two found normals, first in the array queue. */ + if (min_n1 != 0) { + swap_v4_v4(planes_queue[min_n0], planes_queue[0]); + swap_v4_v4(planes_queue[min_n1], planes_queue[1]); + } + else { + swap_v4_v4(planes_queue[min_n0], planes_queue[1]); + } + /* Find the third most important/different normal. */ + min_p = 1.0f; + min_n1 = 2; + float max_p = -1.0f; + for (uint k = 2; k < queue_index; k++) { + max_p = max_ff(dot_v3v3(planes_queue[0], planes_queue[k]), + dot_v3v3(planes_queue[1], planes_queue[k])); + if (max_p <= min_p) { + min_p = max_p; + min_n1 = k; + } + } + swap_v4_v4(planes_queue[min_n1], planes_queue[2]); + } + /* Remove/average duplicate normals in planes_queue. */ + while (queue_index > 2) { + uint best_n0 = 0; + uint best_n1 = 0; + float best_p = -1.0f; + float best_ofs_diff = 0.0f; + for (uint k = 0; k < queue_index; k++) { + for (uint m = k + 1; m < queue_index; m++) { + float p = dot_v3v3(planes_queue[m], planes_queue[k]); + float ofs_diff = fabsf(planes_queue[m][3] - planes_queue[k][3]); + if (p > best_p + FLT_EPSILON || (p >= best_p && ofs_diff < best_ofs_diff)) { + best_p = p; + best_ofs_diff = ofs_diff; + best_n0 = k; + best_n1 = m; } } } - else { - uint best = 0; - uint best_group = 0; - float best_p = -1.0f; - for (uint k = 0; k < queue_index; k++) { - for (uint m = 0; m < face_nors_len; m++) { - float p = dot_v3v3(face_nors[m], normals_queue[k]); - if (p > best_p + FLT_EPSILON) { - best_p = p; - best = m; - best_group = k; + /* Make sure there are no equal planes. This threshold is crucial for the + * methods below to work without numerical issues. */ + if (best_p < 0.98f) { + break; + } + add_v3_v3(planes_queue[best_n0], planes_queue[best_n1]); + normalize_v3(planes_queue[best_n0]); + planes_queue[best_n0][3] = (planes_queue[best_n0][3] + planes_queue[best_n1][3]) * + 0.5f; + queue_index--; + memmove(planes_queue + best_n1, + planes_queue + best_n1 + 1, + (queue_index - best_n1) * sizeof(*planes_queue)); + } + const uint size = queue_index; + /* If there is more than 2 planes at this vertex, the boundary fix should be disabled + * to stay at the correct thickness for all the faces. This is not very good in + * practice though, since that will almost always disable the boundary fix. Instead + * introduce a threshold which decides whether the boundary fix can be used without + * major thickness changes. If the following constant is 1.0, it would always + * prioritize correct thickness. At 0.7 the thickness is allowed to change a bit if + * necessary for the fix (~10%). Note this only applies if a boundary fix is used. */ + const float boundary_fix_threshold = 0.7f; + if (size > 3) { + /* Use the most general least squares method to find the best position. */ + float mat[3][3]; + zero_m3(mat); + for (int k = 0; k < 3; k++) { + for (int m = 0; m < size; m++) { + madd_v3_v3fl(mat[k], planes_queue[m], planes_queue[m][k]); + } + /* Add a small epsilon to ensure the invert is going to work. + * This addition makes the inverse more stable and the results + * seem to get more precise. */ + mat[k][k] += 5e-5f; + } + /* NOTE: this matrix invert fails if there is less than 3 different normals. */ + invert_m3(mat); + zero_v3(nor); + for (int k = 0; k < size; k++) { + madd_v3_v3fl(nor, planes_queue[k], planes_queue[k][3]); + } + mul_v3_m3v3(nor, mat, nor); + + if (!disable_boundary_fix) { + /* Figure out if the approximate boundary fix can get use here. */ + float greatest_angle_cos = 1.0f; + for (uint k = 0; k < 2; k++) { + for (uint m = 2; m < size; m++) { + float p = dot_v3v3(planes_queue[m], planes_queue[k]); + if (p < greatest_angle_cos) { + greatest_angle_cos = p; } } } - add_v3_v3(face_nors[best], normals_queue[best_group]); - normalize_v3(face_nors[best]); - nor_ofs[best] = (nor_ofs[best] + normals_queue[best_group][3]) * 0.5f; - queue_index--; - memmove(normals_queue + best_group, - normals_queue + best_group + 1, - (queue_index - best_group) * sizeof(*normals_queue)); + if (greatest_angle_cos > boundary_fix_threshold) { + approximate_free_direction = true; + } + else { + disable_boundary_fix = true; + } } } - MEM_freeN(normals_queue); - - /* When up to 3 constraint normals are found. */ - if (ELEM(face_nors_len, 2, 3)) { - const float q = dot_v3v3(face_nors[0], face_nors[1]); + else if (size > 1) { + /* When up to 3 constraint normals are found, there is a simple solution. */ + const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; + const float q = dot_v3v3(planes_queue[0], planes_queue[1]); float d = 1.0f - q * q; - cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]); + cross_v3_v3v3(move_nor, planes_queue[0], planes_queue[1]); + normalize_v3(move_nor); if (d > FLT_EPSILON * 10 && q < stop_explosion) { d = 1.0f / d; - mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); - mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); + mul_v3_fl(planes_queue[0], (planes_queue[0][3] - planes_queue[1][3] * q) * d); + mul_v3_fl(planes_queue[1], (planes_queue[1][3] - planes_queue[0][3] * q) * d); } else { d = 1.0f / (fabsf(q) + 1.0f); - mul_v3_fl(face_nors[0], nor_ofs[0] * d); - mul_v3_fl(face_nors[1], nor_ofs[1] * d); + mul_v3_fl(planes_queue[0], planes_queue[0][3] * d); + mul_v3_fl(planes_queue[1], planes_queue[1][3] * d); } - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - if (face_nors_len == 3) { - float *free_nor = move_nor; - mul_v3_fl(face_nors[2], nor_ofs[2]); - d = dot_v3v3(face_nors[2], free_nor); - if (LIKELY(fabsf(d) > FLT_EPSILON)) { - sub_v3_v3v3(face_nors[0], nor, face_nors[2]); /* Override face_nor[0]. */ - mul_v3_fl(free_nor, dot_v3v3(face_nors[2], face_nors[0]) / d); - sub_v3_v3(nor, free_nor); + add_v3_v3v3(nor, planes_queue[0], planes_queue[1]); + if (size == 3) { + d = dot_v3v3(planes_queue[2], move_nor); + /* The following threshold ignores the third plane if it is almost orthogonal to + * the still free direction. */ + if (fabsf(d) > 0.02f) { + float tmp[3]; + madd_v3_v3v3fl(tmp, nor, planes_queue[2], -planes_queue[2][3]); + mul_v3_v3fl(tmp, move_nor, dot_v3v3(planes_queue[2], tmp) / d); + sub_v3_v3(nor, tmp); + /* Disable boundary fix if the constraints would be majorly unsatisfied. */ + if (fabsf(d) > 1.0f - boundary_fix_threshold) { + disable_boundary_fix = true; + } } + } + approximate_free_direction = false; + } + else if (size == 1) { + /* Face corner case. */ + mul_v3_v3fl(nor, planes_queue[0], planes_queue[0][3]); + if (g->edges_len > 2) { disable_boundary_fix = true; + approximate_free_direction = true; } } else { - BLI_assert(face_nors_len < 2); - mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); + /* Fallback case for null faces. */ + mul_v3_v3fl(nor, fallback_nor, fallback_ofs); disable_boundary_fix = true; } + MEM_freeN(planes_queue); } /* Fixed/Even Method. */ else { @@ -1707,26 +1754,29 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } /* Set move_nor for boundary fix. */ if (!disable_boundary_fix && g->edges_len > 2) { - edge_ptr = g->edges + 1; - float tmp[3]; - uint k; - for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { - MEdge *e = orig_medge + (*edge_ptr)->old_edge; - sub_v3_v3v3( - tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]); - add_v3_v3(move_nor, tmp); - } - if (k == 1) { - disable_boundary_fix = true; - } - else { - disable_boundary_fix = normalize_v3(move_nor) == 0.0f; - } + approximate_free_direction = true; } else { disable_boundary_fix = true; } } + if (approximate_free_direction) { + /* Set move_nor for boundary fix. */ + NewEdgeRef **edge_ptr = g->edges + 1; + float tmp[3]; + int k; + for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { + MEdge *e = orig_medge + (*edge_ptr)->old_edge; + sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]); + add_v3_v3(move_nor, tmp); + } + if (k == 1) { + disable_boundary_fix = true; + } + else { + disable_boundary_fix = normalize_v3(move_nor) == 0.0f; + } + } /* Fix boundary verts. */ if (!disable_boundary_fix) { /* Constraint normal, nor * constr_nor == 0 after this fix. */ @@ -1743,8 +1793,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, orig_mvert_co[i]); if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { cross_v3_v3v3(constr_nor, e0, e1); + normalize_v3(constr_nor); } else { + BLI_assert(smd->nonmanifold_boundary_mode == + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND); float f0[3]; float f1[3]; if (g->edges[0]->faces[0]->reversed) { @@ -1766,9 +1819,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, normalize_v3(n0); normalize_v3(n1); add_v3_v3v3(constr_nor, n0, n1); + normalize_v3(constr_nor); } float d = dot_v3v3(constr_nor, move_nor); - if (LIKELY(fabsf(d) > FLT_EPSILON)) { + /* Only allow the thickness to increase about 10 times. */ + if (fabsf(d) > 0.1f) { mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d); sub_v3_v3(nor, move_nor); } diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index cad36959f2b..b791e28d15b 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -234,7 +234,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * assigned at this stage of modifier stack evaluation. */ const bool is_editmode = (mesh->edit_mesh != NULL); const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode); - if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) { + if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex( + scene, ctx->object, mesh, smd, required_mode, false)) { subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd); return result; } @@ -246,9 +247,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Happens on bad topology, but also on empty input mesh. */ return result; } - const bool use_clnors = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && - (mesh->flag & ME_AUTOSMOOTH) && - CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + const bool use_clnors = BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); if (use_clnors) { /* If custom normals are present and the option is turned on calculate the split * normals and clear flag so the normals get interpolated to the result mesh. */ @@ -412,6 +411,13 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(layout, ptr, "show_only_control_edges", 0, NULL, ICON_NONE); + SubsurfModifierData *smd = ptr->data; + Object *ob = ob_ptr.data; + Mesh *mesh = ob->data; + if (BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(smd, mesh)) { + uiItemL(layout, "Autosmooth or custom normals detected, disabling GPU subdivision", ICON_INFO); + } + modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 94482742831..90652c180c6 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -324,7 +324,7 @@ static void modifier_panel_header(const bContext *C, Panel *panel) buttons_number++; } } /* Tessellation point for curve-typed objects. */ - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { /* Some modifiers can work with pre-tessellated curves only. */ if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { /* Add button (appearing to be ON) and add tip why this can't be changed. */ diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 41b600e386b..a8c52108cc0 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -204,7 +204,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, BKE_mesh_orco_ensure(ob, mesh); } } - else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { /* TODO(sybren): get evaluated mesh from depsgraph once * that's properly generated for curves. */ mesh = BKE_mesh_new_nomain_from_curve(ob); diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 51ec0a46d32..d6f493267f8 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -285,9 +285,6 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, mesh->runtime.is_original = false; - /* Mark tessellated CD layers as dirty. */ - mesh->runtime.cd_dirty_vert |= CD_MASK_TESSLOOPNORMAL; - return mesh; } diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index 3e71d1fb106..5ce76046294 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -189,10 +189,8 @@ struct DisplaceGridOp { template<typename GridType> void operator()() { - if constexpr (blender::is_same_any_v<GridType, - openvdb::points::PointDataGrid, - openvdb::StringGrid, - openvdb::MaskGrid>) { + if constexpr (blender:: + is_same_any_v<GridType, openvdb::points::PointDataGrid, openvdb::MaskGrid>) { /* We don't support displacing these grid types yet. */ return; } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 1078ebfaeb2..1b6472e2d42 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -105,6 +105,12 @@ static float mix_weight(float weight, float weight2, char mix_mode) if (mix_mode == MOD_WVG_MIX_AVG) { return (weight + weight2) * 0.5f; } + if (mix_mode == MOD_WVG_MIX_MIN) { + return (weight < weight2 ? weight : weight2); + } + if (mix_mode == MOD_WVG_MIX_MAX) { + return (weight > weight2 ? weight : weight2); + } return weight2; } diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 86dde999ffc..cfcc30655b5 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -82,6 +82,7 @@ void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_duplicate_elements(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_dual_mesh(void); void register_node_type_geo_edge_split(void); @@ -100,6 +101,7 @@ void register_node_type_geo_input_mesh_edge_angle(void); void register_node_type_geo_input_mesh_edge_neighbors(void); void register_node_type_geo_input_mesh_edge_vertices(void); void register_node_type_geo_input_mesh_face_area(void); +void register_node_type_geo_input_mesh_face_is_planar(void); void register_node_type_geo_input_mesh_face_neighbors(void); void register_node_type_geo_input_mesh_island(void); void register_node_type_geo_input_mesh_vertex_neighbors(void); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 3bcd4b87b15..4e78f6c1142 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -110,7 +110,7 @@ class SocketDeclaration { /** * Change the node such that the socket will become visible. The node type's update method * should be called afterwards. - * \note Note that this is not necessarily implemented for all node types. + * \note this is not necessarily implemented for all node types. */ void make_available(bNode &node) const; diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh index e3927488612..7a1aff13020 100644 --- a/source/blender/nodes/NOD_socket_search_link.hh +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -16,7 +16,7 @@ struct bContext; namespace blender::nodes { /** - * Parameters for the operation operation of adding a node after the link drag search menu closes. + * Parameters for the operation of adding a node after the link drag search menu closes. */ class LinkSearchOpParams { private: diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 1f1d7e4dc3a..efd3c420330 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -338,6 +338,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CU DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "") DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "") @@ -358,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", Inpu DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 15e7163d7db..162ef07a6dd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -10,6 +10,7 @@ #include "BLI_assert.h" #include "BLI_dynstr.h" #include "BLI_hash_mm3.h" +#include "BLI_math_vector.h" #include "BLI_string_ref.hh" #include "BLI_utildefines.h" diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc index 2e2e5227a65..e6fdf1820fa 100644 --- a/source/blender/nodes/function/nodes/node_fn_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -3,6 +3,7 @@ #include <cmath> #include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLI_string.h" #include "UI_interface.h" diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index f76bb904153..46787f7575d 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -2,6 +2,8 @@ #include "node_function_util.hh" +#include "BLI_math_vector.h" + #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index f38562a8926..48a83dc825b 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -98,6 +98,7 @@ set(SRC nodes/node_geo_curve_to_points.cc nodes/node_geo_curve_trim.cc nodes/node_geo_delete_geometry.cc + nodes/node_geo_duplicate_elements.cc nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc nodes/node_geo_edge_split.cc @@ -116,6 +117,7 @@ set(SRC nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc nodes/node_geo_input_mesh_face_area.cc + nodes/node_geo_input_mesh_face_is_planar.cc nodes/node_geo_input_mesh_face_neighbors.cc nodes/node_geo_input_mesh_island.cc nodes/node_geo_input_mesh_vertex_neighbors.cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc index b83aa8b69a9..0980c2d6e72 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc @@ -137,15 +137,15 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_default_remaining_outputs(); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); @@ -167,9 +167,9 @@ static void node_geo_exec(GeoNodeExecParams params) end_result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults start_attributes = curve_to_points_create_result_attributes( - start_point_component, curve); + start_point_component, *curve); CurveToPointsResults end_attributes = curve_to_points_create_result_attributes( - end_point_component, curve); + end_point_component, *curve); copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes); copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc index 6deaf5b554a..2fe06a17adf 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc @@ -19,15 +19,15 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( @@ -41,6 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) } }); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc index f0a201c5adf..729ccca5f04 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc @@ -33,24 +33,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_curve_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { @@ -59,10 +59,10 @@ static void select_curve_by_handle_type(const CurveEval &curve, threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i_spline : range) { const Spline &spline = *splines[i_spline]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); - Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left(); - Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right(); + Span<int8_t> types_left = bezier_spline.handle_types_left(); + Span<int8_t> types_right = bezier_spline.handle_types_right(); for (const int i_point : IndexRange(bezier_spline.size())) { r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && types_left[i_point] == type) || @@ -81,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles *storage = (const NodeGeometryCurveSelectHandles *)params.node().storage; - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage->handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; @@ -89,9 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - const CurveEval *curve = curve_component.get_for_read(); - - if (curve != nullptr) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); const std::string selection_name = params.extract_input<std::string>("Selection"); OutputAttribute_Typed<bool> selection = curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index b574b2e3cff..537c7c42610 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -31,20 +31,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -56,31 +56,31 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_POINT, true); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; bool has_bezier_spline = false; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -101,6 +101,8 @@ static void node_geo_exec(GeoNodeExecParams params) bezier_spline.mark_cache_invalid(); } + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index b8696e1eb52..4e3b0839da7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -148,8 +148,8 @@ static SplinePtr poly_to_bezier(const Spline &input) output->positions().copy_from(input.positions()); output->radii().copy_from(input.radii()); output->tilts().copy_from(input.tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Vector); - output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -166,8 +166,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right()); scale_input_assign<float>(input.radii(), 3, 2, output->radii()); scale_input_assign<float>(input.tilts(), 3, 2, output->tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Align); - output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(input, *output); output->attributes.reallocate(output->size()); @@ -183,11 +183,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 6) { params.error_message_add( NodeWarningType::Info, @@ -202,6 +202,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params } return nurbs_to_bezier(input); } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -210,12 +214,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -229,40 +236,40 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read()); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component->attribute_get_for_read( selection_name, ATTR_DOMAIN_CURVE, true); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - for (const int i : curve.splines().index_range()) { + for (const int i : curve->splines().index_range()) { if (selection[i]) { switch (output_type) { case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + new_curve->add_spline(convert_to_poly_spline(*curve->splines()[i])); break; case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + new_curve->add_spline(convert_to_bezier(*curve->splines()[i], params)); break; case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + new_curve->add_spline(convert_to_nurbs(*curve->splines()[i])); break; } } else { - new_curve->add_spline(curve.splines()[i]->copy()); + new_curve->add_spline(curve->splines()[i]->copy()); } } - new_curve->attributes = curve.attributes; - params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); + new_curve->attributes = curve->attributes; + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*new_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc index 8ae9df78936..03f7aec8838 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc @@ -111,8 +111,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -122,10 +122,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -136,10 +136,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -187,8 +187,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -235,26 +235,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -338,7 +342,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", geometry_set); return; } @@ -350,9 +354,11 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + params.set_output("Geometry", + GeometrySet::create_with_curves(curve_eval_to_curves(*output_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc index 64627e61910..f8fcc3cc363 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc @@ -283,19 +283,19 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", GeometrySet()); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { params.set_output("Geometry", GeometrySet()); @@ -306,7 +306,7 @@ static void node_geo_exec(GeoNodeExecParams params) PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, - curve); + *curve); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: case GEO_NODE_CURVE_RESAMPLE_LENGTH: diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc index a0b862546bc..ca98d83c137 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc @@ -111,9 +111,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(spline.radii(), r_spline.radii(), mask); copy_data(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -122,12 +122,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -231,9 +235,9 @@ static void delete_curve_selection(const CurveComponent &in_component, const bool invert) { std::unique_ptr<CurveEval> r_curve = curve_delete( - *in_component.get_for_read(), selection_name, invert); + *curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert); if (r_curve) { - r_component.replace(r_curve.release()); + r_component.replace(curve_eval_to_curves(*r_curve)); } else { r_component.clear(); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc index ff86a92f2c7..8991261a21a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc @@ -48,7 +48,7 @@ static void node_geo_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert( component, IndexMask(selected_edge_indices)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b6d677154d0..b3fe9d160b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_CURVE: { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 6424fccbe04..cb7132d5ea2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -33,25 +33,18 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); - if (geometry_set.has<MeshComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names); - } - if (geometry_set.has<PointCloudComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); - } - if (geometry_set.has<CurveComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); - } - if (geometry_set.has<InstancesComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names); + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { + if (geometry_set.has(type)) { + remove_attribute(geometry_set.get_component_for_write(type), params, attribute_names); + } } params.set_output("Geometry", geometry_set); } + } // namespace blender::nodes::node_geo_attribute_remove_cc void register_node_type_geo_attribute_remove() diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 59147e9b23f..412f35d62fd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -161,9 +161,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) positions_span = varray.get_internal_span(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { positions_span = spline->evaluated_positions(); total_size += positions_span.size(); count++; @@ -201,9 +202,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) offset += varray.size(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { Span<float3> array = spline->evaluated_positions(); positions.as_mutable_span().slice(offset, array.size()).copy_from(array); offset += array.size(); @@ -272,8 +274,8 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) if (set.has_mesh()) { read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords); } - if (set.has_curve()) { - read_curve_positions(*set.get_curve_for_read(), transforms, &coords); + if (set.has_curves()) { + read_curve_positions(*curves_to_curve_eval(*set.get_curves_for_read()), transforms, &coords); } } return hull_from_bullet(nullptr, coords); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index 65aad0fcbf1..ce3058c7d42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -59,10 +59,13 @@ class EndpointFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + if (!curve_component.has_curves()) { + return nullptr; + } - Array<int> control_point_offsets = curve->control_point_offsets(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); if (curve == nullptr || control_point_offsets.last() == 0) { return nullptr; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 9824b2b2ece..6702ee6c0aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -113,12 +113,13 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - if (curve.splines().is_empty()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + if (curve->splines().is_empty()) { geometry_set.replace_curve(nullptr); return; } @@ -127,7 +128,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; - const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type); + const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type); Mesh *mesh = cdt_to_mesh(results); geometry_set.replace_mesh(mesh); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 94425ab48f4..24d72ad553b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -394,9 +394,9 @@ static void update_bezier_positions(const FilletData &fd, dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - handle_length * next_dir; dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = - BezierSpline::HandleType::Align; + BEZIER_HANDLE_ALIGN; dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = - BezierSpline::HandleType::Vector; + BEZIER_HANDLE_VECTOR; dst_spline.mark_cache_invalid(); /* Calculate the center of the radius to be formed. */ @@ -406,8 +406,8 @@ static void update_bezier_positions(const FilletData &fd, float radius; radius_vec = math::normalize_and_get_length(radius_vec, radius); - dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); - dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); + dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); /* For each of the vertices in between the end points. */ for (const int j : IndexRange(1, count - 2)) { @@ -512,12 +512,12 @@ static SplinePtr fillet_spline(const Spline &spline, copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); switch (spline.type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline); BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector); - dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector); + dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR); + dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR); update_poly_positions(fd, dst_spline, src_spline, point_counts); } else { @@ -525,17 +525,21 @@ static SplinePtr fillet_spline(const Spline &spline, } break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline); NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr); copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); update_poly_positions(fd, dst_spline, src_spline, point_counts); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } return dst_spline_ptr; @@ -568,7 +572,7 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, const std::optional<Field<int>> &count_field, const bool limit_radius) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -599,10 +603,10 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, fillet_param.limit_radius = limit_radius; - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); - std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read()); + std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 26a8ad2d988..ccd3a587e63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -31,30 +31,30 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { int offset = 0; for (const SplinePtr &spline : curve.splines()) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { r_selection.slice(offset, spline->size()).fill(false); offset += spline->size(); } @@ -71,11 +71,11 @@ static void select_by_handle_type(const CurveEval &curve, } class HandleTypeFieldInput final : public GeometryFieldInput { - BezierSpline::HandleType type_; + HandleType type_; GeometryNodeCurveHandleMode mode_; public: - HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode) : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) @@ -92,14 +92,14 @@ class HandleTypeFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + const Curves *curve = curve_component.get_for_read(); if (curve == nullptr) { return {}; } if (domain == ATTR_DOMAIN_POINT) { Array<bool> selection(mask.min_array_size()); - select_by_handle_type(*curve, type_, mode_, selection); + select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection); return VArray<bool>::ForContainer(std::move(selection)); } return {}; @@ -124,7 +124,7 @@ class HandleTypeFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles &storage = node_storage(params.node()); - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage.handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 82621189964..d5769c691c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -14,13 +14,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); - if (!curve_set.has_curve()) { + if (!curve_set.has_curves()) { params.set_default_remaining_outputs(); return; } - const CurveEval &curve = *curve_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curves_for_read()); float length = 0.0f; - for (const SplinePtr &spline : curve.splines()) { + for (const SplinePtr &spline : curve->splines()) { length += spline->length(); } params.set_output("Length", length); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc index 9919e24473e..6c7d7ed375b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include <numeric> + #include "BLI_math_base_safe.h" + +#include "BKE_curves.hh" + #include "UI_interface.h" #include "UI_resources.h" + #include "node_geometry_util.hh" -#include <numeric> namespace blender::nodes::node_geo_curve_primitive_arc_cc { @@ -139,32 +143,24 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution, - const float3 a, - const float3 b, - const float3 c, - float angle_offset, - const bool connect_center, - const bool invert_arc, - float3 &r_center, - float3 &r_normal, - float &r_radius) +static Curves *create_arc_curve_from_points(const int resolution, + const float3 a, + const float3 b, + const float3 c, + float angle_offset, + const bool connect_center, + const bool invert_arc, + float3 &r_center, + float3 &r_normal, + float &r_radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const bool is_colinear = colinear_f3_f3_f3(a, b, c); @@ -254,7 +250,7 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = center; } @@ -263,36 +259,26 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut normal = -normal; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); r_center = center; r_radius = radius; r_normal = normal; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution, - const float radius, - const float start_angle, - const float sweep_angle, - const bool connect_center, - const bool invert_arc) +static Curves *create_arc_curve_from_radius(const int resolution, + const float radius, + const float start_angle, + const float sweep_angle, + const bool connect_center, + const bool invert_arc) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle; @@ -305,13 +291,11 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = float3(0.0f, 0.0f, 0.0f); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -322,35 +306,35 @@ static void node_geo_exec(GeoNodeExecParams params) switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: { - std::unique_ptr<CurveEval> curve; float3 r_center, r_normal; float r_radius; - curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float3>("Start"), - params.extract_input<float3>("Middle"), - params.extract_input<float3>("End"), - params.extract_input<float>("Offset Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc"), - r_center, - r_normal, - r_radius); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_points( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float3>("Start"), + params.extract_input<float3>("Middle"), + params.extract_input<float3>("End"), + params.extract_input<float>("Offset Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc"), + r_center, + r_normal, + r_radius); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); params.set_output("Center", r_center); params.set_output("Normal", r_normal); params.set_output("Radius", r_radius); break; } case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: { - std::unique_ptr<CurveEval> curve; - curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float>("Radius"), - params.extract_input<float>("Start Angle"), - params.extract_input<float>("Sweep Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc")); - - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_radius( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float>("Radius"), + params.extract_input<float>("Start Angle"), + params.extract_input<float>("Sweep Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc")); + + params.set_output("Curve", GeometrySet::create_with_curves(curves)); break; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index c6b9018f0db..78e1613b630 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -69,21 +69,30 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( spline->resize(2); MutableSpan<float3> positions = spline->positions(); - spline->handle_types_left().fill(BezierSpline::HandleType::Align); - spline->handle_types_right().fill(BezierSpline::HandleType::Align); + spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN); spline->radii().fill(1.0f); spline->tilts().fill(0.0f); positions.first() = start; positions.last() = end; + MutableSpan<float3> handles_right = spline->handle_positions_right(); + MutableSpan<float3> handles_left = spline->handle_positions_left(); + if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { - spline->set_handle_position_right(0, start_handle_right); - spline->set_handle_position_left(1, end_handle_left); + handles_left.first() = 2.0f * start - start_handle_right; + handles_right.first() = start_handle_right; + + handles_left.last() = end_handle_left; + handles_right.last() = 2.0f * end - end_handle_left; } else { - spline->set_handle_position_right(0, start + start_handle_right); - spline->set_handle_position_left(1, end + end_handle_left); + handles_left.first() = start - start_handle_right; + handles_right.first() = start + start_handle_right; + + handles_left.last() = end + end_handle_left; + handles_right.last() = end - end_handle_left; } curve->add_spline(std::move(spline)); @@ -104,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<float3>("End Handle"), std::max(params.extract_input<int>("Resolution"), 1), mode); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 44505b61a27..874e29dda86 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -92,7 +92,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_point_circle_curve( +static Curves *create_point_circle_curve( const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center) { if (colinear_f3_f3_f3(p1, p2, p3)) { @@ -100,11 +100,11 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( return nullptr; } - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); float3 center; /* Midpoints of `P1->P2` and `P2->P3`. */ @@ -147,24 +147,17 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - r_center = center; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolution, - const float radius) +static Curves *create_radius_circle_curve(const int resolution, const float radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(resolution); for (int i : IndexRange(resolution)) { @@ -173,12 +166,8 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio const float y = radius * sin(theta); positions[i] = float3(x, y, 0.0f); } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -187,23 +176,23 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { float3 center_point; - curve = create_point_circle_curve(params.extract_input<float3>("Point 1"), - params.extract_input<float3>("Point 2"), - params.extract_input<float3>("Point 3"), - std::max(params.extract_input<int>("Resolution"), 3), - center_point); + curves = create_point_circle_curve(params.extract_input<float3>("Point 1"), + params.extract_input<float3>("Point 2"), + params.extract_input<float3>("Point 3"), + std::max(params.extract_input<int>("Resolution"), 3), + center_point); params.set_output("Center", center_point); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) { - curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), - params.extract_input<float>("Radius")); + curves = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), + params.extract_input<float>("Radius")); } - if (curve) { - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + if (curves) { + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index d11af3b1cc0..2e2f4254752 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -60,39 +60,28 @@ static void node_update(bNodeTree *ntree, bNode *node) ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); } -static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end) +static Curves *create_point_line_curve(const float3 start, const float3 end) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = end; - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = end; + + return curves_id; } -static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start, - const float3 direction, - const float length) +static Curves *create_direction_line_curve(const float3 start, + const float3 direction, + const float length) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = math::normalize(direction) * length + start; - - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = math::normalize(direction) * length + start; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -100,18 +89,18 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves = nullptr; if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) { - curve = create_point_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("End")); + curves = create_point_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("End")); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION) { - curve = create_direction_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("Direction"), - params.extract_input<float>("Length")); + curves = create_direction_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("Direction"), + params.extract_input<float>("Length")); } - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_line_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 456f6e55c1e..37810ccaff5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc { @@ -28,18 +28,15 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, - const float3 p2, - const float3 p3, - const int resolution) +static Curves *create_quadratic_bezier_curve(const float3 p1, + const float3 p2, + const float3 p3, + const int resolution) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); - spline->resize(resolution + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float step = 1.0f / resolution; for (const int i : IndexRange(resolution + 1)) { @@ -49,19 +46,17 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, positions[i] = math::interpolate(q1, q2, factor); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve( + Curves *curves = create_quadratic_bezier_curve( params.extract_input<float3>("Start"), params.extract_input<float3>("Middle"), params.extract_input<float3>("End"), std::max(params.extract_input<int>("Resolution"), 3)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index b6a847eebf4..ad3123a6a4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -216,13 +216,11 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode; - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(4); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - MutableSpan<float3> positions = spline->positions(); + Curves *curves_id = bke::curves_new_nomain_single(4, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; + + MutableSpan<float3> positions = curves.positions(); switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE: @@ -262,9 +260,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves_id)); } } // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index f448ddabd2b..22619577d04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -35,26 +35,23 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, - const int resolution, - const float start_radius, - const float end_radius, - const float height, - const bool direction) +static Curves *create_spiral_curve(const float rotations, + const int resolution, + const float start_radius, + const float end_radius, + const float height, + const bool direction) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; const float delta_height = height / (float)totalpoints; const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * (direction ? 1.0f : -1.0f); - spline->resize(totalpoints + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + Curves *curves_id = bke::curves_new_nomain_single(totalpoints + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + MutableSpan<float3> positions = curves.positions(); for (const int i : IndexRange(totalpoints + 1)) { const float theta = i * delta_theta; @@ -66,9 +63,7 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, positions[i] = {x, y, z}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -79,14 +74,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> curve = create_spiral_curve( - rotations, - std::max(params.extract_input<int>("Resolution"), 1), - params.extract_input<float>("Start Radius"), - params.extract_input<float>("End Radius"), - params.extract_input<float>("Height"), - params.extract_input<bool>("Reverse")); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_spiral_curve(rotations, + std::max(params.extract_input<int>("Resolution"), 1), + params.extract_input<float>("Start Radius"), + params.extract_input<float>("End Radius"), + params.extract_input<float>("Height"), + params.extract_input<bool>("Reverse")); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_spiral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 5969af43bc1..e7e899881cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -33,19 +33,16 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("An attribute field with a selection of the outer points")); } -static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, - const float outer_radius, - const float twist, - const int points) +static Curves *create_star_curve(const float inner_radius, + const float outer_radius, + const float twist, + const int points) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(true); + Curves *curves_id = bke::curves_new_nomain_single(points * 2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(points * 2); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(points); for (const int i : IndexRange(points)) { @@ -58,10 +55,7 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, positions[i * 2 + 1] = {inner_x, inner_y, 0.0f}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - - return curve; + return curves_id; } static void create_selection_output(CurveComponent &component, @@ -78,12 +72,11 @@ static void create_selection_output(CurveComponent &component, static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_star_curve( - std::max(params.extract_input<float>("Inner Radius"), 0.0f), - std::max(params.extract_input<float>("Outer Radius"), 0.0f), - params.extract_input<float>("Twist"), - std::max(params.extract_input<int>("Points"), 3)); - GeometrySet output = GeometrySet::create_with_curve(curve.release()); + Curves *curves = create_star_curve(std::max(params.extract_input<float>("Inner Radius"), 0.0f), + std::max(params.extract_input<float>("Outer Radius"), 0.0f), + params.extract_input<float>("Twist"), + std::max(params.extract_input<int>("Points"), 3)); + GeometrySet output = GeometrySet::create_with_curves(curves); if (params.output_is_required("Outer Points")) { StrongAnonymousAttributeID attribute_output("Outer Points"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index d2afeaa7094..c5814a9a1dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -158,7 +158,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src) static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - const CurveEval *input_curve = component->get_for_read(); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read()); GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); @@ -235,14 +235,14 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component static void geometry_set_curve_resample(GeometrySet &geometry_set, const SampleModeParam &mode_param) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } std::unique_ptr<CurveEval> output_curve = resample_curve( geometry_set.get_component_for_read<CurveComponent>(), mode_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 0ef3230937b..8393f9615aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -20,7 +20,7 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -34,13 +34,15 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - CurveEval &curve = *component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { for (const int i : range) { splines[selection[i]]->reverse(); } }); + + component.replace(curve_eval_to_curves(*curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 152828b284c..6661d03a851 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -122,12 +122,13 @@ class SampleCurveFunction : public fn::MultiFunction { } }; - if (!geometry_set_.has_curve()) { + if (!geometry_set_.has_curves()) { return return_default(); } const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>(); - const CurveEval *curve = curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); Span<SplinePtr> splines = curve->splines(); if (splines.is_empty()) { return return_default(); @@ -234,12 +235,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveEval *curve = component->get_for_read(); - if (curve == nullptr) { + if (!component->has_curves()) { params.set_default_remaining_outputs(); return; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read()); + if (curve->splines().is_empty()) { params.set_default_remaining_outputs(); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 3d7b2fddf72..e8da4154586 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -33,20 +33,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -60,14 +60,14 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier_spline = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -77,18 +77,18 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } has_bezier_spline = true; BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -108,6 +108,8 @@ static void node_geo_exec(GeoNodeExecParams params) } bezier_spline.mark_cache_invalid(); } + + curve_component.replace(curve_eval_to_curves(*curve)); }); if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index a303be99242..3edaccba506 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -100,18 +100,22 @@ static Array<float> curve_length_point_domain(const CurveEval &curve) MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; spline_factors.first() = 0.0f; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -201,8 +205,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_curve_parameter_varray(*curve, mask, domain); } } @@ -234,8 +239,8 @@ class CurveLengthFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); return construct_curve_length_varray(*curve, mask, domain); } } @@ -267,8 +272,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_index_on_spline_varray(*curve, mask, domain); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 4e4cabd3c33..55610ec86ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -259,8 +259,8 @@ static SplinePtr poly_to_bezier(const Spline &input) output->positions().copy_from(input.positions()); output->radii().copy_from(input.radii()); output->tilts().copy_from(input.tilts()); - output->handle_types_left().fill(BezierSpline::HandleType::Vector); - output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -298,8 +298,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode); scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left()); scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right()); - output->handle_types_left().fill(BezierSpline::HandleType::Align); - output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(nurbs_spline, *output); output->attributes.reallocate(output->size()); @@ -315,11 +315,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 4) { params.error_message_add( NodeWarningType::Info, @@ -327,6 +327,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params return input.copy(); } return nurbs_to_bezier(input); + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -335,12 +339,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -355,45 +362,48 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + Span<SplinePtr> src_splines = curve->splines(); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - new_curve->resize(curve.splines().size()); + new_curve->resize(src_splines.size()); - threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) { + threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { switch (output_type) { case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params); + new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params); break; case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]); break; } } else { - new_curve->splines()[i] = curve.splines()[i]->copy(); + new_curve->splines()[i] = src_splines[i]->copy(); } } }); - new_curve->attributes = curve.attributes; - geometry_set.replace_curve(new_curve.release()); + new_curve->attributes = curve->attributes; + geometry_set.replace_curve(curve_eval_to_curves(*new_curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 9daf3ff0594..bbe57b2b3fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -93,8 +93,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -104,10 +104,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -118,10 +118,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -169,8 +169,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -217,26 +217,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -320,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -336,9 +340,9 @@ static void node_geo_exec(GeoNodeExecParams params) if (cuts.is_single() && cuts.get_internal_single() < 1) { return; } - - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); - geometry_set.replace_curve(output_curve.release()); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index ed497b6fbe0..e7a8c61290b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,14 +27,16 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, const bool fill_caps) { - const CurveEval *curve = geometry_set.get_curve_for_read(); - const CurveEval *profile_curve = profile_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Curves *profile_curves = profile_set.get_curves_for_read(); - if (profile_curve == nullptr) { + if (profile_curves == nullptr) { Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { + const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves); Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } @@ -46,10 +48,10 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); const bool fill_caps = params.extract_input<bool>("Fill Caps"); - bool has_curve = false; + bool has_curves = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { - has_curve = true; + if (geometry_set.has_curves()) { + has_curves = true; geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 7481f7248a1..1eb18b2f910 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -321,15 +321,16 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); @@ -339,7 +340,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( - points, curve, attribute_outputs); + points, *curve, attribute_outputs); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: @@ -351,7 +352,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } - copy_spline_domain_attributes(curve, offsets, points); + copy_spline_domain_attributes(*curve, offsets, points); if (!point_attributes.rotations.is_empty()) { curve_create_default_rotation_attribute( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index abc5b1649c7..a3dab1b50fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -367,15 +367,18 @@ static void trim_spline(SplinePtr &spline, const Spline::LookupResult end) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: trim_bezier_spline(*spline, start, end); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: trim_poly_spline(*spline, start, end); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } spline->mark_cache_invalid(); } @@ -400,8 +403,8 @@ static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &l const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( trim.left_index, trim.right_index, trim.factor); bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BezierSpline::HandleType::Free; - bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; + bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; bezier.handle_positions_left().first() = new_point.left_handle; bezier.handle_positions_right().first() = new_point.right_handle; @@ -477,15 +480,18 @@ static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::Look static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: to_single_point_bezier(*spline, lookup); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: to_single_point_poly(*spline, lookup); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } } @@ -494,7 +500,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, Field<float> &start_field, Field<float> &end_field) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -509,8 +515,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0); const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1); - CurveEval &curve = *geometry_set.get_curve_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { @@ -559,6 +565,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } } }); + + geometry_set.replace_curve(curve_eval_to_curves(*curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 2eeb957c123..3baee8a25bb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -332,9 +332,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask); copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -343,12 +343,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data_based_on_mask(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -471,9 +475,9 @@ static void separate_curve_selection(GeometrySet &geometry_set, selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> r_curve = curve_separate( - *src_component.get_for_read(), selection, selection_domain, invert); + *curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert); if (r_curve) { - geometry_set.replace_curve(r_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*r_curve)); } else { geometry_set.replace_curve(nullptr); @@ -1282,7 +1286,7 @@ void separate_geometry(GeometrySet &geometry_set, some_valid_domain = true; } } - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert); some_valid_domain = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 51932d341bc..bdd4d74fe4b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -152,6 +152,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( } KDTree_3d *kdtree = build_kdtree(positions); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); for (const int i : positions.index_range()) { if (elimination_mask[i]) { @@ -176,8 +177,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( }, &callback_data); } - - BLI_kdtree_3d_free(kdtree); } BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index a526f4f9e65..5a2c32a6c8e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -272,7 +272,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The * `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and * `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the - * corner in the i-th sorted polygon. + * corner in the i-th sorted polygon. If the polygons couldn't be sorted, `false` is returned. * * How the faces are sorted (see diagrams below): * (For this explanation we'll assume all faces are oriented clockwise) @@ -321,7 +321,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * - Finally if we are in the normal case we also need to add the last "shared edge" to close the * loop. */ -static void sort_vertex_polys(const Mesh &mesh, +static bool sort_vertex_polys(const Mesh &mesh, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, @@ -330,7 +330,7 @@ static void sort_vertex_polys(const Mesh &mesh, MutableSpan<int> r_sorted_corners) { if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { - return; + return true; } /* For each polygon store the two corners whose edge contains the vertex. */ @@ -434,8 +434,11 @@ static void sort_vertex_polys(const Mesh &mesh, break; } } - - BLI_assert(j != connected_polygons.size()); + if (j == connected_polygons.size()) { + /* The vertex is not manifold because the polygons around the vertex don't form a loop, and + * hence can't be sorted. */ + return false; + } std::swap(connected_polygons[i + 1], connected_polygons[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); @@ -445,6 +448,7 @@ static void sort_vertex_polys(const Mesh &mesh, /* Shared edge between first and last polygon. */ r_shared_edges.last() = shared_edge_i; } + return true; } /** @@ -637,19 +641,26 @@ static void calc_dual_mesh(GeometrySet &geometry_set, } MutableSpan<int> loop_indices = vertex_poly_indices[i]; Array<int> sorted_corners(loop_indices.size()); + bool vertex_ok = true; if (vertex_types[i] == VertexType::Normal) { Array<int> shared_edges(loop_indices.size()); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); } else { Array<int> shared_edges(loop_indices.size() - 1); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); + } + if (!vertex_ok) { + /* The sorting failed which means that the vertex is non-manifold and should be ignored + * further on. */ + vertex_types[i] = VertexType::NonManifold; + continue; } - vertex_corners[i] = sorted_corners; + vertex_corners[i] = std::move(sorted_corners); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc new file mode 100644 index 00000000000..1ceab18c01b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -0,0 +1,1119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_noise.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_duplicate_elements_cc { + +NODE_STORAGE_FUNCS(NodeGeometryDuplicateElements); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field(); + b.add_input<decl::Int>(N_("Amount")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The number of duplicates to create for each element")); + + b.add_output<decl::Geometry>(N_("Geometry")) + .description( + N_("The duplicated geometry only. The output does not contain the original geometry")); + b.add_output<decl::Int>(N_("Duplicate Index")) + .field_source() + .description(N_("The indices of the duplicates for each element")); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__); + data->domain = ATTR_DOMAIN_POINT; + node->storage = data; +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +struct IndexAttributes { + StrongAnonymousAttributeID duplicate_index; +}; + +/* -------------------------------------------------------------------- */ +/** \name Attribute Copy/Creation Functions + * \{ */ + +static void gather_attributes_without_id(const GeometrySet &geometry_set, + const GeometryComponentType component_type, + const Span<std::string> skip_attributes, + const bool include_instances, + Map<AttributeIDRef, AttributeKind> &r_gathered_attributes) +{ + geometry_set.gather_attributes_for_propagation( + {component_type}, component_type, include_instances, r_gathered_attributes); + for (const std::string &attribute : skip_attributes) { + r_gathered_attributes.remove(attribute); + } + r_gathered_attributes.remove("id"); +}; + +static IndexRange range_for_offsets_index(const Span<int> offsets, const int index) +{ + return {offsets[index], offsets[index + 1] - offsets[index]}; +} + +static Array<int> accumulate_counts_to_offsets(const IndexMask selection, + const VArray<int> &counts) +{ + Array<int> offsets(selection.size() + 1); + int dst_points_size = 0; + for (const int i_point : selection.index_range()) { + offsets[i_point] = dst_points_size; + dst_points_size += std::max(counts[selection[i_point]], 0); + } + offsets.last() = dst_points_size; + return offsets; +} + +/* Utility functions for threaded copying of attribute data where possible. */ +template<typename T> +static void threaded_slice_fill(Span<int> offsets, Span<T> src, MutableSpan<T> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } + }); +} + +template<typename T> +static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst) +{ + threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst[i] = src[mapping[i]]; + } + }); +} + +static void threaded_id_offset_copy(const Span<int> offsets, + const Span<int> src, + MutableSpan<int> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst[offsets[i]] = src[i]; + const int count = offsets[i + 1] - offsets[i]; + for (const int i_duplicate : IndexRange(1, count - 1)) { + dst[offsets[i] + i_duplicate] = noise::hash(src[i], i_duplicate); + } + } + }); +} + +/** Create the copy indices for the duplication domain. */ +static void create_duplicate_index_attribute(GeometryComponent &component, + const AttributeDomain output_domain, + const IndexMask selection, + const IndexAttributes &attributes, + const Span<int> offsets) +{ + OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>( + attributes.duplicate_index.get(), output_domain); + MutableSpan<int> duplicate_indices = copy_attribute.as_span(); + for (const int i : IndexRange(selection.size())) { + const IndexRange range = range_for_offsets_index(offsets, i); + MutableSpan<int> indices = duplicate_indices.slice(range); + for (const int i : indices.index_range()) { + indices[i] = i; + } + } + copy_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for the point domain elements. + */ +static void copy_stable_id_point(const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threaded_id_offset_copy(offsets, src, dst); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the edge domain. + */ +static void copy_stable_id_edges(const Mesh &mesh, + const IndexMask selection, + const Span<int> edge_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Span<MEdge> edges(mesh.medge, mesh.totedge); + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + if (edge_range.size() == 0) { + continue; + } + const MEdge &edge = edges[i_edge]; + const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2}; + + dst[vert_range[0]] = src[edge.v1]; + dst[vert_range[1]] = src[edge.v2]; + for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) { + dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate); + dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate); + } + } + }); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the face domain. + * + * This function could be threaded in the future, but since it is only 1 attribute and the + * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it? + */ +static void copy_stable_id_faces(const Mesh &mesh, + const IndexMask selection, + const Span<int> poly_offsets, + const Span<int> vert_mapping, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + int loop_index = 0; + for (const int i_poly : selection.index_range()) { + const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); + if (range.size() == 0) { + continue; + } + const MPoly &source = polys[i_poly]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) { + for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) { + if (i_duplicate == 0) { + dst[loop_index] = src[vert_mapping[loop_index]]; + } + else { + dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate); + } + loop_index++; + } + } + } + + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. In the spline case, copy the entire spline's points to the + * destination, + * then loop over the remaining ones point by point, hashing their ids to the new ids. + */ +static void copy_stable_id_splines(const CurveEval &curve, + const IndexMask selection, + const Span<int> curve_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Array<int> control_point_offsets = curve.control_point_offsets(); + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Array<int> curve_point_offsets(selection.size() + 1); + int dst_point_size = 0; + for (const int i_curve : selection.index_range()) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + curve_point_offsets[i_curve] = dst_point_size; + dst_point_size += curve_range.size() * spline_size; + } + curve_point_offsets.last() = dst_point_size; + + threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i_curve : range) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + dst.slice(curve_point_offsets[i_curve], spline_size) + .copy_from(src.slice(control_point_offsets[i_curve], spline_size)); + for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) { + for (const int i_point : IndexRange(spline_size)) { + dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash( + src[control_point_offsets[i_curve] + i_point], i_duplicate); + } + } + } + }); + dst_attribute.save(); +} + +/* The attributes for the point (also instance) duplicated elements are stored sequentially + * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if + * elements are ordered differently a custom function is called to copy the attributes. + */ + +static void copy_point_attributes_without_id(GeometrySet &geometry_set, + const GeometryComponentType component_type, + const bool include_instances, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, component_type, {}, include_instances, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) { + continue; + } + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src = src_attribute.varray.typed<T>(); + MutableSpan<T> dst = dst_attribute.as_span<T>(); + threaded_slice_fill<T>(offsets, src, dst); + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for spline duplicates. If copying the spline domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const Span<std::string> attributes_to_ignore, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_CURVE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are + * copied with an offset fill, for point domain a mapping is used. + */ +static void copy_edge_attributes_without_id(GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + const AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_EDGE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for face duplicates. If copying the face domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_face_attributes_without_id(GeometrySet &geometry_set, + const Span<int> edge_mapping, + const Span<int> vert_mapping, + const Span<int> loop_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_FACE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_EDGE: + threaded_mapped_copy<T>(edge_mapping, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(vert_mapping, src, dst); + break; + case ATTR_DOMAIN_CORNER: + threaded_mapped_copy<T>(loop_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplication Functions + * \{ */ + +static void duplicate_splines(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_curves()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_CURVE); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> curve_offsets(selection.size() + 1); + + int dst_splines_size = 0; + int dst_points_size = 0; + for (const int i_spline : selection.index_range()) { + int count = std::max(counts[selection[i_spline]], 0); + curve_offsets[i_spline] = dst_splines_size; + dst_splines_size += count; + dst_points_size += count * curve->splines()[selection[i_spline]]->size(); + } + curve_offsets.last() = dst_splines_size; + + Array<int> control_point_offsets = curve->control_point_offsets(); + Array<int> point_mapping(dst_points_size); + + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + int point_index = 0; + for (const int i_spline : selection.index_range()) { + const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline); + for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) { + SplinePtr spline = curve->splines()[selection[i_spline]]->copy(); + for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) { + point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point; + } + new_curve->add_spline(std::move(spline)); + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + Vector<std::string> skip( + {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"}); + + copy_spline_attributes_without_id( + geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component); + + copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); + } + + geometry_set.replace_curve(dst_component.get_for_write()); +} + +static void duplicate_faces(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + + GeometryComponent &component = geometry_set.get_component_for_write(GEO_COMPONENT_TYPE_MESH); + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator(field_context, domain_size); + + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + MeshComponent &mesh_component = static_cast<MeshComponent &>(component); + const Mesh &mesh = *mesh_component.get_for_read(); + Span<MVert> verts(mesh.mvert, mesh.totvert); + Span<MEdge> edges(mesh.medge, mesh.totedge); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + Span<MLoop> loops(mesh.mloop, mesh.totloop); + + int total_polys = 0; + int total_loops = 0; + Array<int> offsets(selection.size() + 1); + for (const int i_selection : selection.index_range()) { + const int count = std::max(counts[selection[i_selection]], 0); + offsets[i_selection] = total_polys; + total_polys += count; + total_loops += count * polys[selection[i_selection]].totloop; + } + offsets[selection.size()] = total_polys; + + Array<int> vert_mapping(total_loops); + Array<int> edge_mapping(total_loops); + Array<int> loop_mapping(total_loops); + + Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); + + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); + MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + + int poly_index = 0; + int loop_index = 0; + for (const int i_selection : selection.index_range()) { + const IndexRange poly_range = range_for_offsets_index(offsets, i_selection); + + const MPoly &source = polys[selection[i_selection]]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) { + new_poly[poly_index] = source; + new_poly[poly_index].loopstart = loop_index; + for (const int i_loops : IndexRange(source.totloop)) { + const MLoop ¤t_loop = loops[source.loopstart + i_loops]; + loop_mapping[loop_index] = source.loopstart + i_loops; + new_verts[loop_index] = verts[current_loop.v]; + vert_mapping[loop_index] = current_loop.v; + new_edges[loop_index] = edges[current_loop.e]; + edge_mapping[loop_index] = current_loop.e; + new_edges[loop_index].v1 = loop_index; + if (i_loops + 1 != source.totloop) { + new_edges[loop_index].v2 = loop_index + 1; + } + else { + new_edges[loop_index].v2 = new_poly[poly_index].loopstart; + } + new_loops[loop_index].v = loop_index; + new_loops[loop_index].e = loop_index; + loop_index++; + } + poly_index++; + } + } + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_face_attributes_without_id(geometry_set, + edge_mapping, + vert_mapping, + loop_mapping, + offsets, + mesh_component, + dst_component); + + copy_stable_id_faces(mesh, selection, offsets, vert_mapping, mesh_component, dst_component); + mesh_component.replace(dst_component.get_for_write()); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_FACE, selection, attributes, offsets); + } +} + +static void duplicate_edges(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + }; + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_MESH); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> verts(mesh->mvert, mesh->totvert); + Span<MEdge> edges(mesh->medge, mesh->totedge); + + Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + + Array<int> vert_orig_indices(edge_offsets.last() * 2); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const MEdge &edge = edges[i_edge]; + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + + for (const int i_duplicate : IndexRange(edge_range.size())) { + vert_orig_indices[vert_range[i_duplicate * 2]] = edge.v1; + vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge.v2; + } + } + }); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + for (const int i_duplicate : IndexRange(edge_range.size())) { + MEdge &new_edge = new_edges[edge_range[i_duplicate]]; + new_edge.v1 = vert_range[i_duplicate * 2]; + new_edge.v2 = vert_range[i_duplicate * 2] + 1; + } + } + }); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_edge_attributes_without_id( + geometry_set, vert_orig_indices, edge_offsets, src_component, dst_component); + + copy_stable_id_edges(*mesh, selection, edge_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_EDGE, selection, attributes, edge_offsets); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_curve(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + + Array<int> parent(domain_size); + int spline = 0; + for (const int i_spline : IndexRange(domain_size)) { + if (i_spline == control_point_offsets[spline + 1]) { + spline++; + } + parent[i_spline] = spline; + } + + for (const int i_point : selection) { + const IndexRange point_range = range_for_offsets_index(offsets, i_point); + for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) { + const SplinePtr &parent_spline = curve->splines()[parent[i_point]]; + switch (parent_spline->type()) { + case CurveType::CURVE_TYPE_BEZIER: { + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_NURBS: { + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_POLY: { + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(1); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_CATMULL_ROM: { + /* Catmull Rom curves are not supported yet. */ + break; + } + } + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + curve_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_mesh(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> src_verts(mesh->mvert, mesh->totvert); + + Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); + MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); + + threaded_slice_fill<MVert>(offsets.as_span(), src_verts, dst_verts); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_pointcloud(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last()); + PointCloudComponent dst_component; + dst_component.replace(pointcloud, GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets); + } + geometry_set.replace_pointcloud(pointcloud); +} + +static void duplicate_points(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh() && !geometry_set.has_curves() && !geometry_set.has_pointcloud()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true); + Vector<GeometryComponentType> types_to_keep; + for (const GeometryComponentType component_type : component_types) { + switch (component_type) { + case GEO_COMPONENT_TYPE_POINT_CLOUD: + types_to_keep.append(component_type); + duplicate_points_pointcloud( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_MESH: + types_to_keep.append(component_type); + duplicate_points_mesh( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_CURVE: + types_to_keep.append(component_type); + duplicate_points_curve( + component_type, count_field, selection_field, geometry_set, attributes); + break; + default: + break; + } + } + types_to_keep.append(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.keep_only(types_to_keep); +} + +static void duplicate_instances(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_instances()) { + geometry_set.clear(); + return; + } + + const InstancesComponent &src_instances = + *geometry_set.get_component_for_read<InstancesComponent>(); + + const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + if (offsets.last() == 0) { + geometry_set.clear(); + return; + } + + GeometrySet instances_geometry; + InstancesComponent &dst_instances = + instances_geometry.get_component_for_write<InstancesComponent>(); + dst_instances.resize(offsets.last()); + for (const int i_selection : selection.index_range()) { + const int count = offsets[i_selection + 1] - offsets[i_selection]; + if (count == 0) { + continue; + } + const int old_handle = src_instances.instance_reference_handles()[i_selection]; + const InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances.add_reference(reference); + const float4x4 transform = src_instances.instance_transforms()[i_selection]; + dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform); + dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle); + } + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_INSTANCES, true, offsets, src_instances, dst_instances); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_instances, ATTR_DOMAIN_INSTANCE, selection, attributes, offsets); + } + + geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.add(dst_instances); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const NodeGeometryDuplicateElements &storage = node_storage(params.node()); + const AttributeDomain duplicate_domain = AttributeDomain(storage.domain); + + Field<int> count_field = params.extract_input<Field<int>>("Amount"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + IndexAttributes attributes; + if (params.output_is_required("Duplicate Index")) { + attributes.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); + } + + if (duplicate_domain == ATTR_DOMAIN_INSTANCE) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + duplicate_instances(geometry_set, count_field, selection_field, attributes); + } + else { + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + switch (duplicate_domain) { + case ATTR_DOMAIN_CURVE: + duplicate_splines(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_FACE: + duplicate_faces(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_EDGE: + duplicate_edges(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_POINT: + duplicate_points(geometry_set, count_field, selection_field, attributes); + break; + default: + BLI_assert_unreachable(); + break; + } + }); + } + + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + + if (attributes.duplicate_index) { + params.set_output( + "Duplicate Index", + AnonymousAttributeFieldInput::Create<int>(std::move(attributes.duplicate_index), + params.attribute_producer_name())); + } + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes::node_geo_duplicate_elements_cc + +void register_node_type_geo_duplicate_elements() +{ + namespace file_ns = blender::nodes::node_geo_duplicate_elements_cc; + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY); + + node_type_storage(&ntype, + "NodeGeometryDuplicateElements", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, file_ns::node_init); + ntype.draw_buttons = file_ns::node_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} + +/** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 4e053dbc1a3..c03a340a0c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -25,6 +25,9 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); BMeshFromMeshParams bmesh_from_mesh_params{}; + bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX; BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params); BM_mesh_elem_table_ensure(bm, BM_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 5aeeb72961d..82584f6f413 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -302,7 +302,6 @@ static void extrude_mesh_vertices(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) @@ -626,7 +625,6 @@ static void extrude_mesh_edges(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /** @@ -995,7 +993,6 @@ static void extrude_mesh_face_regions(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */ @@ -1263,7 +1260,6 @@ static void extrude_individual_mesh_faces(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc new file mode 100644 index 00000000000..62af0476057 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Threshold") + .field_source() + .default_value(0.01f) + .subtype(PROP_DISTANCE) + .supports_field() + .description(N_("The distance a point can be from the surface before the face is no longer " + "considered planar")) + .min(0.0f); + b.add_output<decl::Bool>("Planar").field_source(); +} + +class PlanarFieldInput final : public GeometryFieldInput { + private: + Field<float> threshold_; + + public: + PlanarFieldInput(Field<float> threshold) + : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{context, mesh->totpoly}; + evaluator.add(threshold_); + evaluator.evaluate(); + const VArray<float> &thresholds = evaluator.get_evaluated<float>(0); + + Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}; + + auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool { + if (mesh->mpoly[i_poly].totloop <= 3) { + return true; + } + const int loopstart = mesh->mpoly[i_poly].loopstart; + const int loops = mesh->mpoly[i_poly].totloop; + Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops); + float3 reference_normal = poly_normals[i_poly]; + + float min = FLT_MAX; + float max = FLT_MIN; + + for (const int i_loop : poly_loops.index_range()) { + const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; + float dot = math::dot(reference_normal, vert); + if (dot > max) { + max = dot; + } + if (dot < min) { + min = dot; + } + } + return max - min < thresholds[i_poly] / 2.0f; + }; + + return component.attribute_try_adapt_domain<bool>( + VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 2356235652; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr; + } +}; + +static void geo_node_exec(GeoNodeExecParams params) +{ + Field<float> threshold = params.extract_input<Field<float>>("Threshold"); + Field<bool> planar_field{std::make_shared<PlanarFieldInput>(threshold)}; + params.set_output("Planar", std::move(planar_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc + +void register_node_type_geo_input_mesh_face_is_planar() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_face_is_planar_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, "Face is Planar", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::geo_node_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 4537721d173..f952e15fbbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -19,10 +19,10 @@ static void node_declare(NodeDeclarationBuilder &b) static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto length_fn = [splines](int i) { return splines[i]->length(); }; @@ -76,10 +76,10 @@ class SplineLengthFieldInput final : public GeometryFieldInput { static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto count_fn = [splines](int i) { return splines[i]->size(); }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index 9ae99b4d83e..435dd969c03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -52,18 +52,22 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) const Spline &spline = *splines[i]; MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())}; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_tangents(static_cast<const BezierSpline &>(spline), spline_tangents); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_tangents(static_cast<const PolySpline &>(spline), spline_tangents); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_tangents(static_cast<const NURBSpline &>(spline), spline_tangents); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -73,21 +77,12 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; + if (!component.has_curves()) { + return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return VArray<float3>::ForSpan(spline.evaluated_tangents()); - } - Array<float3> tangents = curve_tangent_point_domain(*curve); return VArray<float3>::ForContainer(std::move(tangents)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index df6d10991fb..61f719ade4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -195,35 +195,24 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + const Array<GeometryComponentType> types{ + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}, - GEO_COMPONENT_TYPE_INSTANCES, - false, - attributes_to_propagate); + types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate); attributes_to_propagate.remove("position"); - if (geometry_set.has<MeshComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<MeshComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<PointCloudComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<CurveComponent>(), - instance, - params, - attributes_to_propagate); + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + add_instances_from_component(instances, + *geometry_set.get_component_for_read(type), + instance, + params, + attributes_to_propagate); + } } + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 3284378a2cb..91cde52f9eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params) } std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curve(curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 1731ba64b97..c99b51ffd4c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -198,17 +198,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, Vector<float3> positions; Vector<float> radii; - if (r_geometry_set.has<MeshComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii); - } - if (r_geometry_set.has<PointCloudComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii); - } - if (r_geometry_set.has<CurveComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii); + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (r_geometry_set.has(type)) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(type), positions, radii); + } } const float max_radius = *std::max_element(radii.begin(), radii.end()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1797364ad72..231ef547a8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -141,10 +141,13 @@ static void raycast_to_mesh(IndexMask mask, { BVHTreeFromMesh tree_data; BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&tree_data); }); + if (tree_data.tree == nullptr) { - free_bvhtree_from_mesh(&tree_data); return; } + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); for (const int i : mask) { const float ray_length = ray_lengths[i]; @@ -197,10 +200,6 @@ static void raycast_to_mesh(IndexMask mask, } } } - - /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ - BLI_assert(tree_data.cached); - free_bvhtree_from_mesh(&tree_data); } class RaycastFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index fb648baad08..301410f5126 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -35,7 +35,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) } static void set_position_in_component(const GeometryNodeCurveHandleMode mode, - GeometryComponent &component, + CurveComponent &component, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) @@ -53,37 +53,31 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - CurveComponent *curve_component = static_cast<CurveComponent *>(&component); - CurveEval *curve = curve_component->get_for_write(); - - StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); int current_point = 0; int current_mask = 0; - for (const SplinePtr &spline : curve->splines()) { - if (spline->type() == Spline::Type::Bezier) { + if (spline->type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier = static_cast<BezierSpline &>(*spline); - for (int i : bezier.positions().index_range()) { + + bezier.ensure_auto_handles(); + for (const int i : bezier.positions().index_range()) { if (current_mask < selection.size() && selection[current_mask] == current_point) { if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN; } } else { - if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN; } } current_mask++; @@ -104,15 +98,35 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); - OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( - side, ATTR_DOMAIN_POINT, {0, 0, 0}); - MutableSpan<float3> position_mutable = positions.as_span(); - - for (int i : selection) { - position_mutable[i] = positions_input[i] + offsets_input[i]; + current_point = 0; + current_mask = 0; + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == CURVE_TYPE_BEZIER) { + BezierSpline &bezier = static_cast<BezierSpline &>(*spline); + for (const int i : bezier.positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier.set_handle_position_left(i, positions_input[i] + offsets_input[i]); + } + else { + bezier.set_handle_position_right(i, positions_input[i] + offsets_input[i]); + } + current_mask++; + } + current_point++; + } + } + else { + for ([[maybe_unused]] int i : spline->positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } } - positions.save(); + component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned); } static void node_geo_exec(GeoNodeExecParams params) @@ -127,9 +141,11 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve() && - geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { - has_bezier = true; + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER); + set_position_in_component(mode, geometry_set.get_component_for_write<CurveComponent>(), selection_field, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 2f59d008df0..a23a6c09551 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -44,7 +44,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_radius_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index b94782b8c4c..1155c97dc38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_tilt_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 361a75ee0a8..eb035aa9b6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -61,6 +61,41 @@ static void set_computed_position_and_offset(GeometryComponent &component, } break; } + case GEO_COMPONENT_TYPE_CURVE: { + if (component.attribute_exists("handle_right") && + component.attribute_exists("handle_left")) { + OutputAttribute_Typed<float3> handle_right_attribute = + component.attribute_try_get_for_output<float3>( + "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0}); + OutputAttribute_Typed<float3> handle_left_attribute = + component.attribute_try_get_for_output<float3>( + "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan<float3> handle_right = handle_right_attribute.as_span(); + MutableSpan<float3> handle_left = handle_left_attribute.as_span(); + + MutableSpan<float3> out_positions_span = positions.as_span(); + devirtualize_varray2( + in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + const float3 new_position = in_positions[i] + in_offsets[i]; + const float3 delta = new_position - out_positions_span[i]; + handle_right[i] += delta; + handle_left[i] += delta; + out_positions_span[i] = new_position; + } + }); + }); + + handle_right_attribute.save(); + handle_left_attribute.save(); + break; + } + else { + ATTR_FALLTHROUGH; + } + } default: { MutableSpan<float3> out_positions_span = positions.as_span(); if (in_positions.is_same(positions.varray())) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 70e363064cd..dc7f3b1343a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_cyclic_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index e4702035eec..da8d7bcf255 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -43,10 +43,12 @@ static void node_geo_exec(GeoNodeExecParams params) bool only_poly = true; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (only_poly) { - for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { - if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { + if (ELEM(spline->type(), CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS)) { only_poly = false; break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index ddc0bb2bc11..bc34a1a6f2c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -298,7 +298,8 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, layout.pivot_points.add_new(layout.char_codes[i], pivot_point); } - GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); + GeometrySet geometry_set_curve = GeometrySet::create_with_curves( + curve_eval_to_curves(*curve_eval)); handles.add_new(layout.char_codes[i], instance_component.add_reference(std::move(geometry_set_curve))); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 789478873f6..2d5b0e58367 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -778,7 +778,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { - if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { params.error_message_add(NodeWarningType::Error, TIP_("The source geometry must contain a mesh or a point cloud")); return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 5950a2a16d2..95cec8eab11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -10,6 +10,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" +#include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" @@ -125,8 +126,10 @@ static void translate_geometry_set(GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->translate(translation); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { translate_mesh(*mesh, translation); @@ -146,8 +149,10 @@ void transform_geometry_set(GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->transform(transform); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { transform_mesh(*mesh, transform); diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index d6a4af7ef39..c4befd5828c 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -171,7 +171,7 @@ static void group_verify_socket_list(bNodeTree &node_tree, BLI_addtail(&verify_lb, matching_socket); } else { - /* If there was no socket withe the same identifier already, simply create a new socket + /* If there was no socket with the same identifier already, simply create a new socket * based on the interface socket, which will already add it to the new list. */ add_new_socket_from_interface(node_tree, node, *interface_socket, in_out); } @@ -326,7 +326,7 @@ void ntree_update_reroute_nodes(bNodeTree *ntree) } /* Propagate socket types from right to left. This affects reroute nodes that haven't been - * changed in the the loop above. */ + * changed in the loop above. */ for (bNode *start_node : nodes_linked_with_reroutes) { LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc index 8a3b0258b55..a24b4379e69 100644 --- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc +++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc @@ -38,7 +38,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - float inverted = node->custom2 ? 1.0f : 0.0f; + float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f; float f_samples = divide_ceil_u(node->custom1, 4); return GPU_stack_link(mat, diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 2c5012216f2..e3ffd3cc823 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -151,7 +151,7 @@ static PyObject *pygpu_state_depth_test_set(PyObject *UNUSED(self), PyObject *va } PyDoc_STRVAR(pygpu_state_depth_test_get_doc, - ".. function:: blend_depth_test_get()\n" + ".. function:: depth_test_get()\n" "\n" " Current depth_test equation.\n" "\n"); @@ -179,7 +179,7 @@ static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *va } PyDoc_STRVAR(pygpu_state_depth_mask_get_doc, - ".. function:: depth_mask_set_get()\n" + ".. function:: depth_mask_get()\n" "\n" " Writing status in the depth component.\n"); static PyObject *pygpu_state_depth_mask_get(PyObject *UNUSED(self)) @@ -326,7 +326,7 @@ static PyObject *pygpu_state_front_facing_set(PyObject *UNUSED(self), PyObject * } PyDoc_STRVAR(pygpu_state_program_point_size_set_doc, - ".. function:: use_program_point_size(enable)\n" + ".. function:: program_point_size_set(enable)\n" "\n" " If enabled, the derived point size is taken from the (potentially clipped) " "shader builtin gl_PointSize.\n" diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index 4a2e5fdebb7..223c6ad5f7e 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -6,8 +6,8 @@ #pragma once -#if PY_VERSION_HEX < 0x03090000 -# error "Python 3.9 or greater is required, you'll need to update your Python." +#if PY_VERSION_HEX < 0x030a0000 +# error "Python 3.10 or greater is required, you'll need to update your Python." #endif #ifdef __cplusplus diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 89fb89cfd41..8ed156a7e55 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -76,11 +76,7 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len) x = 0x345678UL; i = 0; while (--len >= 0) { -#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */ y = _Py_HashDouble(NULL, (double)(array[i++])); -#else - y = _Py_HashDouble((double)(array[i++])); -#endif if (y == -1) { return -1; } diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 8adde14d82f..ead255a6716 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -1166,10 +1166,8 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO tree = BLI_bvhtree_new((int)tris_len, epsilon, PY_BVH_TREE_TYPE_DEFAULT, PY_BVH_AXIS_DEFAULT); if (tree) { orig_index = MEM_mallocN(sizeof(*orig_index) * (size_t)tris_len, __func__); - CustomData *pdata = &mesh->pdata; - orig_normal = CustomData_get_layer(pdata, CD_NORMAL); /* can be NULL */ - if (orig_normal) { - orig_normal = MEM_dupallocN(orig_normal); + if (!BKE_mesh_poly_normals_are_dirty(mesh)) { + orig_normal = MEM_dupallocN(BKE_mesh_poly_normals_ensure(mesh)); } for (i = 0; i < tris_len; i++, lt++) { diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 4971071316e..596adafb2c9 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -468,7 +468,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); - const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL); + const float(*precomputed_normals)[3] = BKE_mesh_poly_normals_are_dirty(me) ? + NULL : + BKE_mesh_poly_normals_ensure(me); const bool calculate_normal = precomputed_normals ? false : true; if (precomputed_normals != NULL) { diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index d99b2e729d1..a5c13c26590 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -467,7 +467,6 @@ static void do_multires_bake(MultiresBakeRender *bkr, MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL); float *pvtangent = NULL; ListBase threads; @@ -482,6 +481,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); if (require_tangent) { if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { @@ -497,7 +497,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, NULL, 0, vert_normals, - (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), + poly_normals, (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ /* result */ @@ -542,7 +542,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, handle->data.mlooptri = mlooptri; handle->data.mloop = mloop; handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */ + handle->data.precomputed_normals = (float *)poly_normals; /* don't strictly need this */ handle->data.w = ibuf->x; handle->data.h = ibuf->y; handle->data.lores_dm = dm; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index ed4326bce69..2a93fb2c46b 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1106,6 +1106,8 @@ static void do_render_compositor_scenes(Render *re) return; } + bool changed_scene = false; + /* now foreach render-result node we do a full render */ /* results are stored in a way compositor will find it */ GSet *scenes_rendered = BLI_gset_ptr_new(__func__); @@ -1118,11 +1120,20 @@ static void do_render_compositor_scenes(Render *re) do_render_compositor_scene(re, scene, cfra); BLI_gset_add(scenes_rendered, scene); node->typeinfo->updatefunc(restore_scene->nodetree, node); + + if (scene != re->scene) { + changed_scene = true; + } } } } } BLI_gset_free(scenes_rendered, NULL); + + if (changed_scene) { + /* If rendered another scene, switch back to the current scene with compositing nodes. */ + re->current_scene_update(re->suh, re->scene); + } } /* bad call... need to think over proper method still */ diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 3196ca08155..aa433eeed09 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -1918,7 +1918,7 @@ static void RVBlurBitmap2_float(float *map, int width, int height, float blur, i * I changed the math around to implement an actual Gaussian distribution. * * Watch out though, it tends to misbehave with large blur values on - * a small bitmap. Avoid avoid! */ + * a small bitmap. Avoid! */ float *temp = NULL, *swap; float *filter = NULL; diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index d87da1557e0..91b69bfe01f 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -549,18 +549,6 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, } } -static bool seq_orig_free_anims(Sequence *seq_iter, void *data) -{ - SessionUUID orig_seq_uuid = ((SeqIndexBuildContext *)data)->orig_seq_uuid; - - if (BLI_session_uuid_is_equal(&seq_iter->runtime.session_uuid, &orig_seq_uuid)) { - for (StripAnim *sanim = seq_iter->anims.first; sanim; sanim = sanim->next) { - IMB_close_anim_proxies(sanim->anim); - } - } - return true; -} - void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) { if (context->index_context) { @@ -570,9 +558,6 @@ void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) IMB_close_anim_proxies(sanim->anim); } - /* `context->seq_orig` may have been removed during building. */ - SEQ_for_each_callback(&context->scene->ed->seqbase, seq_orig_free_anims, context); - IMB_anim_index_rebuild_finish(context->index_context, stop); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c1281632cc1..21d76ce93bd 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -127,11 +127,7 @@ void WM_reinit_gizmomap_all(struct Main *bmain); */ void WM_script_tag_reload(void); -wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm, - const wmWindow *win_ignore, - const wmWindow *win, - const int mval[2], - int r_mval[2]); +wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2]); void WM_window_pixel_sample_read(const wmWindowManager *wm, const wmWindow *win, const int pos[2], @@ -1247,6 +1243,8 @@ enum { WM_JOB_TYPE_COMPOSITE, WM_JOB_TYPE_RENDER, WM_JOB_TYPE_RENDER_PREVIEW, /* UI preview */ + /** Job for the UI to load previews from the file system (uses OS thumbnail cache). */ + WM_JOB_TYPE_LOAD_PREVIEW, /* UI preview */ WM_JOB_TYPE_OBJECT_SIM_OCEAN, WM_JOB_TYPE_OBJECT_SIM_FLUID, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE, @@ -1425,15 +1423,17 @@ bool WM_window_modal_keymap_status_draw(struct bContext *C, */ void WM_event_print(const struct wmEvent *event); -int WM_event_modifier_flag(const struct wmEvent *event); - /** - * For modal callbacks, check configuration for how to interpret exit with tweaks. + * For modal callbacks, check configuration for how to interpret exit when dragging. */ -bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); +bool WM_event_is_modal_drag_exit(const struct wmEvent *event, + short init_event_type, + short init_event_val); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); +int WM_event_drag_direction(const wmEvent *event); + /** * Detect motion between selection (callers should only use this for selection picking), * typically mouse press/click events. @@ -1583,8 +1583,7 @@ bool WM_xr_action_create(wmXrData *xr, const char *action_set_name, const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, struct wmOperatorType *ot, struct IDProperty *op_properties, const char *haptic_name, @@ -1599,9 +1598,8 @@ bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path, - unsigned int count_subaction_paths, - const char **subaction_paths, - const char **component_paths, + const ListBase *user_paths, + const ListBase *component_paths, const float *float_thresholds, const eXrAxisFlag *axis_flags, const struct wmXrPose *poses); diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h index 32315d72ba7..068dbb32be2 100644 --- a/source/blender/windowmanager/WM_keymap.h +++ b/source/blender/windowmanager/WM_keymap.h @@ -42,8 +42,13 @@ void WM_keymap_clear(struct wmKeyMap *keymap); /** * Always add item. */ -wmKeyMapItem *WM_keymap_add_item( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_item(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); wmKeyMapItem *WM_keymap_add_item_copy(struct wmKeyMap *keymap, wmKeyMapItem *kmi_src); bool WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi); @@ -80,32 +85,43 @@ bool WM_keymap_item_compare(const struct wmKeyMapItem *k1, const struct wmKeyMap /** * Menu wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_menu( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_menu(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Pie-menu wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_menu_pie( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Panel (popover) wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_panel( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_panel(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Tool wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_tool( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); - -/** Useful for mapping numbers to an enum. */ -void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap, - const struct EnumPropertyItem *items, - const char *data_path, - int type_start, - int val, - int modifier, - int keymodifier); +wmKeyMapItem *WM_keymap_add_tool(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); wmKeyMap *WM_keymap_guess_from_context(const struct bContext *C); @@ -137,10 +153,20 @@ wmKeyMap *WM_modalkeymap_ensure(struct wmKeyConfig *keyconf, const char *idname, const struct EnumPropertyItem *items); wmKeyMap *WM_modalkeymap_find(struct wmKeyConfig *keyconf, const char *idname); -wmKeyMapItem *WM_modalkeymap_add_item( - struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value); -wmKeyMapItem *WM_modalkeymap_add_item_str( - struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value); +wmKeyMapItem *WM_modalkeymap_add_item(struct wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + int value); +wmKeyMapItem *WM_modalkeymap_add_item_str(struct wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + const char *value); const wmKeyMapItem *WM_modalkeymap_find_propvalue(const wmKeyMap *km, int propvalue); void WM_modalkeymap_assign(struct wmKeyMap *km, const char *opname); diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 4886e39943f..a9e1495d9bf 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -103,6 +103,7 @@ void WM_toolsystem_do_msg_notify_tag_refresh(struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +struct IDProperty *WM_toolsystem_ref_properties_get_idprops(struct bToolRef *tref); struct IDProperty *WM_toolsystem_ref_properties_ensure_idprops(struct bToolRef *tref); void WM_toolsystem_ref_properties_ensure_ex(struct bToolRef *tref, const char *idname, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4799483157c..c4858a8a1fa 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -225,35 +225,68 @@ typedef enum eOperatorPropTags { } eOperatorPropTags; #define OP_PROP_TAG_ADVANCED ((eOperatorPropTags)OP_PROP_TAG_ADVANCED) -/* ************** wmKeyMap ************************ */ - -/* modifier */ -#define KM_SHIFT 1 -#define KM_CTRL 2 -#define KM_ALT 4 -#define KM_OSKEY 8 - -/* Used for key-map item creation function arguments (never stored in DNA). */ -#define KM_SHIFT_ANY 16 -#define KM_CTRL_ANY 32 -#define KM_ALT_ANY 64 -#define KM_OSKEY_ANY 128 - -/* KM_MOD_ flags for `wmKeyMapItem` and `wmEvent.alt/shift/oskey/ctrl`. */ -/* note that KM_ANY and KM_NOTHING are used with these defines too */ +/* -------------------------------------------------------------------- */ +/** \name #wmKeyMapItem + * \{ */ + +/** + * Modifier keys, not actually used for #wmKeyMapItem (never stored in DNA), used for: + * - #wmEvent.modifier without the `KM_*_ANY` flags. + * - #WM_keymap_add_item & #WM_modalkeymap_add_item + */ +enum { + KM_SHIFT = (1 << 0), + KM_CTRL = (1 << 1), + KM_ALT = (1 << 2), + KM_OSKEY = (1 << 3), + + /* Used for key-map item creation function arguments. */ + KM_SHIFT_ANY = (1 << 4), + KM_CTRL_ANY = (1 << 5), + KM_ALT_ANY = (1 << 6), + KM_OSKEY_ANY = (1 << 7), +}; + +/* `KM_MOD_*` flags for #wmKeyMapItem and `wmEvent.alt/shift/oskey/ctrl`. */ +/* Note that #KM_ANY and #KM_NOTHING are used with these defines too. */ #define KM_MOD_HELD 1 -/* type: defined in wm_event_types.c */ -#define KM_TEXTINPUT -2 +/** + * #wmKeyMapItem.type + * NOTE: most types are defined in `wm_event_types.h`. + */ +enum { + KM_TEXTINPUT = -2, +}; -/* val */ -#define KM_ANY -1 -#define KM_NOTHING 0 -#define KM_PRESS 1 -#define KM_RELEASE 2 -#define KM_CLICK 3 -#define KM_DBL_CLICK 4 -#define KM_CLICK_DRAG 5 +/** #wmKeyMapItem.val */ +enum { + KM_ANY = -1, + KM_NOTHING = 0, + KM_PRESS = 1, + KM_RELEASE = 2, + KM_CLICK = 3, + KM_DBL_CLICK = 4, + KM_CLICK_DRAG = 5, +}; + +/** + * #wmKeyMapItem.direction + * + * Value of tweaks and line gestures. #KM_ANY (-1) works for this case too. + */ +enum { + KM_DIRECTION_N = 1, + KM_DIRECTION_NE = 2, + KM_DIRECTION_E = 3, + KM_DIRECTION_SE = 4, + KM_DIRECTION_S = 5, + KM_DIRECTION_SW = 6, + KM_DIRECTION_W = 7, + KM_DIRECTION_NW = 8, +}; + +/** \} */ /* ************** UI Handler ***************** */ @@ -470,6 +503,7 @@ typedef struct wmNotifier { #define NS_EDITMODE_ARMATURE (8 << 8) #define NS_MODE_POSE (9 << 8) #define NS_MODE_PARTICLE (10 << 8) +#define NS_EDITMODE_CURVES (11 << 8) /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16 << 8) @@ -493,7 +527,6 @@ typedef struct wmNotifier { /* ************** Gesture Manager data ************** */ /* wmGesture->type */ -#define WM_GESTURE_TWEAK 0 #define WM_GESTURE_LINES 1 #define WM_GESTURE_RECT 2 #define WM_GESTURE_CROSS_RECT 3 @@ -503,12 +536,15 @@ typedef struct wmNotifier { /** * wmGesture is registered to #wmWindow.gesture, handled by operator callbacks. - * Tweak gesture is builtin feature. */ typedef struct wmGesture { struct wmGesture *next, *prev; /** #wmEvent.type */ int event_type; + /** #wmEvent.modifier */ + uint8_t event_modifier; + /** #wmEvent.keymodifier */ + short event_keymodifier; /** Gesture type define. */ int type; /** bounds of region to draw gesture within. */ @@ -555,6 +591,22 @@ typedef struct wmGesture { /* ************** wmEvent ************************ */ +typedef enum eWM_EventFlag { + /** + * True if the operating system inverted the delta x/y values and resulting + * `prev_xy` values, for natural scroll direction. + * For absolute scroll direction, the delta must be negated again. + */ + WM_EVENT_SCROLL_INVERT = (1 << 0), + /** + * Generated by auto-repeat, note that this must only ever be set for keyboard events + * where `ISKEYBOARD(event->type) == true`. + * + * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this. + */ + WM_EVENT_IS_REPEAT = (1 << 1), +} eWM_EventFlag; + typedef struct wmTabletData { /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ int active; @@ -611,22 +663,10 @@ typedef struct wmEvent { /** From ghost, fallback if utf8 isn't set. */ char ascii; - /** - * Generated by auto-repeat, note that this must only ever be set for keyboard events - * where `ISKEYBOARD(event->type) == true`. - * - * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this. - */ - char is_repeat; - /** The previous value of `type`. */ short prev_type; /** The previous value of `val`. */ short prev_val; - /** The time when the key is pressed, see #PIL_check_seconds_timer. */ - double prev_click_time; - /** The location when the key is pressed (used to enforce drag thresholds). */ - int prev_click_xy[2]; /** * The previous value of #wmEvent.xy, * Unlike other previous state variables, this is set on any mouse motion. @@ -634,29 +674,40 @@ typedef struct wmEvent { */ int prev_xy[2]; - /** Modifier states. */ - /** 'oskey' is apple or windows-key, value denotes order of pressed. */ - short shift, ctrl, alt, oskey; + /** The `type` at the point of the click action. */ + short prev_click_type; + /** The time when the key is pressed, see #PIL_check_seconds_timer. */ + double prev_click_time; + /** The location when the key is pressed (used to enforce drag thresholds). */ + int prev_click_xy[2]; + /** The `modifier` at the point of the click action. */ + uint8_t prev_click_modifier; + /** The `keymodifier` at the point of the click action. */ + short prev_click_keymodifier; + + /** + * Modifier states. + * #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY is apple or windows-key. + */ + uint8_t modifier; + + /** The direction (for #KM_CLICK_DRAG events only). */ + int8_t direction; + /** Raw-key modifier (allow using any key as a modifier). */ short keymodifier; /** Tablet info, available for mouse move and button events. */ wmTabletData tablet; + eWM_EventFlag flag; + /* Custom data. */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ short custom; short customdata_free; - int pad2; /** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */ void *customdata; - - /** - * True if the operating system inverted the delta x/y values and resulting - * `prev_xy` values, for natural scroll direction. - * For absolute scroll direction, the delta must be negated again. - */ - char is_direction_inverted; } wmEvent; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index c46a2b6afe5..2971cdf40c3 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -640,24 +640,29 @@ wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_ensure(keyconf, name, modal_items); /* items for modal map */ - WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); - WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL); - WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM); WM_modalkeymap_add_item( - keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON); WM_modalkeymap_add_item( - keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); - WM_modalkeymap_add_item(keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF); WM_modalkeymap_add_item( - keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item( + keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF); - WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); - WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); - WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); - WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item( + keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item( + keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item( + keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item( + keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF); WM_modalkeymap_assign(keymap, "GIZMOGROUP_OT_gizmo_tweak"); @@ -709,24 +714,26 @@ static wmKeyMap *WM_gizmogroup_keymap_template_select_ex( const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R; const int action_mouse = (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE; #else - const int select_mouse = RIGHTMOUSE; - const int select_tweak = EVT_TWEAK_R; - const int action_mouse = LEFTMOUSE; + const int select_mouse = RIGHTMOUSE, select_mouse_val = KM_PRESS; + const int select_tweak = RIGHTMOUSE, select_tweak_val = KM_CLICK_DRAG; + const int action_mouse = LEFTMOUSE, action_mouse_val = KM_PRESS; #endif if (do_init) { - WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, KM_PRESS, KM_ANY, 0); - WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, KM_ANY, 0, 0); + WM_keymap_add_item( + km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, action_mouse_val, KM_ANY, 0, KM_ANY); + WM_keymap_add_item( + km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, select_tweak_val, 0, 0, KM_ANY); } if (do_init) { wmKeyMapItem *kmi = WM_keymap_add_item( - km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, 0, 0); + km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, 0, 0, KM_ANY); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "deselect", false); RNA_boolean_set(kmi->ptr, "toggle", false); kmi = WM_keymap_add_item( - km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, KM_SHIFT, 0); + km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, KM_SHIFT, 0, KM_ANY); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "deselect", false); RNA_boolean_set(kmi->ptr, "toggle", true); @@ -1129,7 +1136,8 @@ void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup) ARegion *region = CTX_wm_region(C); BLI_assert(region->gizmo_map == gzmap); /* Check if the tweak event originated from this region. */ - if ((win->tweak != NULL) && BLI_rcti_compare(®ion->winrct, &win->tweak->winrct)) { + if ((win->eventstate != NULL) && (win->event_queue_check_drag) && + BLI_rcti_isect_pt_v(®ion->winrct, win->eventstate->prev_click_xy)) { /* We need to run refresh again. */ gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH; WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup)); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 4aba287aefb..f1ac19f4651 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -726,7 +726,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos. * If there are no gizmos or only one - early exit, otherwise. * - * - Bind the depth buffer and and use selection picking logic. + * - Bind the depth buffer and use selection picking logic. * This is much slower than occlusion queries (since it's reading depths while drawing). * When there is a single gizmo under the cursor (quite common), early exit, otherwise. * @@ -819,8 +819,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, do_step[i] = WM_gizmo_context_check_drawstep(C, i); } - const int event_modifier = WM_event_modifier_flag(event); - LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) { /* If it were important we could initialize here, @@ -839,11 +837,11 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, } if (step == WM_GIZMOMAP_DRAWSTEP_3D) { wm_gizmogroup_intersectable_gizmos_to_list( - wm, gzgroup, event_modifier, &visible_3d_gizmos); + wm, gzgroup, event->modifier, &visible_3d_gizmos); } else if (step == WM_GIZMOMAP_DRAWSTEP_2D) { if ((gz = wm_gizmogroup_find_intersected_gizmo( - wm, gzgroup, C, event_modifier, event->mval, r_part))) { + wm, gzgroup, C, event->modifier, event->mval, r_part))) { break; } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 13bf902f02c..c333d8149ed 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -156,7 +156,6 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) win->gpuctx = NULL; win->eventstate = NULL; win->cursor_keymap_status = NULL; - win->tweak = NULL; #if defined(WIN32) || defined(__APPLE__) win->ime_data = NULL; #endif diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index f69a612df19..4ffb6b90e11 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -318,6 +318,10 @@ static wmDropBox *dropbox_active(bContext *C, wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { + if (drag->drop_state.ui_context) { + CTX_store_set(C, drag->drop_state.ui_context); + } + if (!drop->poll(C, drag, event)) { /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. * Instead show it only if the drop box could be used in principle, but the operator @@ -326,10 +330,6 @@ static wmDropBox *dropbox_active(bContext *C, } const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop); - if (drag->drop_state.ui_context) { - CTX_store_set(C, drag->drop_state.ui_context); - } - if (WM_operator_poll_context(C, drop->ot, opcontext)) { return drop; } @@ -651,8 +651,12 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } ID *id = BKE_libblock_find_name(bmain, asset_drag->id_type, name); - if (id) { - BKE_id_delete(bmain, id); + if (id != NULL) { + /* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will + * cause T95636. Note that we need first to add the user that we want to remove in + * #BKE_id_free_us. */ + id_us_plus(id); + BKE_id_free_us(bmain, id); } } diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 304d7f73eb1..ee13e1832ed 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -47,12 +47,7 @@ static void event_ids_from_type_and_value(const short type, RNA_enum_identifier(rna_enum_event_type_items, type, r_type_id); /* Value. */ - if (ISTWEAK(type)) { - RNA_enum_identifier(rna_enum_event_value_tweak_items, val, r_val_id); - } - else { - RNA_enum_identifier(rna_enum_event_value_all_items, val, r_val_id); - } + RNA_enum_identifier(rna_enum_event_value_items, val, r_val_id); } void WM_event_print(const wmEvent *event) @@ -80,12 +75,12 @@ void WM_event_print(const wmEvent *event) prev_type_id, event->prev_val, prev_val_id, - event->shift, - event->ctrl, - event->alt, - event->oskey, + (event->modifier & KM_SHIFT) != 0, + (event->modifier & KM_CTRL) != 0, + (event->modifier & KM_ALT) != 0, + (event->modifier & KM_OSKEY) != 0, event->keymodifier, - event->is_repeat, + (event->flag & WM_EVENT_IS_REPEAT) != 0, event->xy[0], event->xy[1], event->ascii, @@ -129,24 +124,6 @@ void WM_event_print(const wmEvent *event) /** \name Event Modifier/Type Queries * \{ */ -int WM_event_modifier_flag(const wmEvent *event) -{ - int flag = 0; - if (event->ctrl) { - flag |= KM_CTRL; - } - if (event->alt) { - flag |= KM_ALT; - } - if (event->shift) { - flag |= KM_SHIFT; - } - if (event->oskey) { - flag |= KM_OSKEY; - } - return flag; -} - bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) { /* Keyboard. */ @@ -178,13 +155,6 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma } } - /* Tweak. */ - if (mask & EVT_TYPE_MASK_TWEAK) { - if (ISTWEAK(event_type)) { - return true; - } - } - /* Action Zone. */ if (mask & EVT_TYPE_MASK_ACTIONZONE) { if (IS_EVENT_ACTIONZONE(event_type)) { @@ -201,34 +171,30 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma /** \name Event Motion Queries * \{ */ -bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) +bool WM_event_is_modal_drag_exit(const wmEvent *event, + const short init_event_type, + const short init_event_val) { - /* if the release-confirm userpref setting is enabled, - * tweak events can be canceled when mouse is released - */ + /* If the release-confirm preference setting is enabled, + * drag events can be canceled when mouse is released. */ if (U.flag & USER_RELEASECONFIRM) { /* option on, so can exit with km-release */ if (event->val == KM_RELEASE) { - switch (tweak_event) { - case EVT_TWEAK_L: - case EVT_TWEAK_M: - case EVT_TWEAK_R: - return 1; + if ((init_event_val == KM_CLICK_DRAG) && (event->type == init_event_type)) { + return 1; } } else { - /* if the initial event wasn't a tweak event then - * ignore USER_RELEASECONFIRM setting: see T26756. */ - if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { + /* If the initial event wasn't a drag event then + * ignore #USER_RELEASECONFIRM setting: see T26756. */ + if (init_event_val != KM_CLICK_DRAG) { return 1; } } } else { - /* this is fine as long as not doing km-release, otherwise - * some items (i.e. markers) being tweaked may end up getting - * dropped all over - */ + /* This is fine as long as not doing km-release, otherwise some items (i.e. markers) + * being tweaked may end up getting dropped all over. */ if (event->val != KM_RELEASE) { return 1; } @@ -249,7 +215,7 @@ bool WM_event_is_last_mousemove(const wmEvent *event) bool WM_event_is_mouse_drag(const wmEvent *event) { - return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); + return (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); } bool WM_event_is_mouse_drag_or_press(const wmEvent *event) @@ -258,6 +224,68 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } +int WM_event_drag_direction(const wmEvent *event) +{ + const int delta[2] = { + event->xy[0] - event->prev_click_xy[0], + event->xy[1] - event->prev_click_xy[1], + }; + + int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); + int val = KM_DIRECTION_W; + + if (theta == 0) { + val = KM_DIRECTION_E; + } + else if (theta == 1) { + val = KM_DIRECTION_NE; + } + else if (theta == 2) { + val = KM_DIRECTION_N; + } + else if (theta == 3) { + val = KM_DIRECTION_NW; + } + else if (theta == -1) { + val = KM_DIRECTION_SE; + } + else if (theta == -2) { + val = KM_DIRECTION_S; + } + else if (theta == -3) { + val = KM_DIRECTION_SW; + } + +#if 0 + /* debug */ + if (val == 1) { + printf("tweak north\n"); + } + if (val == 2) { + printf("tweak north-east\n"); + } + if (val == 3) { + printf("tweak east\n"); + } + if (val == 4) { + printf("tweak south-east\n"); + } + if (val == 5) { + printf("tweak south\n"); + } + if (val == 6) { + printf("tweak south-west\n"); + } + if (val == 7) { + printf("tweak west\n"); + } + if (val == 8) { + printf("tweak north-west\n"); + } +#endif + return val; +} + bool WM_cursor_test_motion_and_update(const int mval[2]) { static int mval_prev[2] = {-1, -1}; @@ -278,8 +306,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2]) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (ISMOUSE(event->prev_type)) { - BLI_assert(event->prev_type != MOUSEMOVE); + if (ISMOUSE(event->prev_click_type)) { + BLI_assert(event->prev_click_type != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ @@ -331,12 +359,6 @@ int WM_userdef_event_map(int kmitype) int WM_userdef_event_type_from_keymap_type(int kmitype) { switch (kmitype) { - case EVT_TWEAK_L: - return LEFTMOUSE; - case EVT_TWEAK_M: - return MIDDLEMOUSE; - case EVT_TWEAK_R: - return RIGHTMOUSE; case WHEELOUTMOUSE: return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; case WHEELINMOUSE: @@ -458,7 +480,7 @@ int WM_event_absolute_delta_x(const struct wmEvent *event) { int dx = event->xy[0] - event->prev_xy[0]; - if (!event->is_direction_inverted) { + if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) { dx = -dx; } @@ -469,7 +491,7 @@ int WM_event_absolute_delta_y(const struct wmEvent *event) { int dy = event->xy[1] - event->prev_xy[1]; - if (!event->is_direction_inverted) { + if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) { dy = -dy; } @@ -491,8 +513,8 @@ int WM_event_absolute_delta_y(const struct wmEvent *event) */ bool WM_event_is_ime_switch(const struct wmEvent *event) { - return event->val == KM_PRESS && event->type == EVT_SPACEKEY && - (event->ctrl || event->oskey || event->alt); + return (event->val == KM_PRESS) && (event->type == EVT_SPACEKEY) && + (event->modifier & (KM_CTRL | KM_OSKEY | KM_ALT)); } #endif diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 8a19c99f59b..375cc4e785e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -153,7 +153,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) win->eventstate->type = event->type; if (event->val == KM_PRESS) { - if (event->is_repeat == false) { + if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy); } } @@ -166,7 +166,7 @@ void wm_event_free(wmEvent *event) #ifndef NDEBUG /* Don't use assert here because it's fairly harmless in most cases, * more an issue of correctness, something we should avoid in general. */ - if (event->is_repeat && !ISKEYBOARD(event->type)) { + if ((event->flag & WM_EVENT_IS_REPEAT) && !ISKEYBOARD(event->type)) { printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__); WM_event_print(event); } @@ -739,7 +739,7 @@ void wm_event_handler_ui_cancel_ex(bContext *C, wm_event_init_from_window(win, &event); event.type = EVT_BUT_CANCEL; event.val = reactivate_button ? 0 : 1; - event.is_repeat = false; + event.flag = 0; handler->handle_fn(C, &event, handler->user_data); } } @@ -1982,7 +1982,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) return false; } - if (winevent->is_repeat) { + if (winevent->flag & WM_EVENT_IS_REPEAT) { if (kmi->flag & KMI_REPEAT_IGNORE) { return false; } @@ -2029,29 +2029,41 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) } } + if (kmi->val == KM_CLICK_DRAG) { + if (kmi->direction != KM_ANY) { + if (kmi->direction != winevent->direction) { + return false; + } + } + } + + const bool shift = (winevent->modifier & KM_SHIFT) != 0; + const bool ctrl = (winevent->modifier & KM_CTRL) != 0; + const bool alt = (winevent->modifier & KM_ALT) != 0; + const bool oskey = (winevent->modifier & KM_OSKEY) != 0; + /* Modifiers also check bits, so it allows modifier order. * Account for rare case of when these keys are used as the 'type' not as modifiers. */ if (kmi->shift != KM_ANY) { - if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) && + if ((shift != kmi->shift) && !(shift & kmi->shift) && !ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) { return false; } } if (kmi->ctrl != KM_ANY) { - if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) && + if (ctrl != kmi->ctrl && !(ctrl & kmi->ctrl) && !ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) { return false; } } if (kmi->alt != KM_ANY) { - if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) && + if (alt != kmi->alt && !(alt & kmi->alt) && !ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) { return false; } } if (kmi->oskey != KM_ANY) { - if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) && - (winevent->type != EVT_OSKEY)) { + if (oskey != kmi->oskey && !(oskey & kmi->oskey) && (winevent->type != EVT_OSKEY)) { return false; } } @@ -2771,7 +2783,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, { /* Drag events use the previous click location to highlight the gizmos, * Get the highlight again in case the user dragged off the gizmo. */ - const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG); + const bool is_event_drag = (event->val == KM_CLICK_DRAG); const bool is_event_modifier = ISKEYMODIFIER(event->type); /* Only keep the highlight if the gizmo becomes modal as result of event handling. * Without this check, even un-handled drag events will set the highlight if the drag @@ -2882,15 +2894,10 @@ static int wm_handlers_do_gizmo_handler(bContext *C, wmEvent event_test_click_drag = *event; event_test_click_drag.val = KM_CLICK_DRAG; - wmEvent event_test_tweak = *event; - event_test_tweak.type = EVT_TWEAK_L + (event->type - LEFTMOUSE); - event_test_tweak.val = KM_ANY; - LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { if ((kmi->flag & KMI_INACTIVE) == 0) { if (wm_eventmatch(&event_test_click, kmi) || - wm_eventmatch(&event_test_click_drag, kmi) || - wm_eventmatch(&event_test_tweak, kmi)) { + wm_eventmatch(&event_test_click_drag, kmi)) { wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { is_event_handle_all = true; @@ -3151,39 +3158,45 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - /* Test for CLICK_DRAG events. */ - if (wm_action_not_handled(action)) { - if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, event->prev_click_xy)) { - win->event_queue_check_drag_handled = true; - - int xy[2] = {UNPACK2(event->xy)}; - short val = event->val; - short type = event->type; - - copy_v2_v2_int(event->xy, event->prev_click_xy); - event->val = KM_CLICK_DRAG; - event->type = event->prev_type; - - CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); + /* Test for #WM_CLICK_DRAG events. */ + + /* NOTE(@campbellbarton): Ignore `action` so drag can be used for editors that use both click + * selection and passing through the drag action to box select. See #WM_generic_select_modal. + * In the case of marker select-drag the combinations of (pass-through / finished / modal) + * can accumulate to have flags set that they can't be properly interpreted here. + * Instead `win->event_queue_check_drag` is cleared in `wm_event_do_handlers`. */ + if (win->event_queue_check_drag) { + if (WM_event_drag_test(event, event->prev_click_xy)) { + win->event_queue_check_drag_handled = true; + const int direction = WM_event_drag_direction(event); + + const int prev_xy[2] = {UNPACK2(event->xy)}; + const short prev_val = event->val; + const short prev_type = event->type; + const uint8_t prev_modifier = event->modifier; + const short prev_keymodifier = event->keymodifier; + + copy_v2_v2_int(event->xy, event->prev_click_xy); + event->val = KM_CLICK_DRAG; + event->type = event->prev_click_type; + event->modifier = event->prev_click_modifier; + event->keymodifier = event->prev_click_keymodifier; + event->direction = direction; + + CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); + + action |= wm_handlers_do_intern(C, win, event, handlers); + + event->direction = 0; + event->keymodifier = prev_keymodifier; + event->modifier = prev_modifier; + event->val = prev_val; + event->type = prev_type; + copy_v2_v2_int(event->xy, prev_xy); - action |= wm_handlers_do_intern(C, win, event, handlers); - - event->val = val; - event->type = type; - copy_v2_v2_int(event->xy, xy); - - win->event_queue_check_click = false; - if (!wm_action_not_handled(action)) { - /* Only disable when handled as other handlers may use this drag event. */ - win->event_queue_check_drag = false; - } - } + win->event_queue_check_click = false; } } - else { - win->event_queue_check_drag = false; - } } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { /* All events that don't set wmEvent.prev_type must be ignored. */ @@ -3194,17 +3207,26 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) * wasn't handled, the KM_RELEASE will become a KM_CLICK */ if (event->val == KM_PRESS) { - if (event->is_repeat == false) { + if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { win->event_queue_check_click = true; win->event_queue_check_drag = true; win->event_queue_check_drag_handled = false; } } else if (event->val == KM_RELEASE) { - win->event_queue_check_drag = false; + if (win->event_queue_check_drag) { + if ((event->prev_click_type != event->type) && + (ISKEYMODIFIER(event->type) || (event->type == event->prev_click_keymodifier))) { + /* Support releasing modifier keys without canceling the drag event, see T89989. + * NOTE: this logic is replicated for tweak gestures. */ + } + else { + win->event_queue_check_drag = false; + } + } } - if (event->prev_type == event->type) { + if (event->prev_click_type == event->type) { if (event->val == KM_RELEASE) { if (event->prev_val == KM_PRESS) { @@ -3245,7 +3267,6 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } else { win->event_queue_check_click = false; - win->event_queue_check_drag = false; } } else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) { @@ -3692,8 +3713,10 @@ void wm_event_do_handlers(bContext *C) /* Check dragging, creates new event or frees, adds draw tag. */ wm_event_drag_and_drop_test(wm, win, event); - /* Builtin tweak, if action is break it removes tweak. */ - wm_tweakevent_test(C, event, action); + /* Builtin drag: #KM_CLICK_DRAG. */ + if (action & WM_HANDLER_BREAK) { + win->event_queue_check_drag = false; + } if ((action & WM_HANDLER_BREAK) == 0) { /* NOTE: setting subwin active should be done here, after modal handlers have been done. */ @@ -3795,7 +3818,7 @@ void wm_event_do_handlers(bContext *C) tevent.type = MOUSEMOVE; tevent.prev_xy[0] = tevent.xy[0]; tevent.prev_xy[1] = tevent.xy[1]; - tevent.is_repeat = false; + tevent.flag = 0; wm_event_add(win, &tevent); win->addmousemove = 0; } @@ -4487,19 +4510,18 @@ static void wm_eventemulation(wmEvent *event, bool test_only) if (U.flag & USER_TWOBUTTONMOUSE) { if (event->type == LEFTMOUSE) { - short *mod = ( + const uint8_t mod_test = ( #if !defined(WIN32) - (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? &event->oskey : - &event->alt + (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? KM_OSKEY : KM_ALT #else /* Disable for WIN32 for now because it accesses the start menu. */ - &event->alt + KM_ALT #endif ); if (event->val == KM_PRESS) { - if (*mod) { - *mod = 0; + if (event->modifier & mod_test) { + event->modifier &= ~mod_test; event->type = MIDDLEMOUSE; if (!test_only) { @@ -4511,7 +4533,7 @@ static void wm_eventemulation(wmEvent *event, bool test_only) /* Only send middle-mouse release if emulated. */ if (emulating_event == MIDDLEMOUSE) { event->type = MIDDLEMOUSE; - *mod = 0; + event->modifier &= ~mod_test; } if (!test_only) { @@ -4649,8 +4671,8 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi } } - wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval); - if (win_other) { + wmWindow *win_other = WM_window_find_under_cursor(win, mval, mval); + if (win_other && win_other != win) { copy_v2_v2_int(event->xy, mval); return win_other; } @@ -4687,6 +4709,9 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer(); + event->prev_click_type = event_state->prev_click_type = event_state->type; + event->prev_click_modifier = event_state->prev_click_modifier = event_state->modifier; + event->prev_click_keymodifier = event_state->prev_click_keymodifier = event_state->keymodifier; event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } @@ -4700,7 +4725,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) * them for better performance. */ if (event_last && event_last->type == MOUSEMOVE) { event_last->type = INBETWEEN_MOUSEMOVE; - event_last->is_repeat = false; + event_last->flag = 0; } wmEvent *event_new = wm_event_add(win, event); @@ -4752,7 +4777,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Initialize and copy state (only mouse x y and modifiers). */ event = *event_state; - event.is_repeat = false; + event.flag = 0; /** * Always support accessing the last key press/release. This is set from `win->eventstate`, @@ -4850,7 +4875,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.val = KM_NOTHING; /* The direction is inverted from the device due to system preferences. */ - event.is_direction_inverted = pd->isDirectionInverted; + if (pd->isDirectionInverted) { + event.flag |= WM_EVENT_SCROLL_INVERT; + } wm_event_add_trackpad(win, &event, pd->deltaX, -pd->deltaY); break; @@ -4931,12 +4958,16 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventKeyDown: case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = customdata; - short keymodifier = KM_NOTHING; + /* Only copy these flags into the `event_state`. */ + const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT; + bool keymodifier = 0; event.type = convert_key(kd->key); event.ascii = kd->ascii; /* Might be not NULL terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); - event.is_repeat = kd->is_repeat; + if (kd->is_repeat) { + event.flag |= WM_EVENT_IS_REPEAT; + } event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE; wm_eventemulation(&event, false); @@ -4945,7 +4976,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Copy to event state. */ event_state->val = event.val; event_state->type = event.type; - event_state->is_repeat = event.is_repeat; + event_state->flag = (event.flag & event_state_flag_mask); /* Exclude arrow keys, esc, etc from text input. */ if (type == GHOST_kEventKeyUp) { @@ -4981,29 +5012,57 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case EVT_LEFTSHIFTKEY: case EVT_RIGHTSHIFTKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_SHIFT; + event_state->modifier |= KM_SHIFT; + } + else { + event.modifier &= ~KM_SHIFT; + event_state->modifier &= ~KM_SHIFT; } - event.shift = event_state->shift = keymodifier; break; case EVT_LEFTCTRLKEY: case EVT_RIGHTCTRLKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_CTRL; + event_state->modifier |= KM_CTRL; + } + else { + event.modifier &= ~KM_CTRL; + event_state->modifier &= ~KM_CTRL; } - event.ctrl = event_state->ctrl = keymodifier; break; case EVT_LEFTALTKEY: case EVT_RIGHTALTKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_ALT; + event_state->modifier |= KM_ALT; + } + else { + event.modifier &= ~KM_ALT; + event_state->modifier &= ~KM_ALT; } - event.alt = event_state->alt = keymodifier; break; case EVT_OSKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_OSKEY; + event_state->modifier |= KM_OSKEY; + } + else { + event.modifier &= ~KM_OSKEY; + event_state->modifier &= ~KM_OSKEY; } - event.oskey = event_state->oskey = keymodifier; break; default: if (event.val == KM_PRESS && event.keymodifier == 0) { @@ -5041,14 +5100,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void * XXX Keep global for now? */ if ((event.type == EVT_ESCKEY && event.val == KM_PRESS) && /* Check other modifiers because ms-windows uses these to bring up the task manager. */ - (event.shift == 0 && event.ctrl == 0 && event.alt == 0)) { + ((event.modifier & (KM_SHIFT | KM_CTRL | KM_ALT)) == 0)) { G.is_break = true; } /* Double click test - only for press. */ if (event.val == KM_PRESS) { /* Don't reset timer & location when holding the key generates repeat events. */ - if (event.is_repeat == false) { + if ((event.flag & WM_EVENT_IS_REPEAT) == 0) { wm_event_prev_click_set(&event, event_state); } } @@ -5169,7 +5228,7 @@ void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) wmEvent event = { .type = EVT_XR_ACTION, .val = val, - .is_repeat = false, + .flag = 0, .custom = EVT_DATA_XR, .customdata = actiondata, .customdata_free = true, @@ -5301,9 +5360,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers( /** State storage to detect changes between calls to refresh the information. */ struct CursorKeymapInfo_State { - struct { - short shift, ctrl, alt, oskey; - } modifiers; + uint8_t modifier; short space_type; short region_type; /* Never use, just compare memory for changes. */ @@ -5326,10 +5383,7 @@ static void wm_event_cursor_store(struct CursorKeymapInfo_State *state, short region_type, const bToolRef *tref) { - state->modifiers.shift = event->shift; - state->modifiers.ctrl = event->ctrl; - state->modifiers.alt = event->alt; - state->modifiers.oskey = event->oskey; + state->modifier = event->modifier; state->space_type = space_type; state->region_type = region_type; state->tref = tref ? *tref : (bToolRef){0}; @@ -5460,15 +5514,15 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) } event_data[] = { {0, 0, LEFTMOUSE, KM_PRESS}, {0, 0, LEFTMOUSE, KM_CLICK}, - {0, 1, EVT_TWEAK_L, KM_ANY}, + {0, 0, LEFTMOUSE, KM_CLICK_DRAG}, {1, 0, MIDDLEMOUSE, KM_PRESS}, {1, 0, MIDDLEMOUSE, KM_CLICK}, - {1, 1, EVT_TWEAK_M, KM_ANY}, + {1, 0, MIDDLEMOUSE, KM_CLICK_DRAG}, {2, 0, RIGHTMOUSE, KM_PRESS}, {2, 0, RIGHTMOUSE, KM_CLICK}, - {2, 1, EVT_TWEAK_R, KM_ANY}, + {2, 0, RIGHTMOUSE, KM_CLICK_DRAG}, }; for (int button_index = 0; button_index < 3; button_index++) { diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 581c5f8a198..a6fbad8b171 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -42,6 +42,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->type = type; gesture->event_type = event->type; + gesture->event_modifier = event->modifier; + gesture->event_keymodifier = event->keymodifier; gesture->winrct = region->winrct; gesture->user_data.use_free = true; /* Free if userdata is set. */ gesture->modal_state = GESTURE_MODAL_NOP; @@ -50,7 +52,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, - WM_GESTURE_TWEAK, WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE)) { rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); @@ -81,9 +82,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent void WM_gesture_end(wmWindow *win, wmGesture *gesture) { - if (win->tweak == gesture) { - win->tweak = NULL; - } BLI_remlink(&win->gesture, gesture); MEM_freeN(gesture->customdata); WM_generic_user_data_free(&gesture->user_data); @@ -112,74 +110,6 @@ bool WM_gesture_is_modal_first(const wmGesture *gesture) return (gesture->is_active_prev == false); } -int wm_gesture_evaluate(wmGesture *gesture, const wmEvent *event) -{ - if (gesture->type == WM_GESTURE_TWEAK) { - rcti *rect = gesture->customdata; - const int delta[2] = { - BLI_rcti_size_x(rect), - BLI_rcti_size_y(rect), - }; - - if (WM_event_drag_test_with_delta(event, delta)) { - int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); - int val = EVT_GESTURE_W; - - if (theta == 0) { - val = EVT_GESTURE_E; - } - else if (theta == 1) { - val = EVT_GESTURE_NE; - } - else if (theta == 2) { - val = EVT_GESTURE_N; - } - else if (theta == 3) { - val = EVT_GESTURE_NW; - } - else if (theta == -1) { - val = EVT_GESTURE_SE; - } - else if (theta == -2) { - val = EVT_GESTURE_S; - } - else if (theta == -3) { - val = EVT_GESTURE_SW; - } - -#if 0 - /* debug */ - if (val == 1) { - printf("tweak north\n"); - } - if (val == 2) { - printf("tweak north-east\n"); - } - if (val == 3) { - printf("tweak east\n"); - } - if (val == 4) { - printf("tweak south-east\n"); - } - if (val == 5) { - printf("tweak south\n"); - } - if (val == 6) { - printf("tweak south-west\n"); - } - if (val == 7) { - printf("tweak west\n"); - } - if (val == 8) { - printf("tweak north-west\n"); - } -#endif - return val; - } - } - return 0; -} - /* ******************* gesture draw ******************* */ static void wm_gesture_draw_line_active_side(rcti *rect, const bool flip) @@ -509,11 +439,6 @@ void wm_gesture_draw(wmWindow *win) if (gt->type == WM_GESTURE_RECT) { wm_gesture_draw_rect(gt); } -#if 0 - else if (gt->type == WM_GESTURE_TWEAK) { - wm_gesture_draw_line(gt); - } -#endif else if (gt->type == WM_GESTURE_CIRCLE) { wm_gesture_draw_circle(gt); } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 2a27a8df411..1fdc8bbe2c8 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -471,118 +471,6 @@ void WM_OT_circle_gesture(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Tweak Gesture - * \{ */ - -static void gesture_tweak_modal(bContext *C, const wmEvent *event) -{ - wmWindow *window = CTX_wm_window(C); - wmGesture *gesture = window->tweak; - rcti *rect = gesture->customdata; - bool gesture_end = false; - - switch (event->type) { - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: { - - rect->xmax = event->xy[0] - gesture->winrct.xmin; - rect->ymax = event->xy[1] - gesture->winrct.ymin; - - const int val = wm_gesture_evaluate(gesture, event); - if (val != 0) { - wmEvent tevent; - - wm_event_init_from_window(window, &tevent); - /* We want to get coord from start of drag, - * not from point where it becomes a tweak event, see T40549. */ - tevent.xy[0] = rect->xmin + gesture->winrct.xmin; - tevent.xy[1] = rect->ymin + gesture->winrct.ymin; - if (gesture->event_type == LEFTMOUSE) { - tevent.type = EVT_TWEAK_L; - } - else if (gesture->event_type == RIGHTMOUSE) { - tevent.type = EVT_TWEAK_R; - } - else { - tevent.type = EVT_TWEAK_M; - } - tevent.val = val; - tevent.is_repeat = false; - /* mouse coords! */ - - /* important we add immediately after this event, so future mouse releases - * (which may be in the queue already), are handled in order, see T44740 */ - wm_event_add_ex(window, &tevent, event); - - gesture_end = true; - } - - break; - } - - case LEFTMOUSE: - case RIGHTMOUSE: - case MIDDLEMOUSE: - if (gesture->event_type == event->type) { - gesture_end = true; - - /* when tweak fails we should give the other keymap entries a chance */ - - /* XXX, assigning to readonly, BAD JUJU! */ - ((wmEvent *)event)->val = KM_RELEASE; - } - break; - default: - if (!ISTIMER(event->type) && event->type != EVENT_NONE) { - gesture_end = true; - } - break; - } - - if (gesture_end) { - /* Frees gesture itself, and unregisters from window. */ - WM_gesture_end(window, gesture); - - /* This isn't very nice but needed to redraw gizmos which are hidden while tweaking, - * See #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */ - ARegion *region = CTX_wm_region(C); - if ((region != NULL) && (region->gizmo_map != NULL)) { - if (WM_gizmomap_tag_delay_refresh_for_tweak_check(region->gizmo_map)) { - ED_region_tag_redraw(region); - } - } - } -} - -void wm_tweakevent_test(bContext *C, const wmEvent *event, int action) -{ - wmWindow *win = CTX_wm_window(C); - - if (win->tweak == NULL) { - const ARegion *region = CTX_wm_region(C); - - if (region) { - if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { - win->tweak = WM_gesture_new(win, region, event, WM_GESTURE_TWEAK); - } - } - } - } - else { - /* no tweaks if event was handled */ - if (action & WM_HANDLER_BREAK) { - WM_gesture_end(win, win->tweak); - } - else { - gesture_tweak_modal(C, event); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Lasso Gesture * \{ */ diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index f7bc138f163..ffac585cde7 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -169,6 +169,7 @@ static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b) return (wm_keymap_item_equals_result(a, b) && a->type == b->type && a->val == b->val && a->shift == b->shift && a->ctrl == b->ctrl && a->alt == b->alt && a->oskey == b->oskey && a->keymodifier == b->keymodifier && a->maptype == b->maptype && + ((a->val != KM_CLICK_DRAG) || (a->direction == b->direction)) && ((ISKEYBOARD(a->type) == 0) || (a->flag & KMI_REPEAT_IGNORE) == (b->flag & KMI_REPEAT_IGNORE))); } @@ -195,9 +196,6 @@ int WM_keymap_item_map_type_get(const wmKeyMapItem *kmi) if (ISKEYBOARD(kmi->type)) { return KMI_TYPE_KEYBOARD; } - if (ISTWEAK(kmi->type)) { - return KMI_TYPE_TWEAK; - } if (ISMOUSE(kmi->type)) { return KMI_TYPE_MOUSE; } @@ -459,11 +457,12 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) } static void keymap_event_set( - wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier) + wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier, int direction) { kmi->type = type; kmi->val = val; kmi->keymodifier = keymodifier; + kmi->direction = direction; if (modifier == KM_ANY) { kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY; @@ -497,15 +496,20 @@ static void keymap_item_set_id(wmKeyMap *keymap, wmKeyMapItem *kmi) } } -wmKeyMapItem *WM_keymap_add_item( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&keymap->items, kmi); BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME); - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); wm_keymap_item_properties_set(kmi); keymap_item_set_id(keymap, kmi); @@ -919,14 +923,14 @@ wmKeyMap *WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname) } wmKeyMapItem *WM_modalkeymap_add_item( - wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value) + wmKeyMap *km, int type, int val, int modifier, int keymodifier, int direction, int value) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&km->items, kmi); kmi->propvalue = value; - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); keymap_item_set_id(km, kmi); @@ -935,15 +939,20 @@ wmKeyMapItem *WM_modalkeymap_add_item( return kmi; } -wmKeyMapItem *WM_modalkeymap_add_item_str( - wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value) +wmKeyMapItem *WM_modalkeymap_add_item_str(wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + const char *value) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&km->items, kmi); BLI_strncpy(kmi->propvalue_str, value, sizeof(kmi->propvalue_str)); - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); keymap_item_set_id(km, kmi); @@ -1730,6 +1739,9 @@ bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2) if (k1->val != k2->val) { return 0; } + if (k1->val == KM_CLICK_DRAG && (k1->direction != k2->direction)) { + return 0; + } } if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift) { diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index 162246798de..24c221221d1 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -29,64 +29,64 @@ /** \name Wrappers for #WM_keymap_add_item * \{ */ -wmKeyMapItem *WM_keymap_add_menu( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_menu", type, val, modifier, keymodifier); + keymap, "WM_OT_call_menu", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -wmKeyMapItem *WM_keymap_add_menu_pie( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier); + keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -wmKeyMapItem *WM_keymap_add_panel( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_panel", type, val, modifier, keymodifier); + keymap, "WM_OT_call_panel", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); /* TODO: we might want to disable this. */ RNA_boolean_set(kmi->ptr, "keep_open", false); return kmi; } -wmKeyMapItem *WM_keymap_add_tool( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier); + keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap, - const EnumPropertyItem *items, - const char *data_path, - int type_start, - int val, - int modifier, - int keymodifier) -{ - for (int i = 0, type_offset = 0; items[i].identifier; i++) { - if (items[i].identifier[0] == '\0') { - continue; - } - wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_context_set_enum", type_start + type_offset, val, modifier, keymodifier); - RNA_string_set(kmi->ptr, "data_path", data_path); - RNA_string_set(kmi->ptr, "value", items[i].identifier); - type_offset += 1; - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -106,6 +106,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) case CTX_MODE_EDIT_CURVE: km_id = "Curve"; break; + case CTX_MODE_EDIT_CURVES: + km_id = "Curves"; + break; case CTX_MODE_EDIT_SURFACE: km_id = "Curve"; break; @@ -158,7 +161,7 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) km_id = "Grease Pencil Stroke Vertex Mode"; break; case CTX_MODE_SCULPT_CURVES: - km_id = "Curves Sculpt Mode"; + km_id = "Curves Sculpt"; break; } } diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index 6f3f42bee53..5a817075cd5 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -115,11 +115,11 @@ static bool interactive_value_update(ValueInteraction *inter, (((float)(mval_curr - mval_init) / inter->context_vars.region->winx) * value_range)) * value_scale; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { const double snap = 0.1; value_delta = (float)roundf((double)value_delta / snap) * snap; } - if (event->shift) { + if (event->modifier & KM_SHIFT) { value_delta *= 0.1f; } const float value_final = inter->init.prop_value + value_delta; @@ -133,8 +133,8 @@ static bool interactive_value_update(ValueInteraction *inter, } inter->prev.prop_value = value_final; - inter->prev.is_snap = event->ctrl; - inter->prev.is_precise = event->shift; + inter->prev.is_snap = (event->modifier & KM_CTRL) != 0; + inter->prev.is_precise = (event->modifier & KM_SHIFT) != 0; *r_value_final = value_final; return changed; diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a476fb4fa13..7e680af4537 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -911,7 +911,6 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event) ret_value = op->type->exec(C, op); OPERATOR_RETVAL_CHECK(ret_value); - op->customdata = POINTER_FROM_INT((int)event->type); if (ret_value & OPERATOR_RUNNING_MODAL) { WM_event_add_modal_handler(C, op); @@ -2822,7 +2821,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even float numValue; /* TODO: fix hardcoded events */ - bool snap = event->ctrl != 0; + bool snap = (event->modifier & KM_CTRL) != 0; /* Modal numinput active, try to handle numeric inputs first... */ if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &rc->num_input, event)) { diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 2ee608c0755..95879829d42 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -483,7 +483,6 @@ static void draw_display_buffer(PlayState *ps, ImBuf *ibuf) if (!glsl_used) { immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); immUniformColor3f(1.0f, 1.0f, 1.0f); - immUniform1i("image", 0); } immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 6bdd11df776..182308cbe5e 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -67,7 +67,7 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view) const float halfx = GLA_PIXEL_OFS / sizex; const float halfy = GLA_PIXEL_OFS / sizex; - immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ + /* Texture is already bound to GL_TEXTURE0 unit. */ immBegin(GPU_PRIM_TRI_FAN, 4); @@ -111,7 +111,7 @@ void wm_stereo3d_draw_topbottom(wmWindow *win, int view) const float halfx = GLA_PIXEL_OFS / sizex; const float halfy = GLA_PIXEL_OFS / sizex; - immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ + /* Texture is already bound to GL_TEXTURE0 unit. */ immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 4ae935b14f2..51e4bc9faa8 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -663,6 +663,8 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) return "builtin_brush.Weight"; case CTX_MODE_VERTEX_GPENCIL: return "builtin_brush.Draw"; + case CTX_MODE_SCULPT_CURVES: + return "builtin_brush.Test 1"; /* end temporary hack. */ case CTX_MODE_PARTICLE: @@ -804,13 +806,34 @@ void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C, WM_toolsystem_refresh_screen_area(workspace, view_layer, area); } +static IDProperty *idprops_ensure_named_group(IDProperty *group, const char *idname) +{ + IDProperty *prop = IDP_GetPropertyFromGroup(group, idname); + if ((prop == NULL) || (prop->type != IDP_GROUP)) { + IDPropertyTemplate val = {0}; + prop = IDP_New(IDP_GROUP, &val, __func__); + STRNCPY(prop->name, idname); + IDP_ReplaceInGroup_ex(group, prop, NULL); + } + return prop; +} + +IDProperty *WM_toolsystem_ref_properties_get_idprops(bToolRef *tref) +{ + IDProperty *group = tref->properties; + if (group == NULL) { + return NULL; + } + return IDP_GetPropertyFromGroup(group, tref->idname); +} + IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref) { if (tref->properties == NULL) { IDPropertyTemplate val = {0}; - tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + tref->properties = IDP_New(IDP_GROUP, &val, __func__); } - return tref->properties; + return idprops_ensure_named_group(tref->properties, tref->idname); } bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref, @@ -818,7 +841,7 @@ bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref, StructRNA *type, PointerRNA *r_ptr) { - IDProperty *group = tref->properties; + IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref); IDProperty *prop = group ? IDP_GetPropertyFromGroup(group, idname) : NULL; RNA_pointer_create(NULL, type, prop, r_ptr); return (prop != NULL); @@ -830,17 +853,7 @@ void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref, PointerRNA *r_ptr) { IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref); - IDProperty *prop = IDP_GetPropertyFromGroup(group, idname); - if (prop == NULL) { - IDPropertyTemplate val = {0}; - prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties"); - STRNCPY(prop->name, idname); - IDP_ReplaceInGroup_ex(group, prop, NULL); - } - else { - BLI_assert(prop->type == IDP_GROUP); - } - + IDProperty *prop = idprops_ensure_named_group(group, idname); RNA_pointer_create(NULL, type, prop, r_ptr); } @@ -857,8 +870,9 @@ void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref, IDPropertyTemplate val = {0}; dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); } - if (tref->properties != NULL) { - IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname); + IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref); + if (group != NULL) { + IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname); if (prop) { /* Important key-map items properties don't get overwritten by the tools. * - When a key-map item doesn't set a property, the tool-systems is used. diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a983150b504..e93ffe48aba 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1104,10 +1104,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->active = 0; /* XXX */ /* clear modifiers for inactive windows */ - win->eventstate->alt = 0; - win->eventstate->ctrl = 0; - win->eventstate->shift = 0; - win->eventstate->oskey = 0; + win->eventstate->modifier = 0; win->eventstate->keymodifier = 0; break; @@ -1138,7 +1135,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr kdata.ascii = '\0'; kdata.utf8_buf[0] = '\0'; - if (win->eventstate->shift) { + if (win->eventstate->modifier & KM_SHIFT) { if ((keymodifier & KM_SHIFT) == 0) { kdata.key = GHOST_kKeyLeftShift; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1147,11 +1144,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_SHIFT) { - win->eventstate->shift = KM_MOD_HELD; + win->eventstate->modifier |= KM_SHIFT; } } #endif - if (win->eventstate->ctrl) { + if (win->eventstate->modifier & KM_CTRL) { if ((keymodifier & KM_CTRL) == 0) { kdata.key = GHOST_kKeyLeftControl; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1160,11 +1157,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_CTRL) { - win->eventstate->ctrl = KM_MOD_HELD; + win->eventstate->modifier |= KM_CTRL; } } #endif - if (win->eventstate->alt) { + if (win->eventstate->modifier & KM_ALT) { if ((keymodifier & KM_ALT) == 0) { kdata.key = GHOST_kKeyLeftAlt; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1173,11 +1170,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_ALT) { - win->eventstate->alt = KM_MOD_HELD; + win->eventstate->modifier |= KM_ALT; } } #endif - if (win->eventstate->oskey) { + if (win->eventstate->modifier & KM_OSKEY) { if ((keymodifier & KM_OSKEY) == 0) { kdata.key = GHOST_kKeyOS; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1186,7 +1183,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_OSKEY) { - win->eventstate->oskey = KM_MOD_HELD; + win->eventstate->modifier |= KM_OSKEY; } } #endif @@ -1216,7 +1213,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_init_from_window(win, &event); event.type = MOUSEMOVE; copy_v2_v2_int(event.prev_xy, event.xy); - event.is_repeat = false; + event.flag = 0; wm_event_add(win, &event); @@ -1347,7 +1344,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* activate region */ event.type = MOUSEMOVE; copy_v2_v2_int(event.prev_xy, event.xy); - event.is_repeat = false; + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ wm->winactive = win; @@ -1488,7 +1485,7 @@ static bool wm_window_timer(const bContext *C) event.type = wt->event_type; event.val = KM_NOTHING; event.keymodifier = 0; - event.is_repeat = false; + event.flag = 0; event.custom = EVT_DATA_TIMER; event.customdata = wt; wm_event_add(win, &event); @@ -1844,56 +1841,23 @@ bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut) /** \name Find Window Utility * \{ */ -static void wm_window_desktop_pos_get(const wmWindow *win, - const int screen_pos[2], - int r_desk_pos[2]) +wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2]) { - /* To desktop space. */ - r_desk_pos[0] = screen_pos[0] + (int)(U.pixelsize * win->posx); - r_desk_pos[1] = screen_pos[1] + (int)(U.pixelsize * win->posy); -} - -static void wm_window_screen_pos_get(const wmWindow *win, - const int desktop_pos[2], - int r_scr_pos[2]) -{ - /* To window space. */ - r_scr_pos[0] = desktop_pos[0] - (int)(U.pixelsize * win->posx); - r_scr_pos[1] = desktop_pos[1] - (int)(U.pixelsize * win->posy); -} - -wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm, - const wmWindow *win_ignore, - const wmWindow *win, - const int mval[2], - int r_mval[2]) -{ - int desk_pos[2]; - wm_window_desktop_pos_get(win, mval, desk_pos); - - /* TODO: This should follow the order of the activated windows. - * The current solution is imperfect but usable in most cases. */ - LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) { - if (win_iter == win_ignore) { - continue; - } - - if (win_iter->windowstate == GHOST_kWindowStateMinimized) { - continue; - } - - int scr_pos[2]; - wm_window_screen_pos_get(win_iter, desk_pos, scr_pos); + int tmp[2]; + copy_v2_v2_int(tmp, mval); + wm_cursor_position_to_ghost(win, &tmp[0], &tmp[1]); - if (scr_pos[0] >= 0 && scr_pos[1] >= 0 && scr_pos[0] <= WM_window_pixels_x(win_iter) && - scr_pos[1] <= WM_window_pixels_y(win_iter)) { + GHOST_WindowHandle ghostwin = GHOST_GetWindowUnderCursor(g_system, tmp[0], tmp[1]); - copy_v2_v2_int(r_mval, scr_pos); - return win_iter; - } + if (!ghostwin) { + return NULL; } - return NULL; + wmWindow *r_win = GHOST_GetWindowUserData(ghostwin); + wm_cursor_position_from_ghost(r_win, &tmp[0], &tmp[1]); + copy_v2_v2_int(r_mval, tmp); + + return r_win; } void WM_window_pixel_sample_read(const wmWindowManager *wm, diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 68b16d46746..172a879e118 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -74,16 +74,8 @@ void wm_gesture_draw(struct wmWindow *win); /** * Tweak and line gestures. */ -int wm_gesture_evaluate(wmGesture *gesture, const struct wmEvent *event); void wm_gesture_tag_redraw(struct wmWindow *win); -/* wm_gesture_ops.c */ - -/** - * Standard tweak, called after window handlers passed on event. - */ -void wm_tweakevent_test(bContext *C, const wmEvent *event, int action); - /* wm_jobs.c */ /** diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 0ff181db9b1..d5c8c5022cc 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -325,16 +325,6 @@ enum { /* NOTE: these values are saved in key-map files, do not change them but just add new ones. */ - /* Tweak events: - * Sent as additional event with the mouse coordinates - * from where the initial click was placed. */ - - /* Tweak events for L M R mouse-buttons. */ - EVT_TWEAK_L = 0x5002, /* 20482 */ - EVT_TWEAK_M = 0x5003, /* 20483 */ - EVT_TWEAK_R = 0x5004, /* 20484 */ - /* 0x5010 (and lower) should be left to add other tweak types in the future. */ - /* 0x5011 is taken, see EVT_ACTIONZONE_FULLSCREEN */ /* Misc Blender internals: 0x502x */ @@ -394,9 +384,6 @@ enum { BUTTON6MOUSE, \ BUTTON7MOUSE)) -/** Test whether the event is tweak event. */ -#define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_TWEAK_R) - /** Test whether the event is a NDOF event. */ #define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX) @@ -408,15 +395,6 @@ enum { ((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \ (ISKEYMODIFIER(event_type) == false)) -/* Internal helpers. */ -#define _VA_IS_EVENT_MOD2(v, a) (CHECK_TYPE_INLINE(v, wmEvent *), ((v)->a)) -#define _VA_IS_EVENT_MOD3(v, a, b) (_VA_IS_EVENT_MOD2(v, a) || ((v)->b)) -#define _VA_IS_EVENT_MOD4(v, a, b, c) (_VA_IS_EVENT_MOD3(v, a, b) || ((v)->c)) -#define _VA_IS_EVENT_MOD5(v, a, b, c, d) (_VA_IS_EVENT_MOD4(v, a, b, c) || ((v)->d)) - -/** Reusable `IS_EVENT_MOD(event, shift, ctrl, alt, oskey)` macro. */ -#define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__) - enum eEventType_Mask { /** #ISKEYMODIFIER */ EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0), @@ -432,14 +410,11 @@ enum eEventType_Mask { EVT_TYPE_MASK_MOUSE = (1 << 5), /** #ISNDOF */ EVT_TYPE_MASK_NDOF = (1 << 6), - /** #ISTWEAK */ - EVT_TYPE_MASK_TWEAK = (1 << 7), /** #IS_EVENT_ACTIONZONE */ - EVT_TYPE_MASK_ACTIONZONE = (1 << 8), + EVT_TYPE_MASK_ACTIONZONE = (1 << 7), }; #define EVT_TYPE_MASK_ALL \ - (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_TWEAK | \ - EVT_TYPE_MASK_ACTIONZONE) + (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_ACTIONZONE) #define EVT_TYPE_MASK_HOTKEY_INCLUDE \ (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF) @@ -454,18 +429,6 @@ bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask); * \{ */ /* Gestures */ -/* NOTE: these values are saved in keymap files, do not change them but just add new ones */ -enum { - /* Value of tweaks and line gestures. #KM_ANY (-1) works for this case too. */ - EVT_GESTURE_N = 1, - EVT_GESTURE_NE = 2, - EVT_GESTURE_E = 3, - EVT_GESTURE_SE = 4, - EVT_GESTURE_S = 5, - EVT_GESTURE_SW = 6, - EVT_GESTURE_W = 7, - EVT_GESTURE_NW = 8, -}; /* File select */ enum { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index f6003428700..6750e7a7d77 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -56,8 +56,7 @@ static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) static wmXrAction *action_create(const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, @@ -73,15 +72,16 @@ static wmXrAction *action_create(const char *action_name, strcpy(action->name, action_name); action->type = type; - const unsigned int count = count_subaction_paths; + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + unsigned int subaction_idx = 0; action->count_subaction_paths = count; action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, "XrAction_SubactionPaths"); - for (unsigned int i = 0; i < count; ++i) { - action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, - "XrAction_SubactionPath"); - strcpy(action->subaction_paths[i], subaction_paths[i]); + LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) { + action->subaction_paths[subaction_idx] = MEM_mallocN(strlen(user_path->path) + 1, + "XrAction_SubactionPath"); + strcpy(action->subaction_paths[subaction_idx], user_path->path); } size_t size; @@ -140,10 +140,9 @@ static void action_destroy(void *val) MEM_SAFE_FREE(action->name); - const unsigned int count = action->count_subaction_paths; char **subaction_paths = action->subaction_paths; if (subaction_paths) { - for (unsigned int i = 0; i < count; ++i) { + for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { MEM_SAFE_FREE(subaction_paths[i]); } MEM_freeN(subaction_paths); @@ -214,8 +213,7 @@ bool WM_xr_action_create(wmXrData *xr, const char *action_set_name, const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, @@ -232,8 +230,7 @@ bool WM_xr_action_create(wmXrData *xr, wmXrAction *action = action_create(action_name, type, - count_subaction_paths, - subaction_paths, + user_paths, ot, op_properties, haptic_name, @@ -244,10 +241,20 @@ bool WM_xr_action_create(wmXrData *xr, action_flag, haptic_flag); + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + unsigned int subaction_idx = 0; + + char **subaction_paths = MEM_calloc_arrayN( + count, sizeof(*subaction_paths), "XrAction_SubactionPathPointers"); + + LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) { + subaction_paths[subaction_idx] = (char *)user_path->path; + } + GHOST_XrActionInfo info = { .name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, + .count_subaction_paths = count, + .subaction_paths = (const char **)subaction_paths, .states = action->states, .float_thresholds = action->float_thresholds, .axis_flags = (int16_t *)action->axis_flags, @@ -273,11 +280,11 @@ bool WM_xr_action_create(wmXrData *xr, break; } - if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { - return false; - } + const bool success = GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info); - return true; + MEM_freeN(subaction_paths); + + return success; } void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) @@ -323,19 +330,29 @@ bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path, - unsigned int count_subaction_paths, - const char **subaction_paths, - const char **component_paths, + const ListBase *user_paths, + const ListBase *component_paths, const float *float_thresholds, const eXrAxisFlag *axis_flags, const struct wmXrPose *poses) { + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + BLI_assert(count == (unsigned int)BLI_listbase_count(component_paths)); + GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( - count_subaction_paths, sizeof(*binding_infos), __func__); + count, sizeof(*binding_infos), "XrActionBinding_Infos"); - for (unsigned int i = 0; i < count_subaction_paths; ++i) { + char **subaction_paths = MEM_calloc_arrayN( + count, sizeof(*subaction_paths), "XrActionBinding_SubactionPathPointers"); + + for (unsigned int i = 0; i < count; ++i) { GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; - binding_info->component_path = component_paths[i]; + const XrUserPath *user_path = BLI_findlink(user_paths, i); + const XrComponentPath *component_path = BLI_findlink(component_paths, i); + + subaction_paths[i] = (char *)user_path->path; + + binding_info->component_path = component_path->path; if (float_thresholds) { binding_info->float_threshold = float_thresholds[i]; } @@ -351,15 +368,18 @@ bool WM_xr_action_binding_create(wmXrData *xr, GHOST_XrActionProfileInfo profile_info = { .action_name = action_name, .profile_path = profile_path, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, + .count_subaction_paths = count, + .subaction_paths = (const char **)subaction_paths, .bindings = binding_infos, }; - bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + const bool success = GHOST_XrCreateActionBindings( + xr->runtime->context, action_set_name, 1, &profile_info); + MEM_freeN(subaction_paths); MEM_freeN(binding_infos); - return ret; + + return success; } void WM_xr_action_binding_destroy(wmXrData *xr, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c index 0c356ab2b2e..8a1982fa8b5 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -103,6 +103,12 @@ static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_ XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src); amb_dst->prev = amb_dst->next = NULL; + BLI_listbase_clear(&amb_dst->component_paths); + LISTBASE_FOREACH (XrComponentPath *, path, &amb_src->component_paths) { + XrComponentPath *path_new = MEM_dupallocN(path); + BLI_addtail(&amb_dst->component_paths, path_new); + } + return amb_dst; } @@ -118,11 +124,17 @@ XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, return amb_dst; } +static void wm_xr_actionmap_binding_clear(XrActionMapBinding *amb) +{ + BLI_freelistN(&amb->component_paths); +} + bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) { int idx = BLI_findindex(&ami->bindings, amb); if (idx != -1) { + wm_xr_actionmap_binding_clear(amb); BLI_freelinkN(&ami->bindings, amb); if (idx <= ami->selbinding) { @@ -155,12 +167,6 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output. * \{ */ -static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami) -{ - BLI_freelistN(&ami->bindings); - ami->selbinding = 0; -} - static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) { WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op); @@ -180,6 +186,19 @@ static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami) } } +static void wm_xr_actionmap_item_clear(XrActionMapItem *ami) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + wm_xr_actionmap_binding_clear(amb); + } + BLI_freelistN(&ami->bindings); + ami->selbinding = 0; + + wm_xr_actionmap_item_properties_free(ami); + + BLI_freelistN(&ami->user_paths); +} + void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) { switch (ami->type) { @@ -305,6 +324,12 @@ static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src) ami_dst->op_properties_ptr = NULL; } + BLI_listbase_clear(&ami_dst->user_paths); + LISTBASE_FOREACH (XrUserPath *, path, &ami_src->user_paths) { + XrUserPath *path_new = MEM_dupallocN(path); + BLI_addtail(&ami_dst->user_paths, path_new); + } + return ami_dst; } @@ -324,8 +349,7 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) int idx = BLI_findindex(&actionmap->items, ami); if (idx != -1) { - wm_xr_actionmap_item_bindings_clear(ami); - wm_xr_actionmap_item_properties_free(ami); + wm_xr_actionmap_item_clear(ami); BLI_freelinkN(&actionmap->items, ami); if (idx <= actionmap->selitem) { @@ -480,12 +504,9 @@ XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) void WM_xr_actionmap_clear(XrActionMap *actionmap) { LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { - wm_xr_actionmap_item_bindings_clear(ami); - wm_xr_actionmap_item_properties_free(ami); + wm_xr_actionmap_item_clear(ami); } - BLI_freelistN(&actionmap->items); - actionmap->selitem = 0; } @@ -494,9 +515,7 @@ void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) { WM_xr_actionmap_clear(am); } - BLI_freelistN(&runtime->actionmaps); - runtime->actactionmap = runtime->selactionmap = 0; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 0e7c4d18753..9480104150a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -114,12 +114,8 @@ typedef struct wmXrDrawData { typedef struct wmXrController { struct wmXrController *next, *prev; - /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256). - This subaction path will later be combined with a component path, and that combined path should - also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path = - /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). - */ - char subaction_path[64]; + /** OpenXR user path identifier. */ + char subaction_path[64]; /* XR_MAX_USER_PATH_LENGTH */ /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 59b4eb00363..dfeaeae196c 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1188,8 +1188,9 @@ void wm_xr_session_actions_update(wmWindowManager *wm) &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); } - int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); - if (!ret) { + const bool synced = GHOST_XrSyncActions(xr_context, + active_action_set ? active_action_set->name : NULL); + if (!synced) { return; } diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index e6e122508a9..d17afad0918 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -319,16 +319,23 @@ elseif(WIN32) elseif(APPLE) if(WITH_PYTHON_MODULE) if(WITH_INSTALL_PORTABLE) + set(BPY_INSTALL_DIR) set(TARGETDIR_VER $<TARGET_FILE_DIR:blender>/../Resources/${BLENDER_VERSION}) # Keep the `BLENDER_VERSION` folder and bpy.so in the build folder. set(INSTALL_BPY_TO_SITE_PACKAGES OFF) else() - set(TARGETDIR_VER "${PYTHON_LIBPATH}/Resources/${BLENDER_VERSION}") + # Parent directory of bpy.so for installation. + set(BPY_INSTALL_DIR ${PYTHON_LIBPATH}/site-packages) + # Defined in terms of site-packages since the site-packages + # directory can be a symlink (brew for example). + set(TARGETDIR_VER "${BPY_INSTALL_DIR}/../Resources/${BLENDER_VERSION}") set(INSTALL_BPY_TO_SITE_PACKAGES ON) endif() else() set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION}) endif() + # License, copyright, readme files. + set(BLENDER_TEXT_FILES_DESTINATION "${TARGETDIR_VER}/../text") set(MAC_BLENDER_TARGET_DYLIBS_DIR "${TARGETDIR_VER}/lib") # Skip relinking on cpack / install set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true) @@ -1052,9 +1059,6 @@ elseif(APPLE) DESTINATION "." ) - # install release and app files - set(BLENDER_TEXT_FILES_DESTINATION Blender.app/Contents/Resources/text) - install( FILES ${OSX_APP_SOURCEDIR}/Contents/PkgInfo DESTINATION Blender.app/Contents @@ -1108,11 +1112,12 @@ elseif(APPLE) ) unset(_py_inc_suffix) endif() + if(WITH_PYTHON_MODULE) if(INSTALL_BPY_TO_SITE_PACKAGES) install( TARGETS blender - LIBRARY DESTINATION ${PYTHON_LIBPATH}/site-packages + LIBRARY DESTINATION ${BPY_INSTALL_DIR} ) endif() endif() diff --git a/source/tools b/source/tools -Subproject 3fc56d7bc28f83c864c37503700a8a182f77a59 +Subproject 5715a567950258c17089c9b9cb175a6ef8c602c diff --git a/tests/check_deprecated.py b/tests/check_deprecated.py deleted file mode 100644 index 089e0275229..00000000000 --- a/tests/check_deprecated.py +++ /dev/null @@ -1,131 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -# <pep8 compliant> - -import os -from os.path import splitext - -DEPRECATE_DAYS = 120 - -SKIP_DIRS = ("extern", - "tests", # not this dir - ) - - -def is_c_header(filename): - ext = splitext(filename)[1] - return (ext in {".h", ".hpp", ".hxx", ".hh"}) - - -def is_c(filename): - ext = splitext(filename)[1] - return (ext in {".c", ".cpp", ".cxx", ".m", ".mm", ".rc", ".cc", ".inl"}) - - -def is_c_any(filename): - return is_c(filename) or is_c_header(filename) - - -def is_py(filename): - ext = splitext(filename)[1] - return (ext == ".py") - - -def is_source_any(filename): - return is_c_any(filename) or is_py(filename) - - -def source_list(path, filename_check=None): - for dirpath, dirnames, filenames in os.walk(path): - # skip '.git' - dirnames[:] = [d for d in dirnames if not d.startswith(".")] - - for filename in filenames: - if filename_check is None or filename_check(filename): - yield os.path.join(dirpath, filename) - - -def deprecations(): - """ - Searches out source code for lines like - - /* *DEPRECATED* 2011/7/17 bgl.Buffer.list info text */ - - Or... - - # *DEPRECATED* 2010/12/22 some.py.func more info */ - - """ - import datetime - SOURCE_DIR = os.path.normpath(os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(__file__), "..")))) - - SKIP_DIRS_ABS = [os.path.join(SOURCE_DIR, p) for p in SKIP_DIRS] - - deprecations_ls = [] - - scan_tot = 0 - - print("scanning in %r for '*DEPRECATED* YYYY/MM/DD info'" % SOURCE_DIR) - - for fn in source_list(SOURCE_DIR, is_source_any): - # print(fn) - skip = False - for p in SKIP_DIRS_ABS: - if fn.startswith(p): - skip = True - break - if skip: - continue - - file = open(fn, 'r', encoding="utf8") - for i, l in enumerate(file): - # logic for deprecation warnings - if '*DEPRECATED*' in l: - try: - l = l.strip() - data = l.split('*DEPRECATED*', 1)[-1].strip().strip() - data = [w.strip() for w in data.split('/', 2)] - data[-1], info = data[-1].split(' ', 1) - info = info.split("*/", 1)[0] - if len(data) != 3: - print(" poorly formatting line:\n" - " %r:%d\n" - " %s" % - (fn, i + 1, l) - ) - else: - data = datetime.datetime(*tuple([int(w) for w in data])) - - deprecations_ls.append((data, (fn, i + 1), info)) - except: - print("Error file - %r:%d" % (fn, i + 1)) - import traceback - traceback.print_exc() - - scan_tot += 1 - - print(" scanned %d files" % scan_tot) - - return deprecations_ls - - -def main(): - import datetime - now = datetime.datetime.now() - - deps = deprecations() - - print("\nAll deprecations...") - for data, fileinfo, info in deps: - days_old = (now - data).days - if days_old > DEPRECATE_DAYS: - info = "*** REMOVE! *** " + info - print(" %r, days-old(%.2d), %s:%d - %s" % (data, days_old, fileinfo[0], fileinfo[1], info)) - if deps: - print("\ndone!") - else: - print("\nnone found!") - - -if __name__ == '__main__': - main() diff --git a/tests/python/bl_keymap_validate.py b/tests/python/bl_keymap_validate.py index ce2022f6ae7..1743893dc8a 100644 --- a/tests/python/bl_keymap_validate.py +++ b/tests/python/bl_keymap_validate.py @@ -16,6 +16,7 @@ This catches the following kinds of issues: - Unused keymaps (keymaps which are defined but not used anywhere). - Event values that don't make sense for the event type, e.g. An escape key could have the value "NORTH" instead of "PRESS". +- Identical key-map items. This works by taking the keymap data (before it's loaded into Blender), then comparing it with that same keymap after exporting and importing. @@ -27,16 +28,46 @@ NOTE: """ import types -import typing - +from typing import ( + Any, + Dict, + Generator, + List, + Optional, + Sequence, + Tuple, +) + +KeyConfigData = List[Tuple[str, Tuple[Any], Dict[str, Any]]] + +import os import contextlib -import bpy +import bpy # type: ignore # Useful for diffing the output to see what changed in context. # this writes keymaps into the current directory with `.orig.py` & `.rewrite.py` extensions. -WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory. +WRITE_OUTPUT_DIR = "" # "/tmp", defaults to the systems temp directory. +# For each preset, test all of these options. +# The key is the preset name, containing a sequence of (attribute, value) pairs to test. +# +# NOTE(@campbellbarton): only add these for preferences which impact multiple keys as exposing all preferences +# this way would create too many combinations making the tests take too long to complete. +PRESET_PREFS = { + "Blender": ( + (("select_mouse", 'LEFT'), ("use_alt_tool", False)), + (("select_mouse", 'LEFT'), ("use_alt_tool", True)), + (("select_mouse", 'RIGHT'), ("rmb_action", 'TWEAK')), + (("select_mouse", 'RIGHT'), ("rmb_action", 'FALLBACK_TOOL')), + ), +} + +# Don't report duplicates for these presets. +ALLOW_DUPLICATES = { + # This key-map manipulates the default key-map, making it difficult to avoid duplicates entirely. + "Industry_Compatible" +} # ----------------------------------------------------------------------------- # Generic Utilities @@ -45,7 +76,7 @@ WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory. def temp_fn_argument_extractor( mod: types.ModuleType, mod_attr: str, -) -> typing.Iterator[typing.List[typing.Tuple[list, dict]]]: +) -> Generator[List[Tuple[Tuple[Tuple[Any], ...], Dict[str, Dict[str, Any]]]], None, None]: """ Temporarily intercept a function, so it's arguments can be extracted. The context manager gives us a list where each item is a tuple of @@ -54,7 +85,7 @@ def temp_fn_argument_extractor( args_collected = [] real_fn = getattr(mod, mod_attr) - def wrap_fn(*args, **kw): + def wrap_fn(*args: Tuple[Any], **kw: Dict[str, Any]) -> Any: args_collected.append((args, kw)) return real_fn(*args, **kw) setattr(mod, mod_attr, wrap_fn) @@ -66,10 +97,10 @@ def temp_fn_argument_extractor( def round_float_32(f: float) -> float: from struct import pack, unpack - return unpack("f", pack("f", f))[0] + return unpack("f", pack("f", f))[0] # type: ignore -def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.Optional[str]: +def report_humanly_readable_difference(a: Any, b: Any) -> Optional[str]: """ Compare strings, return None whrn they match, otherwise a humanly readable difference message. @@ -86,7 +117,7 @@ def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.O # ----------------------------------------------------------------------------- # Keymap Utilities. -def keyconfig_preset_scan() -> typing.List[str]: +def keyconfig_preset_scan() -> List[str]: """ Return all bundled presets (keymaps), not user presets. """ @@ -104,7 +135,7 @@ def keyconfig_preset_scan() -> typing.List[str]: ] -def keymap_item_property_clean(value: typing.Any) -> typing.Any: +def keymap_item_property_clean(value: Any) -> Any: """ Recursive property sanitize. @@ -118,12 +149,13 @@ def keymap_item_property_clean(value: typing.Any) -> typing.Any: return sorted( # Convert to `dict` to de-duplicate. dict([(k, keymap_item_property_clean(v)) for k, v in value]).items(), - key=lambda item: item[0], + # Ignore type checking, these are strings which we know can be sorted. + key=lambda item: item[0], # type: ignore ) return value -def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None: +def keymap_data_clean(keyconfig_data: KeyConfigData, *, relaxed: bool) -> None: """ Order & sanitize keymap data so the result from the hand written Python script is comparable with data exported & imported. @@ -153,22 +185,82 @@ def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None: items[i] = item_op, item_event, None -def keyconfig_activate_and_extract_data(filepath: str, *, relaxed: bool) -> typing.List: +def keyconfig_config_as_filename_component(values: Sequence[Tuple[str, Any]]) -> str: + """ + Takes a configuration, eg: + + [("select_mouse", 'LEFT'), ("rmb_action", 'TWEAK')] + + And returns a filename compatible path: + """ + from urllib.parse import quote + if not values: + return "" + + return "(" + quote( + ".".join([ + "-".join((str(key), str(val))) + for key, val in values + ]), + # Needed so forward slashes aren't included in the resulting name. + safe="", + ) + ")" + + +def keyconfig_activate_and_extract_data( + filepath: str, + *, + relaxed: bool, + config: Sequence[Tuple[str, Any]], +) -> KeyConfigData: """ Activate the key-map by filepath, return the key-config data (cleaned for comparison). """ - import bl_keymap_utils.io + import bl_keymap_utils.io # type: ignore + + if config: + bpy.ops.preferences.keyconfig_activate(filepath=filepath) + km_prefs = bpy.context.window_manager.keyconfigs.active.preferences + for attr, value in config: + setattr(km_prefs, attr, value) + with temp_fn_argument_extractor(bl_keymap_utils.io, "keyconfig_init_from_data") as args_collected: bpy.ops.preferences.keyconfig_activate(filepath=filepath) + # If called multiple times, something strange is happening. assert(len(args_collected) == 1) args, _kw = args_collected[0] - keyconfig_data = args[1] + # Ignore the type check as `temp_fn_argument_extractor` is a generic function + # which doesn't contain type information of the function being wrapped. + keyconfig_data: KeyConfigData = args[1] # type: ignore keymap_data_clean(keyconfig_data, relaxed=relaxed) return keyconfig_data +def keyconfig_report_duplicates(keyconfig_data: KeyConfigData) -> str: + """ + Return true if any of the key-maps have duplicate items. + + Duplicate items are reported so they can be resolved. + """ + error_text = [] + for km_idname, km_args, km_items_data in keyconfig_data: + items = tuple(km_items_data["items"]) + unique: Dict[str, List[int]] = {} + for i, (item_op, item_event, item_prop) in enumerate(items): + # Ensure stable order as `repr` will use order of definition. + item_event = {key: item_event[key] for key in sorted(item_event.keys())} + if item_prop is not None: + item_prop = {key: item_prop[key] for key in sorted(item_prop.keys())} + item_repr = repr((item_op, item_event, item_prop)) + unique.setdefault(item_repr, []).append(i) + for key, value in unique.items(): + if len(value) > 1: + error_text.append("\"%s\" %r indices %r for item %r" % (km_idname, km_args, value, key)) + return "\n".join(error_text) + + def main() -> None: import os import sys @@ -185,38 +277,64 @@ def main() -> None: presets = keyconfig_preset_scan() for filepath in presets: name_only = os.path.splitext(os.path.basename(filepath))[0] - - print("KeyMap Validate:", name_only, end=" ... ") - - data_orig = keyconfig_activate_and_extract_data(filepath, relaxed=relaxed) - - with tempfile.TemporaryDirectory() as dir_temp: - filepath_temp = os.path.join(dir_temp, name_only + ".test.py") - bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True) - data_reimport = keyconfig_activate_and_extract_data(filepath_temp, relaxed=relaxed) - - # Comparing a pretty printed string tends to give more useful - # text output compared to the data-structure. Both will work. - if (cmp_message := report_humanly_readable_difference( - pprint.pformat(data_orig, indent=0, width=120), - pprint.pformat(data_reimport, indent=0, width=120), - )): - print("FAILED!") - sys.stdout.write(( - "Keymap %s has inconsistency on re-importing:\n" - " %r" - ) % (filepath, cmp_message)) - has_error = True - else: - print("OK!") - - if WRITE_OUTPUT_DIR: - name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only) - print("Writing data to:", name_only_temp + ".*.py") - with open(name_only_temp + ".orig.py", 'w') as fh: - fh.write(pprint.pformat(data_orig, indent=0, width=120)) - with open(name_only_temp + ".rewrite.py", 'w') as fh: - fh.write(pprint.pformat(data_reimport, indent=0, width=120)) + for config in PRESET_PREFS.get(name_only, ((),)): + name_only_with_config = name_only + keyconfig_config_as_filename_component(config) + print("KeyMap Validate:", name_only_with_config, end=" ... ") + data_orig = keyconfig_activate_and_extract_data( + filepath, + relaxed=relaxed, + config=config, + ) + + with tempfile.TemporaryDirectory() as dir_temp: + filepath_temp = os.path.join( + dir_temp, + name_only_with_config + ".test" + ".py", + ) + + bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True) + data_reimport = keyconfig_activate_and_extract_data( + filepath_temp, + relaxed=relaxed, + # No configuration supported when loading exported key-maps. + config=(), + ) + + # Comparing a pretty printed string tends to give more useful + # text output compared to the data-structure. Both will work. + if (cmp_message := report_humanly_readable_difference( + pprint.pformat(data_orig, indent=0, width=120), + pprint.pformat(data_reimport, indent=0, width=120), + )): + error_text_consistency = "Keymap %s has inconsistency on re-importing." % cmp_message + else: + error_text_consistency = "" + + # Perform an additional sanity check: + # That there are no identical key-map items. + if name_only not in ALLOW_DUPLICATES: + error_text_duplicates = keyconfig_report_duplicates(data_orig) + else: + error_text_duplicates = "" + + if error_text_consistency or error_text_duplicates: + print("FAILED!") + print("%r has errors!" % filepath) + if error_text_consistency: + print(error_text_consistency) + if error_text_duplicates: + print(error_text_duplicates) + else: + print("OK!") + + if WRITE_OUTPUT_DIR: + os.makedirs(WRITE_OUTPUT_DIR, exist_ok=True) + name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only_with_config) + print("Writing data to:", name_only_temp + ".*.py") + with open(name_only_temp + ".orig.py", 'w') as fh: + fh.write(pprint.pformat(data_orig, indent=0, width=120)) + with open(name_only_temp + ".rewrite.py", 'w') as fh: + fh.write(pprint.pformat(data_reimport, indent=0, width=120)) if has_error: sys.exit(1) |